#ifndef Transform_hpp #define Transform_hpp #include "Lottie/Private/Model/Keyframes/KeyframeGroup.hpp" #include "Lottie/Private/Parsing/JsonParsing.hpp" #include namespace lottie { class Transform { public: enum class PositionInternalRepresentation { None, TopLevelXY, TopLevelCombined, NestedXY }; enum class RotationZInternalRepresentation { RZ, R }; public: Transform( std::optional> anchorPoint_, std::optional> position_, std::optional> positionX_, std::optional> positionY_, std::optional> scale_, std::optional> rotation_, std::optional> &opacity_, std::optional> rotationZ_ ) : _anchorPoint(anchorPoint_), _position(position_), _positionX(positionX_), _positionY(positionY_), _scale(scale_), _rotation(rotation_), _opacity(opacity_), _rotationZ(rotationZ_) { } explicit Transform(json11::Json::object const &json) noexcept(false) { // AnchorPoint if (const auto anchorPointDictionary = getOptionalObject(json, "a")) { _anchorPoint = KeyframeGroup(anchorPointDictionary.value()); } try { auto xDictionary = getOptionalObject(json, "px"); auto yDictionary = getOptionalObject(json, "py"); if (xDictionary.has_value() && yDictionary.has_value()) { _positionX = KeyframeGroup(xDictionary.value()); _positionY = KeyframeGroup(yDictionary.value()); _position = std::nullopt; _positionInternalRepresentation = PositionInternalRepresentation::TopLevelXY; } else if (const auto positionData = getOptionalObject(json, "p")) { try { LottieParsingException::Guard expectedGuard; _position = KeyframeGroup(positionData.value()); _positionX = std::nullopt; _positionX = std::nullopt; _positionInternalRepresentation = PositionInternalRepresentation::TopLevelCombined; } catch(...) { auto xData = getObject(positionData.value(), "x"); auto yData = getObject(positionData.value(), "y"); _positionX = KeyframeGroup(xData); _positionY = KeyframeGroup(yData); _position = std::nullopt; _positionInternalRepresentation = PositionInternalRepresentation::NestedXY; _extra_positionS = getOptionalBool(positionData.value(), "s"); } } else { _position = std::nullopt; _positionX = std::nullopt; _positionY = std::nullopt; _positionInternalRepresentation = PositionInternalRepresentation::None; } } catch(...) { throw LottieParsingException(); } try { // Scale if (const auto scaleData = getOptionalObject(json, "s")) { _scale = KeyframeGroup(scaleData.value()); } // Rotation if (const auto rotationZData = getOptionalObject(json, "rz")) { _rotationZ = KeyframeGroup(rotationZData.value()); _rotationZInternalRepresentation = RotationZInternalRepresentation::RZ; } else if (const auto rotationData = getOptionalObject(json, "r")) { _rotation = KeyframeGroup(rotationData.value()); _rotationZInternalRepresentation = RotationZInternalRepresentation::R; } // Opacity if (const auto opacityData = getOptionalObject(json, "o")) { _opacity = KeyframeGroup(opacityData.value()); } } catch(...) { throw LottieParsingException(); } _extraTy = getOptionalString(json, "ty"); _extraSa = getOptionalAny(json, "sa"); _extraSk = getOptionalAny(json, "sk"); } json11::Json::object toJson() const { json11::Json::object result; if (_anchorPoint.has_value()) { result.insert(std::make_pair("a", _anchorPoint->toJson())); } switch (_positionInternalRepresentation) { case PositionInternalRepresentation::None: assert(!_positionX.has_value()); assert(!_positionY.has_value()); assert(!_position.has_value()); break; case PositionInternalRepresentation::TopLevelXY: assert(_positionX.has_value()); assert(_positionY.has_value()); assert(!_position.has_value()); result.insert(std::make_pair("x", _positionX->toJson())); result.insert(std::make_pair("y", _positionY->toJson())); break; case PositionInternalRepresentation::TopLevelCombined: assert(_position.has_value()); assert(!_positionX.has_value()); assert(!_positionY.has_value()); result.insert(std::make_pair("p", _position->toJson())); break; case PositionInternalRepresentation::NestedXY: json11::Json::object nestedPosition; assert(_positionX.has_value()); assert(_positionY.has_value()); assert(!_position.has_value()); nestedPosition.insert(std::make_pair("x", _positionX->toJson())); nestedPosition.insert(std::make_pair("y", _positionY->toJson())); if (_extra_positionS.has_value()) { nestedPosition.insert(std::make_pair("s", _extra_positionS.value())); } result.insert(std::make_pair("p", nestedPosition)); break; } if (_scale.has_value()) { result.insert(std::make_pair("s", _scale->toJson())); } if (_rotation.has_value()) { switch (_rotationZInternalRepresentation) { case RotationZInternalRepresentation::RZ: result.insert(std::make_pair("rz", _rotation->toJson())); break; case RotationZInternalRepresentation::R: result.insert(std::make_pair("r", _rotation->toJson())); break; } } if (_opacity.has_value()) { result.insert(std::make_pair("o", _opacity->toJson())); } if (_extraTy.has_value()) { result.insert(std::make_pair("ty", _extraTy.value())); } if (_extraSa.has_value()) { result.insert(std::make_pair("sa", _extraSa.value())); } if (_extraSk.has_value()) { result.insert(std::make_pair("sk", _extraSk.value())); } return result; } KeyframeGroup anchorPoint() { if (_anchorPoint.has_value()) { return _anchorPoint.value(); } else { return KeyframeGroup(Vector3D(0.0, 0.0, 0.0)); } } KeyframeGroup scale() const { if (_scale) { return _scale.value(); } else { return KeyframeGroup(Vector3D(100.0, 100.0, 100.0)); } } KeyframeGroup rotation() const { if (_rotation) { return _rotation.value(); } else { return KeyframeGroup(Vector1D(0.0)); } } KeyframeGroup opacity() const { if (_opacity) { return _opacity.value(); } else { return KeyframeGroup(Vector1D(100.0)); } } std::optional> const &position() const { return _position; } std::optional> const &positionX() const { return _positionX; } std::optional> const &positionY() const { return _positionY; } private: /// The anchor point of the transform. std::optional> _anchorPoint; /// The position of the transform. This is nil if the position data was split. std::optional> _position; /// The positionX of the transform. This is nil if the position property is set. std::optional> _positionX; /// The positionY of the transform. This is nil if the position property is set. std::optional> _positionY; PositionInternalRepresentation _positionInternalRepresentation = PositionInternalRepresentation::None; /// The scale of the transform std::optional> _scale; /// The rotation of the transform. Note: This is single dimensional rotation. std::optional> _rotation; /// The opacity of the transform. std::optional> _opacity; /// Should always be nil. std::optional> _rotationZ; RotationZInternalRepresentation _rotationZInternalRepresentation; std::optional _extra_positionS; std::optional _extraTy; std::optional _extraSa; std::optional _extraSk; }; } #endif /* Transform_hpp */