Merge commit '7621e2f8dec938cf48181c8b10afc9b01f444e68' into beta

This commit is contained in:
Ilya Laktyushin
2025-12-06 02:17:48 +04:00
commit 8344b97e03
28070 changed files with 7995182 additions and 0 deletions
@@ -0,0 +1,75 @@
//
// EllipseItem.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
// MARK: - PathDirection
enum PathDirection: Int, Codable {
case clockwise = 1
case userSetClockwise = 2
case counterClockwise = 3
}
// MARK: - Ellipse
/// An item that define an ellipse shape
final class Ellipse: ShapeItem {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Ellipse.CodingKeys.self)
direction = try container.decodeIfPresent(PathDirection.self, forKey: .direction) ?? .clockwise
position = try container.decode(KeyframeGroup<Vector3D>.self, forKey: .position)
size = try container.decode(KeyframeGroup<Vector3D>.self, forKey: .size)
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
if
let directionRawType = dictionary[CodingKeys.direction.rawValue] as? Int,
let direction = PathDirection(rawValue: directionRawType)
{
self.direction = direction
} else {
direction = .clockwise
}
let positionDictionary: [String: Any] = try dictionary.value(for: CodingKeys.position)
position = try KeyframeGroup<Vector3D>(dictionary: positionDictionary)
let sizeDictionary: [String: Any] = try dictionary.value(for: CodingKeys.size)
size = try KeyframeGroup<Vector3D>(dictionary: sizeDictionary)
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// The direction of the ellipse.
let direction: PathDirection
/// The position of the ellipse
let position: KeyframeGroup<Vector3D>
/// The size of the ellipse
let size: KeyframeGroup<Vector3D>
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(direction, forKey: .direction)
try container.encode(position, forKey: .position)
try container.encode(size, forKey: .size)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case direction = "d"
case position = "p"
case size = "s"
}
}
@@ -0,0 +1,74 @@
//
// FillShape.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
// MARK: - FillRule
enum FillRule: Int, Codable {
case none
case nonZeroWinding
case evenOdd
}
// MARK: - Fill
/// An item that defines a fill render
final class Fill: ShapeItem {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Fill.CodingKeys.self)
opacity = try container.decode(KeyframeGroup<Vector1D>.self, forKey: .opacity)
color = try container.decode(KeyframeGroup<Color>.self, forKey: .color)
fillRule = try container.decodeIfPresent(FillRule.self, forKey: .fillRule) ?? .nonZeroWinding
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
let opacityDictionary: [String: Any] = try dictionary.value(for: CodingKeys.opacity)
opacity = try KeyframeGroup<Vector1D>(dictionary: opacityDictionary)
let colorDictionary: [String: Any] = try dictionary.value(for: CodingKeys.color)
color = try KeyframeGroup<Color>(dictionary: colorDictionary)
if
let fillRuleRawValue = dictionary[CodingKeys.fillRule.rawValue] as? Int,
let fillRule = FillRule(rawValue: fillRuleRawValue)
{
self.fillRule = fillRule
} else {
fillRule = .nonZeroWinding
}
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// The opacity of the fill
let opacity: KeyframeGroup<Vector1D>
/// The color keyframes for the fill
let color: KeyframeGroup<Color>
let fillRule: FillRule
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(opacity, forKey: .opacity)
try container.encode(color, forKey: .color)
try container.encode(fillRule, forKey: .fillRule)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case opacity = "o"
case color = "c"
case fillRule = "r"
}
}
@@ -0,0 +1,124 @@
//
// GradientFill.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
// MARK: - GradientType
enum GradientType: Int, Codable {
case none
case linear
case radial
}
// MARK: - GradientFill
/// An item that define a gradient fill
final class GradientFill: ShapeItem {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: GradientFill.CodingKeys.self)
opacity = try container.decode(KeyframeGroup<Vector1D>.self, forKey: .opacity)
startPoint = try container.decode(KeyframeGroup<Vector3D>.self, forKey: .startPoint)
endPoint = try container.decode(KeyframeGroup<Vector3D>.self, forKey: .endPoint)
gradientType = try container.decode(GradientType.self, forKey: .gradientType)
highlightLength = try container.decodeIfPresent(KeyframeGroup<Vector1D>.self, forKey: .highlightLength)
highlightAngle = try container.decodeIfPresent(KeyframeGroup<Vector1D>.self, forKey: .highlightAngle)
let colorsContainer = try container.nestedContainer(keyedBy: GradientDataKeys.self, forKey: .colors)
colors = try colorsContainer.decode(KeyframeGroup<[Double]>.self, forKey: .colors)
numberOfColors = try colorsContainer.decode(Int.self, forKey: .numberOfColors)
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
let opacityDictionary: [String: Any] = try dictionary.value(for: CodingKeys.opacity)
opacity = try KeyframeGroup<Vector1D>(dictionary: opacityDictionary)
let startPointDictionary: [String: Any] = try dictionary.value(for: CodingKeys.startPoint)
startPoint = try KeyframeGroup<Vector3D>(dictionary: startPointDictionary)
let endPointDictionary: [String: Any] = try dictionary.value(for: CodingKeys.endPoint)
endPoint = try KeyframeGroup<Vector3D>(dictionary: endPointDictionary)
let gradientRawType: Int = try dictionary.value(for: CodingKeys.gradientType)
guard let gradient = GradientType(rawValue: gradientRawType) else {
throw InitializableError.invalidInput
}
gradientType = gradient
if let highlightLengthDictionary = dictionary[CodingKeys.highlightLength.rawValue] as? [String: Any] {
highlightLength = try? KeyframeGroup<Vector1D>(dictionary: highlightLengthDictionary)
} else {
highlightLength = nil
}
if let highlightAngleDictionary = dictionary[CodingKeys.highlightAngle.rawValue] as? [String: Any] {
highlightAngle = try? KeyframeGroup<Vector1D>(dictionary: highlightAngleDictionary)
} else {
highlightAngle = nil
}
let colorsDictionary: [String: Any] = try dictionary.value(for: CodingKeys.colors)
let nestedColorsDictionary: [String: Any] = try colorsDictionary.value(for: GradientDataKeys.colors)
colors = try KeyframeGroup<[Double]>(dictionary: nestedColorsDictionary)
numberOfColors = try colorsDictionary.value(for: GradientDataKeys.numberOfColors)
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// The opacity of the fill
let opacity: KeyframeGroup<Vector1D>
/// The start of the gradient
let startPoint: KeyframeGroup<Vector3D>
/// The end of the gradient
let endPoint: KeyframeGroup<Vector3D>
/// The type of gradient
let gradientType: GradientType
/// Gradient Highlight Length. Only if type is Radial
let highlightLength: KeyframeGroup<Vector1D>?
/// Highlight Angle. Only if type is Radial
let highlightAngle: KeyframeGroup<Vector1D>?
/// The number of color points in the gradient
let numberOfColors: Int
/// The Colors of the gradient.
let colors: KeyframeGroup<[Double]>
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(opacity, forKey: .opacity)
try container.encode(startPoint, forKey: .startPoint)
try container.encode(endPoint, forKey: .endPoint)
try container.encode(gradientType, forKey: .gradientType)
try container.encodeIfPresent(highlightLength, forKey: .highlightLength)
try container.encodeIfPresent(highlightAngle, forKey: .highlightAngle)
var colorsContainer = container.nestedContainer(keyedBy: GradientDataKeys.self, forKey: .colors)
try colorsContainer.encode(numberOfColors, forKey: .numberOfColors)
try colorsContainer.encode(colors, forKey: .colors)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case opacity = "o"
case startPoint = "s"
case endPoint = "e"
case gradientType = "t"
case highlightLength = "h"
case highlightAngle = "a"
case colors = "g"
}
private enum GradientDataKeys: String, CodingKey {
case numberOfColors = "p"
case colors = "k"
}
}
@@ -0,0 +1,186 @@
//
// GradientStroke.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
// MARK: - LineCap
enum LineCap: Int, Codable {
case none
case butt
case round
case square
}
// MARK: - LineJoin
enum LineJoin: Int, Codable {
case none
case miter
case round
case bevel
}
// MARK: - GradientStroke
/// An item that define an ellipse shape
final class GradientStroke: ShapeItem {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: GradientStroke.CodingKeys.self)
opacity = try container.decode(KeyframeGroup<Vector1D>.self, forKey: .opacity)
startPoint = try container.decode(KeyframeGroup<Vector3D>.self, forKey: .startPoint)
endPoint = try container.decode(KeyframeGroup<Vector3D>.self, forKey: .endPoint)
gradientType = try container.decode(GradientType.self, forKey: .gradientType)
highlightLength = try container.decodeIfPresent(KeyframeGroup<Vector1D>.self, forKey: .highlightLength)
highlightAngle = try container.decodeIfPresent(KeyframeGroup<Vector1D>.self, forKey: .highlightAngle)
width = try container.decode(KeyframeGroup<Vector1D>.self, forKey: .width)
lineCap = try container.decodeIfPresent(LineCap.self, forKey: .lineCap) ?? .round
lineJoin = try container.decodeIfPresent(LineJoin.self, forKey: .lineJoin) ?? .round
miterLimit = try container.decodeIfPresent(Double.self, forKey: .miterLimit) ?? 4
// TODO Decode Color Objects instead of array.
let colorsContainer = try container.nestedContainer(keyedBy: GradientDataKeys.self, forKey: .colors)
colors = try colorsContainer.decode(KeyframeGroup<[Double]>.self, forKey: .colors)
numberOfColors = try colorsContainer.decode(Int.self, forKey: .numberOfColors)
dashPattern = try container.decodeIfPresent([DashElement].self, forKey: .dashPattern)
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
let opacityDictionary: [String: Any] = try dictionary.value(for: CodingKeys.opacity)
opacity = try KeyframeGroup<Vector1D>(dictionary: opacityDictionary)
let startPointDictionary: [String: Any] = try dictionary.value(for: CodingKeys.startPoint)
startPoint = try KeyframeGroup<Vector3D>(dictionary: startPointDictionary)
let endPointDictionary: [String: Any] = try dictionary.value(for: CodingKeys.endPoint)
endPoint = try KeyframeGroup<Vector3D>(dictionary: endPointDictionary)
let gradientRawType: Int = try dictionary.value(for: CodingKeys.gradientType)
guard let gradient = GradientType(rawValue: gradientRawType) else {
throw InitializableError.invalidInput
}
gradientType = gradient
if let highlightLengthDictionary = dictionary[CodingKeys.highlightLength.rawValue] as? [String: Any] {
highlightLength = try? KeyframeGroup<Vector1D>(dictionary: highlightLengthDictionary)
} else {
highlightLength = nil
}
if let highlightAngleDictionary = dictionary[CodingKeys.highlightAngle.rawValue] as? [String: Any] {
highlightAngle = try? KeyframeGroup<Vector1D>(dictionary: highlightAngleDictionary)
} else {
highlightAngle = nil
}
let widthDictionary: [String: Any] = try dictionary.value(for: CodingKeys.width)
width = try KeyframeGroup<Vector1D>(dictionary: widthDictionary)
if
let lineCapRawValue = dictionary[CodingKeys.lineCap.rawValue] as? Int,
let lineCap = LineCap(rawValue: lineCapRawValue)
{
self.lineCap = lineCap
} else {
lineCap = .round
}
if
let lineJoinRawValue = dictionary[CodingKeys.lineJoin.rawValue] as? Int,
let lineJoin = LineJoin(rawValue: lineJoinRawValue)
{
self.lineJoin = lineJoin
} else {
lineJoin = .round
}
miterLimit = (try? dictionary.value(for: CodingKeys.miterLimit)) ?? 4
let colorsDictionary: [String: Any] = try dictionary.value(for: CodingKeys.colors)
let nestedColorsDictionary: [String: Any] = try colorsDictionary.value(for: GradientDataKeys.colors)
colors = try KeyframeGroup<[Double]>(dictionary: nestedColorsDictionary)
numberOfColors = try colorsDictionary.value(for: GradientDataKeys.numberOfColors)
let dashPatternDictionaries = dictionary[CodingKeys.dashPattern.rawValue] as? [[String: Any]]
dashPattern = try? dashPatternDictionaries?.map({ try DashElement(dictionary: $0) })
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// The opacity of the fill
let opacity: KeyframeGroup<Vector1D>
/// The start of the gradient
let startPoint: KeyframeGroup<Vector3D>
/// The end of the gradient
let endPoint: KeyframeGroup<Vector3D>
/// The type of gradient
let gradientType: GradientType
/// Gradient Highlight Length. Only if type is Radial
let highlightLength: KeyframeGroup<Vector1D>?
/// Highlight Angle. Only if type is Radial
let highlightAngle: KeyframeGroup<Vector1D>?
/// The number of color points in the gradient
let numberOfColors: Int
/// The Colors of the gradient.
let colors: KeyframeGroup<[Double]>
/// The width of the stroke
let width: KeyframeGroup<Vector1D>
/// Line Cap
let lineCap: LineCap
/// Line Join
let lineJoin: LineJoin
/// Miter Limit
let miterLimit: Double
/// The dash pattern of the stroke
let dashPattern: [DashElement]?
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(opacity, forKey: .opacity)
try container.encode(startPoint, forKey: .startPoint)
try container.encode(endPoint, forKey: .endPoint)
try container.encode(gradientType, forKey: .gradientType)
try container.encodeIfPresent(highlightLength, forKey: .highlightLength)
try container.encodeIfPresent(highlightAngle, forKey: .highlightAngle)
try container.encode(width, forKey: .width)
try container.encode(lineCap, forKey: .lineCap)
try container.encode(lineJoin, forKey: .lineJoin)
try container.encode(miterLimit, forKey: .miterLimit)
var colorsContainer = container.nestedContainer(keyedBy: GradientDataKeys.self, forKey: .colors)
try colorsContainer.encode(numberOfColors, forKey: .numberOfColors)
try colorsContainer.encode(colors, forKey: .colors)
try container.encodeIfPresent(dashPattern, forKey: .dashPattern)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case opacity = "o"
case startPoint = "s"
case endPoint = "e"
case gradientType = "t"
case highlightLength = "h"
case highlightAngle = "a"
case colors = "g"
case width = "w"
case lineCap = "lc"
case lineJoin = "lj"
case miterLimit = "ml"
case dashPattern = "d"
}
private enum GradientDataKeys: String, CodingKey {
case numberOfColors = "p"
case colors = "k"
}
}
@@ -0,0 +1,43 @@
//
// GroupItem.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
/// An item that define an ellipse shape
final class Group: ShapeItem {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Group.CodingKeys.self)
items = try container.decode([ShapeItem].self, ofFamily: ShapeType.self, forKey: .items)
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
let itemDictionaries: [[String: Any]] = try dictionary.value(for: CodingKeys.items)
items = try [ShapeItem].fromDictionaries(itemDictionaries)
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// A list of shape items.
let items: [ShapeItem]
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(items, forKey: .items)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case items = "it"
}
}
@@ -0,0 +1,59 @@
//
// Merge.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
// MARK: - MergeMode
enum MergeMode: Int, Codable {
case none
case merge
case add
case subtract
case intersect
case exclude
}
// MARK: - Merge
/// An item that define an ellipse shape
final class Merge: ShapeItem {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Merge.CodingKeys.self)
mode = try container.decode(MergeMode.self, forKey: .mode)
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
let modeRawType: Int = try dictionary.value(for: CodingKeys.mode)
guard let mode = MergeMode(rawValue: modeRawType) else {
throw InitializableError.invalidInput
}
self.mode = mode
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// The mode of the merge path
let mode: MergeMode
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(mode, forKey: .mode)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case mode = "mm"
}
}
@@ -0,0 +1,73 @@
//
// Rectangle.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
/// An item that define an ellipse shape
final class Rectangle: ShapeItem {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Rectangle.CodingKeys.self)
direction = try container.decodeIfPresent(PathDirection.self, forKey: .direction) ?? .clockwise
position = try container.decode(KeyframeGroup<Vector3D>.self, forKey: .position)
size = try container.decode(KeyframeGroup<Vector3D>.self, forKey: .size)
cornerRadius = try container.decode(KeyframeGroup<Vector1D>.self, forKey: .cornerRadius)
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
if
let directionRawType = dictionary[CodingKeys.direction.rawValue] as? Int,
let direction = PathDirection(rawValue: directionRawType)
{
self.direction = direction
} else {
direction = .clockwise
}
let positionDictionary: [String: Any] = try dictionary.value(for: CodingKeys.position)
position = try KeyframeGroup<Vector3D>(dictionary: positionDictionary)
let sizeDictionary: [String: Any] = try dictionary.value(for: CodingKeys.size)
size = try KeyframeGroup<Vector3D>(dictionary: sizeDictionary)
let cornerRadiusDictionary: [String: Any] = try dictionary.value(for: CodingKeys.cornerRadius)
cornerRadius = try KeyframeGroup<Vector1D>(dictionary: cornerRadiusDictionary)
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// The direction of the rect.
let direction: PathDirection
/// The position
let position: KeyframeGroup<Vector3D>
/// The size
let size: KeyframeGroup<Vector3D>
/// The Corner radius of the rectangle
let cornerRadius: KeyframeGroup<Vector1D>
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(direction, forKey: .direction)
try container.encode(position, forKey: .position)
try container.encode(size, forKey: .size)
try container.encode(cornerRadius, forKey: .cornerRadius)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case direction = "d"
case position = "p"
case size = "s"
case cornerRadius = "r"
}
}
@@ -0,0 +1,136 @@
//
// Repeater.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
/// An item that define an ellipse shape
final class Repeater: ShapeItem {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Repeater.CodingKeys.self)
copies = try container.decodeIfPresent(KeyframeGroup<Vector1D>.self, forKey: .copies) ?? KeyframeGroup(Vector1D(0))
offset = try container.decodeIfPresent(KeyframeGroup<Vector1D>.self, forKey: .offset) ?? KeyframeGroup(Vector1D(0))
let transformContainer = try container.nestedContainer(keyedBy: TransformKeys.self, forKey: .transform)
startOpacity = try transformContainer
.decodeIfPresent(KeyframeGroup<Vector1D>.self, forKey: .startOpacity) ?? KeyframeGroup(Vector1D(100))
endOpacity = try transformContainer
.decodeIfPresent(KeyframeGroup<Vector1D>.self, forKey: .endOpacity) ?? KeyframeGroup(Vector1D(100))
rotation = try transformContainer
.decodeIfPresent(KeyframeGroup<Vector1D>.self, forKey: .rotation) ?? KeyframeGroup(Vector1D(0))
position = try transformContainer
.decodeIfPresent(KeyframeGroup<Vector3D>.self, forKey: .position) ?? KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0))
anchorPoint = try transformContainer
.decodeIfPresent(KeyframeGroup<Vector3D>.self, forKey: .anchorPoint) ?? KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0))
scale = try transformContainer
.decodeIfPresent(KeyframeGroup<Vector3D>.self, forKey: .scale) ?? KeyframeGroup(Vector3D(x: Double(100), y: 100, z: 100))
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
if let copiesDictionary = dictionary[CodingKeys.copies.rawValue] as? [String: Any] {
copies = try KeyframeGroup<Vector1D>(dictionary: copiesDictionary)
} else {
copies = KeyframeGroup(Vector1D(0))
}
if let offsetDictionary = dictionary[CodingKeys.offset.rawValue] as? [String: Any] {
offset = try KeyframeGroup<Vector1D>(dictionary: offsetDictionary)
} else {
offset = KeyframeGroup(Vector1D(0))
}
let transformDictionary: [String: Any] = try dictionary.value(for: CodingKeys.transform)
if let startOpacityDictionary = transformDictionary[TransformKeys.startOpacity.rawValue] as? [String: Any] {
startOpacity = try KeyframeGroup<Vector1D>(dictionary: startOpacityDictionary)
} else {
startOpacity = KeyframeGroup(Vector1D(100))
}
if let endOpacityDictionary = transformDictionary[TransformKeys.endOpacity.rawValue] as? [String: Any] {
endOpacity = try KeyframeGroup<Vector1D>(dictionary: endOpacityDictionary)
} else {
endOpacity = KeyframeGroup(Vector1D(100))
}
if let rotationDictionary = transformDictionary[TransformKeys.rotation.rawValue] as? [String: Any] {
rotation = try KeyframeGroup<Vector1D>(dictionary: rotationDictionary)
} else {
rotation = KeyframeGroup(Vector1D(0))
}
if let positionDictionary = transformDictionary[TransformKeys.position.rawValue] as? [String: Any] {
position = try KeyframeGroup<Vector3D>(dictionary: positionDictionary)
} else {
position = KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0))
}
if let anchorPointDictionary = transformDictionary[TransformKeys.anchorPoint.rawValue] as? [String: Any] {
anchorPoint = try KeyframeGroup<Vector3D>(dictionary: anchorPointDictionary)
} else {
anchorPoint = KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0))
}
if let scaleDictionary = transformDictionary[TransformKeys.scale.rawValue] as? [String: Any] {
scale = try KeyframeGroup<Vector3D>(dictionary: scaleDictionary)
} else {
scale = KeyframeGroup(Vector3D(x: Double(100), y: 100, z: 100))
}
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// The number of copies to repeat
let copies: KeyframeGroup<Vector1D>
/// The offset of each copy
let offset: KeyframeGroup<Vector1D>
/// Start Opacity
let startOpacity: KeyframeGroup<Vector1D>
/// End opacity
let endOpacity: KeyframeGroup<Vector1D>
/// The rotation
let rotation: KeyframeGroup<Vector1D>
/// Anchor Point
let anchorPoint: KeyframeGroup<Vector3D>
/// Position
let position: KeyframeGroup<Vector3D>
/// Scale
let scale: KeyframeGroup<Vector3D>
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(copies, forKey: .copies)
try container.encode(offset, forKey: .offset)
var transformContainer = container.nestedContainer(keyedBy: TransformKeys.self, forKey: .transform)
try transformContainer.encode(startOpacity, forKey: .startOpacity)
try transformContainer.encode(endOpacity, forKey: .endOpacity)
try transformContainer.encode(rotation, forKey: .rotation)
try transformContainer.encode(position, forKey: .position)
try transformContainer.encode(anchorPoint, forKey: .anchorPoint)
try transformContainer.encode(scale, forKey: .scale)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case copies = "c"
case offset = "o"
case transform = "tr"
}
private enum TransformKeys: String, CodingKey {
case rotation = "r"
case startOpacity = "so"
case endOpacity = "eo"
case anchorPoint = "a"
case position = "p"
case scale = "s"
}
}
@@ -0,0 +1,56 @@
//
// VectorShape.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
/// An item that defines an custom shape
final class Shape: ShapeItem {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Shape.CodingKeys.self)
path = try container.decode(KeyframeGroup<BezierPath>.self, forKey: .path)
direction = try container.decodeIfPresent(PathDirection.self, forKey: .direction)
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
let pathDictionary: [String: Any] = try dictionary.value(for: CodingKeys.path)
path = try KeyframeGroup<BezierPath>(dictionary: pathDictionary)
if
let directionRawValue = dictionary[CodingKeys.direction.rawValue] as? Int,
let direction = PathDirection(rawValue: directionRawValue)
{
self.direction = direction
} else {
direction = nil
}
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// The Path
let path: KeyframeGroup<BezierPath>
let direction: PathDirection?
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(path, forKey: .path)
try container.encodeIfPresent(direction, forKey: .direction)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case path = "ks"
case direction = "d"
}
}
@@ -0,0 +1,163 @@
//
// ShapeItem.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
// MARK: - ShapeType + ClassFamily
/// Used for mapping a heterogeneous list to classes for parsing.
extension ShapeType: ClassFamily {
static var discriminator: Discriminator = .type
func getType() -> AnyObject.Type {
switch self {
case .ellipse:
return Ellipse.self
case .fill:
return Fill.self
case .gradientFill:
return GradientFill.self
case .group:
return Group.self
case .gradientStroke:
return GradientStroke.self
case .merge:
return Merge.self
case .rectangle:
return Rectangle.self
case .repeater:
return Repeater.self
case .shape:
return Shape.self
case .star:
return Star.self
case .stroke:
return Stroke.self
case .trim:
return Trim.self
case .transform:
return ShapeTransform.self
default:
return ShapeItem.self
}
}
}
// MARK: - ShapeType
enum ShapeType: String, Codable {
case ellipse = "el"
case fill = "fl"
case gradientFill = "gf"
case group = "gr"
case gradientStroke = "gs"
case merge = "mm"
case rectangle = "rc"
case repeater = "rp"
case round = "rd"
case shape = "sh"
case star = "sr"
case stroke = "st"
case trim = "tm"
case transform = "tr"
case unknown
public init(from decoder: Decoder) throws {
self = try ShapeType(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .unknown
}
}
// MARK: - ShapeItem
/// An item belonging to a Shape Layer
class ShapeItem: Codable, DictionaryInitializable {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: ShapeItem.CodingKeys.self)
name = try container.decodeIfPresent(String.self, forKey: .name) ?? "Layer"
type = try container.decode(ShapeType.self, forKey: .type)
hidden = try container.decodeIfPresent(Bool.self, forKey: .hidden) ?? false
}
required init(dictionary: [String: Any]) throws {
name = (try? dictionary.value(for: CodingKeys.name)) ?? "Layer"
type = ShapeType(rawValue: try dictionary.value(for: CodingKeys.type)) ?? .unknown
hidden = (try? dictionary.value(for: CodingKeys.hidden)) ?? false
}
init(
name: String,
type: ShapeType,
hidden: Bool)
{
self.name = name
self.type = type
self.hidden = hidden
}
// MARK: Internal
/// The name of the shape
let name: String
/// The type of shape
let type: ShapeType
let hidden: Bool
// MARK: Fileprivate
fileprivate enum CodingKeys: String, CodingKey {
case name = "nm"
case type = "ty"
case hidden = "hd"
}
}
extension Array where Element == ShapeItem {
static func fromDictionaries(_ dictionaries: [[String: Any]]) throws -> [ShapeItem] {
try dictionaries.compactMap { dictionary in
let shapeType = dictionary[ShapeItem.CodingKeys.type.rawValue] as? String
switch ShapeType(rawValue: shapeType ?? ShapeType.unknown.rawValue) {
case .ellipse:
return try Ellipse(dictionary: dictionary)
case .fill:
return try Fill(dictionary: dictionary)
case .gradientFill:
return try GradientFill(dictionary: dictionary)
case .group:
return try Group(dictionary: dictionary)
case .gradientStroke:
return try GradientStroke(dictionary: dictionary)
case .merge:
return try Merge(dictionary: dictionary)
case .rectangle:
return try Rectangle(dictionary: dictionary)
case .repeater:
return try Repeater(dictionary: dictionary)
case .shape:
return try Shape(dictionary: dictionary)
case .star:
return try Star(dictionary: dictionary)
case .stroke:
return try Stroke(dictionary: dictionary)
case .trim:
return try Trim(dictionary: dictionary)
case .transform:
return try ShapeTransform(dictionary: dictionary)
case .none:
return nil
default:
return try ShapeItem(dictionary: dictionary)
}
}
}
}
@@ -0,0 +1,136 @@
//
// TransformItem.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
/// An item that define an ellipse shape
final class ShapeTransform: ShapeItem {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: ShapeTransform.CodingKeys.self)
anchor = try container
.decodeIfPresent(KeyframeGroup<Vector3D>.self, forKey: .anchor) ?? KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0))
position = try container
.decodeIfPresent(KeyframeGroup<Vector3D>.self, forKey: .position) ?? KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0))
scale = try container
.decodeIfPresent(KeyframeGroup<Vector3D>.self, forKey: .scale) ?? KeyframeGroup(Vector3D(x: Double(100), y: 100, z: 100))
rotation = try container.decodeIfPresent(KeyframeGroup<Vector1D>.self, forKey: .rotation) ?? KeyframeGroup(Vector1D(0))
opacity = try container.decodeIfPresent(KeyframeGroup<Vector1D>.self, forKey: .opacity) ?? KeyframeGroup(Vector1D(100))
skew = try container.decodeIfPresent(KeyframeGroup<Vector1D>.self, forKey: .skew) ?? KeyframeGroup(Vector1D(0))
skewAxis = try container.decodeIfPresent(KeyframeGroup<Vector1D>.self, forKey: .skewAxis) ?? KeyframeGroup(Vector1D(0))
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
if
let anchorDictionary = dictionary[CodingKeys.anchor.rawValue] as? [String: Any],
let anchor = try? KeyframeGroup<Vector3D>(dictionary: anchorDictionary)
{
self.anchor = anchor
} else {
anchor = KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0))
}
if
let positionDictionary = dictionary[CodingKeys.position.rawValue] as? [String: Any],
let position = try? KeyframeGroup<Vector3D>(dictionary: positionDictionary)
{
self.position = position
} else {
position = KeyframeGroup(Vector3D(x: Double(0), y: 0, z: 0))
}
if
let scaleDictionary = dictionary[CodingKeys.scale.rawValue] as? [String: Any],
let scale = try? KeyframeGroup<Vector3D>(dictionary: scaleDictionary)
{
self.scale = scale
} else {
scale = KeyframeGroup(Vector3D(x: Double(100), y: 100, z: 100))
}
if
let rotationDictionary = dictionary[CodingKeys.rotation.rawValue] as? [String: Any],
let rotation = try? KeyframeGroup<Vector1D>(dictionary: rotationDictionary)
{
self.rotation = rotation
} else {
rotation = KeyframeGroup(Vector1D(0))
}
if
let opacityDictionary = dictionary[CodingKeys.opacity.rawValue] as? [String: Any],
let opacity = try? KeyframeGroup<Vector1D>(dictionary: opacityDictionary)
{
self.opacity = opacity
} else {
opacity = KeyframeGroup(Vector1D(100))
}
if
let skewDictionary = dictionary[CodingKeys.skew.rawValue] as? [String: Any],
let skew = try? KeyframeGroup<Vector1D>(dictionary: skewDictionary)
{
self.skew = skew
} else {
skew = KeyframeGroup(Vector1D(0))
}
if
let skewAxisDictionary = dictionary[CodingKeys.skewAxis.rawValue] as? [String: Any],
let skewAxis = try? KeyframeGroup<Vector1D>(dictionary: skewAxisDictionary)
{
self.skewAxis = skewAxis
} else {
skewAxis = KeyframeGroup(Vector1D(0))
}
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// Anchor Point
let anchor: KeyframeGroup<Vector3D>
/// Position
let position: KeyframeGroup<Vector3D>
/// Scale
let scale: KeyframeGroup<Vector3D>
/// Rotation
let rotation: KeyframeGroup<Vector1D>
/// opacity
let opacity: KeyframeGroup<Vector1D>
/// Skew
let skew: KeyframeGroup<Vector1D>
/// Skew Axis
let skewAxis: KeyframeGroup<Vector1D>
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(anchor, forKey: .anchor)
try container.encode(position, forKey: .position)
try container.encode(scale, forKey: .scale)
try container.encode(rotation, forKey: .rotation)
try container.encode(opacity, forKey: .opacity)
try container.encode(skew, forKey: .skew)
try container.encode(skewAxis, forKey: .skewAxis)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case anchor = "a"
case position = "p"
case scale = "s"
case rotation = "r"
case opacity = "o"
case skew = "sk"
case skewAxis = "sa"
}
}
@@ -0,0 +1,132 @@
//
// Star.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
// MARK: - StarType
enum StarType: Int, Codable {
case none
case star
case polygon
}
// MARK: - Star
/// An item that define an ellipse shape
final class Star: ShapeItem {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Star.CodingKeys.self)
direction = try container.decodeIfPresent(PathDirection.self, forKey: .direction) ?? .clockwise
position = try container.decode(KeyframeGroup<Vector3D>.self, forKey: .position)
outerRadius = try container.decode(KeyframeGroup<Vector1D>.self, forKey: .outerRadius)
outerRoundness = try container.decode(KeyframeGroup<Vector1D>.self, forKey: .outerRoundness)
innerRadius = try container.decodeIfPresent(KeyframeGroup<Vector1D>.self, forKey: .innerRadius)
innerRoundness = try container.decodeIfPresent(KeyframeGroup<Vector1D>.self, forKey: .innerRoundness)
rotation = try container.decode(KeyframeGroup<Vector1D>.self, forKey: .rotation)
points = try container.decode(KeyframeGroup<Vector1D>.self, forKey: .points)
starType = try container.decode(StarType.self, forKey: .starType)
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
if
let directionRawValue = dictionary[CodingKeys.direction.rawValue] as? Int,
let direction = PathDirection(rawValue: directionRawValue)
{
self.direction = direction
} else {
direction = .clockwise
}
let positionDictionary: [String: Any] = try dictionary.value(for: CodingKeys.position)
position = try KeyframeGroup<Vector3D>(dictionary: positionDictionary)
let outerRadiusDictionary: [String: Any] = try dictionary.value(for: CodingKeys.outerRadius)
outerRadius = try KeyframeGroup<Vector1D>(dictionary: outerRadiusDictionary)
let outerRoundnessDictionary: [String: Any] = try dictionary.value(for: CodingKeys.outerRoundness)
outerRoundness = try KeyframeGroup<Vector1D>(dictionary: outerRoundnessDictionary)
if let innerRadiusDictionary = dictionary[CodingKeys.innerRadius.rawValue] as? [String: Any] {
innerRadius = try KeyframeGroup<Vector1D>(dictionary: innerRadiusDictionary)
} else {
innerRadius = nil
}
if let innerRoundnessDictionary = dictionary[CodingKeys.innerRoundness.rawValue] as? [String: Any] {
innerRoundness = try KeyframeGroup<Vector1D>(dictionary: innerRoundnessDictionary)
} else {
innerRoundness = nil
}
let rotationDictionary: [String: Any] = try dictionary.value(for: CodingKeys.rotation)
rotation = try KeyframeGroup<Vector1D>(dictionary: rotationDictionary)
let pointsDictionary: [String: Any] = try dictionary.value(for: CodingKeys.points)
points = try KeyframeGroup<Vector1D>(dictionary: pointsDictionary)
let starTypeRawValue: Int = try dictionary.value(for: CodingKeys.starType)
guard let starType = StarType(rawValue: starTypeRawValue) else {
throw InitializableError.invalidInput
}
self.starType = starType
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// The direction of the star.
let direction: PathDirection
/// The position of the star
let position: KeyframeGroup<Vector3D>
/// The outer radius of the star
let outerRadius: KeyframeGroup<Vector1D>
/// The outer roundness of the star
let outerRoundness: KeyframeGroup<Vector1D>
/// The outer radius of the star
let innerRadius: KeyframeGroup<Vector1D>?
/// The outer roundness of the star
let innerRoundness: KeyframeGroup<Vector1D>?
/// The rotation of the star
let rotation: KeyframeGroup<Vector1D>
/// The number of points on the star
let points: KeyframeGroup<Vector1D>
/// The type of star
let starType: StarType
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(direction, forKey: .direction)
try container.encode(position, forKey: .position)
try container.encode(outerRadius, forKey: .outerRadius)
try container.encode(outerRoundness, forKey: .outerRoundness)
try container.encode(innerRadius, forKey: .innerRadius)
try container.encode(innerRoundness, forKey: .innerRoundness)
try container.encode(rotation, forKey: .rotation)
try container.encode(points, forKey: .points)
try container.encode(starType, forKey: .starType)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case direction = "d"
case position = "p"
case outerRadius = "or"
case outerRoundness = "os"
case innerRadius = "ir"
case innerRoundness = "is"
case rotation = "r"
case points = "pt"
case starType = "sy"
}
}
@@ -0,0 +1,102 @@
//
// Stroke.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
/// An item that define an ellipse shape
final class Stroke: ShapeItem {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Stroke.CodingKeys.self)
opacity = try container.decode(KeyframeGroup<Vector1D>.self, forKey: .opacity)
color = try container.decode(KeyframeGroup<Color>.self, forKey: .color)
width = try container.decode(KeyframeGroup<Vector1D>.self, forKey: .width)
lineCap = try container.decodeIfPresent(LineCap.self, forKey: .lineCap) ?? .round
lineJoin = try container.decodeIfPresent(LineJoin.self, forKey: .lineJoin) ?? .round
miterLimit = try container.decodeIfPresent(Double.self, forKey: .miterLimit) ?? 4
dashPattern = try container.decodeIfPresent([DashElement].self, forKey: .dashPattern)
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
let opacityDictionary: [String: Any] = try dictionary.value(for: CodingKeys.opacity)
opacity = try KeyframeGroup<Vector1D>(dictionary: opacityDictionary)
let colorDictionary: [String: Any] = try dictionary.value(for: CodingKeys.color)
color = try KeyframeGroup<Color>(dictionary: colorDictionary)
let widthDictionary: [String: Any] = try dictionary.value(for: CodingKeys.width)
width = try KeyframeGroup<Vector1D>(dictionary: widthDictionary)
if
let lineCapRawValue = dictionary[CodingKeys.lineCap.rawValue] as? Int,
let lineCap = LineCap(rawValue: lineCapRawValue)
{
self.lineCap = lineCap
} else {
lineCap = .round
}
if
let lineJoinRawValue = dictionary[CodingKeys.lineJoin.rawValue] as? Int,
let lineJoin = LineJoin(rawValue: lineJoinRawValue)
{
self.lineJoin = lineJoin
} else {
lineJoin = .round
}
miterLimit = (try? dictionary.value(for: CodingKeys.miterLimit)) ?? 4
let dashPatternDictionaries = dictionary[CodingKeys.dashPattern.rawValue] as? [[String: Any]]
dashPattern = try? dashPatternDictionaries?.map({ try DashElement(dictionary: $0) })
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// The opacity of the stroke
let opacity: KeyframeGroup<Vector1D>
/// The Color of the stroke
let color: KeyframeGroup<Color>
/// The width of the stroke
let width: KeyframeGroup<Vector1D>
/// Line Cap
let lineCap: LineCap
/// Line Join
let lineJoin: LineJoin
/// Miter Limit
let miterLimit: Double
/// The dash pattern of the stroke
let dashPattern: [DashElement]?
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(opacity, forKey: .opacity)
try container.encode(color, forKey: .color)
try container.encode(width, forKey: .width)
try container.encode(lineCap, forKey: .lineCap)
try container.encode(lineJoin, forKey: .lineJoin)
try container.encode(miterLimit, forKey: .miterLimit)
try container.encodeIfPresent(dashPattern, forKey: .dashPattern)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case opacity = "o"
case color = "c"
case width = "w"
case lineCap = "lc"
case lineJoin = "lj"
case miterLimit = "ml"
case dashPattern = "d"
}
}
@@ -0,0 +1,78 @@
//
// Trim.swift
// lottie-swift
//
// Created by Brandon Withrow on 1/8/19.
//
import Foundation
// MARK: - TrimType
enum TrimType: Int, Codable {
case simultaneously = 1
case individually = 2
}
// MARK: - Trim
/// An item that define an ellipse shape
final class Trim: ShapeItem {
// MARK: Lifecycle
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Trim.CodingKeys.self)
start = try container.decode(KeyframeGroup<Vector1D>.self, forKey: .start)
end = try container.decode(KeyframeGroup<Vector1D>.self, forKey: .end)
offset = try container.decode(KeyframeGroup<Vector1D>.self, forKey: .offset)
trimType = try container.decode(TrimType.self, forKey: .trimType)
try super.init(from: decoder)
}
required init(dictionary: [String: Any]) throws {
let startDictionary: [String: Any] = try dictionary.value(for: CodingKeys.start)
start = try KeyframeGroup<Vector1D>(dictionary: startDictionary)
let endDictionary: [String: Any] = try dictionary.value(for: CodingKeys.end)
end = try KeyframeGroup<Vector1D>(dictionary: endDictionary)
let offsetDictionary: [String: Any] = try dictionary.value(for: CodingKeys.offset)
offset = try KeyframeGroup<Vector1D>(dictionary: offsetDictionary)
let trimTypeRawValue: Int = try dictionary.value(for: CodingKeys.trimType)
guard let trimType = TrimType(rawValue: trimTypeRawValue) else {
throw InitializableError.invalidInput
}
self.trimType = trimType
try super.init(dictionary: dictionary)
}
// MARK: Internal
/// The start of the trim
let start: KeyframeGroup<Vector1D>
/// The end of the trim
let end: KeyframeGroup<Vector1D>
/// The offset of the trim
let offset: KeyframeGroup<Vector1D>
let trimType: TrimType
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(start, forKey: .start)
try container.encode(end, forKey: .end)
try container.encode(offset, forKey: .offset)
try container.encode(trimType, forKey: .trimType)
}
// MARK: Private
private enum CodingKeys: String, CodingKey {
case start = "s"
case end = "e"
case offset = "o"
case trimType = "m"
}
}