// Copyright (c) 2012-2017 VideoStitch SAS // Copyright (c) 2018 stitchEm #include "panoInputDefsPimpl.hpp" #include "libvideostitch/overlayInputDef.hpp" #include "parse/json.hpp" #include "common/angles.hpp" #include "libvideostitch/logging.hpp" #include "libvideostitch/cameraDef.hpp" #include <sstream> namespace VideoStitch { namespace Core { GENCURVEFUNCTIONS(OverlayInputDefinition, Curve, ScaleCurve, scaleCurve, PTV_DEFAULT_OVERLAY_SCALE) GENCURVEFUNCTIONS(OverlayInputDefinition, Curve, AlphaCurve, alphaCurve, PTV_DEFAULT_OVERLAY_ALPHA) GENCURVEFUNCTIONS(OverlayInputDefinition, Curve, TransXCurve, transXCurve, PTV_DEFAULT_OVERLAY_TRANSX) GENCURVEFUNCTIONS(OverlayInputDefinition, Curve, TransYCurve, transYCurve, PTV_DEFAULT_OVERLAY_TRANSY) GENCURVEFUNCTIONS(OverlayInputDefinition, Curve, TransZCurve, transZCurve, PTV_DEFAULT_OVERLAY_TRANSZ) GENCURVEFUNCTIONS(OverlayInputDefinition, QuaternionCurve, RotationCurve, rotationCurve, Quaternion<double>()) OverlayInputDefinition::Pimpl::Pimpl() : globalOrientationApplied(false), scaleCurve(new Curve(PTV_DEFAULT_OVERLAY_SCALE)), alphaCurve(new Curve(PTV_DEFAULT_OVERLAY_ALPHA)), transXCurve(new Curve(PTV_DEFAULT_OVERLAY_TRANSX)), transYCurve(new Curve(PTV_DEFAULT_OVERLAY_TRANSY)), transZCurve(new Curve(PTV_DEFAULT_OVERLAY_TRANSZ)), rotationCurve(new QuaternionCurve(Quaternion<double>())) {} OverlayInputDefinition::OverlayInputDefinition() : ReaderInputDefinition(), pimpl(new Pimpl()) {} OverlayInputDefinition* OverlayInputDefinition::create(const Ptv::Value& value, bool enforceMandatoryFields) { std::unique_ptr<OverlayInputDefinition> res(new OverlayInputDefinition()); if (!res->applyDiff(value, enforceMandatoryFields).ok()) { return nullptr; } std::stringstream errors; if (!res->validate(errors)) { Logger::get(Logger::Error) << errors.str(); return nullptr; } return res.release(); } Status OverlayInputDefinition::applyDiff(const Ptv::Value& value, bool enforceMandatoryFields) { Status stat; // Make sure value is an object. if (!Parse::checkType("OverlayInputDefinition", value, Ptv::Value::OBJECT)) { return {Origin::PanoramaConfiguration, ErrType::InvalidConfiguration, "Could not find valid 'OverlayInputDefinition', expected object type"}; } stat = ReaderInputDefinition::applyDiff(value, enforceMandatoryFields); FAIL_RETURN(stat); #define POPULATE_CURVE_PROPAGATE_WRONGTYPE(config_name, varName, varType) \ { \ const Ptv::Value* var = value.has(config_name); \ if (var) { \ varType* curve = varType::create(*var); \ pimpl->varName.reset(curve); \ } else { \ return {Origin::PanoramaConfiguration, ErrType::InvalidConfiguration, \ "Invalid type for '" config_name "' in OverlayInputDefinition, expected curve value"}; \ } \ } #define POPULATE_BOOL_PROPAGATE_WRONGTYPE(config_name, varName, shouldEnforce) \ if (Parse::populateBool("OverlayInputDefinition", value, config_name, varName, shouldEnforce) == \ Parse::PopulateResult_WrongType) { \ return {Origin::PanoramaConfiguration, ErrType::InvalidConfiguration, \ "Invalid type for '" config_name "' in OverlayInputDefinition, expected boolean value"}; \ } #define POPULATE_INT_PROPAGATE_WRONGTYPE(config_name, varName, shouldEnforce) \ if (Parse::populateInt("OverlayInputDefinition", value, config_name, varName, shouldEnforce) == \ Parse::PopulateResult_WrongType) { \ return {Origin::PanoramaConfiguration, ErrType::InvalidConfiguration, \ "Invalid type for '" config_name "' in OverlayInputDefinition, expected integer value"}; \ } POPULATE_BOOL_PROPAGATE_WRONGTYPE("global_orientation_applied", pimpl->globalOrientationApplied, false); POPULATE_CURVE_PROPAGATE_WRONGTYPE("scale", scaleCurve, Curve); POPULATE_CURVE_PROPAGATE_WRONGTYPE("alpha", alphaCurve, Curve); POPULATE_CURVE_PROPAGATE_WRONGTYPE("transX", transXCurve, Curve); POPULATE_CURVE_PROPAGATE_WRONGTYPE("transY", transYCurve, Curve); POPULATE_CURVE_PROPAGATE_WRONGTYPE("transZ", transZCurve, Curve); POPULATE_CURVE_PROPAGATE_WRONGTYPE("rotation", rotationCurve, QuaternionCurve); #undef POPULATE_CURVE_PROPAGATE_WRONGTYPE #undef POPULATE_BOOL_PROPAGATE_WRONGTYPE #undef POPULATE_INT_PROPAGATE_WRONGTYPE return stat; }; bool OverlayInputDefinition::validate(std::ostream& os) const { if (!ReaderInputDefinition::validate(os)) { return false; } #define OS_MESSAGE(varName, infValue, supValue) \ { \ os << #varName << " must be in the interval [" << infValue << ", " << supValue << "]." << std::endl; \ return false; \ } #define VALIDATE_CURVE_VALUE(varName, infValue, supValue) \ { \ auto spline = get##varName().splines(); \ if (spline) { \ while (spline) { \ if (spline->end.v < infValue || spline->end.v > supValue) { \ OS_MESSAGE(varName, infValue, supValue) \ } else { \ spline = spline->next; \ } \ } \ } else { \ if (get##varName().getConstantValue() <= infValue || get##varName().getConstantValue() > supValue) { \ OS_MESSAGE(varName, infValue, supValue) \ } \ } \ } VALIDATE_CURVE_VALUE(ScaleCurve, 0.0, 1.0); VALIDATE_CURVE_VALUE(AlphaCurve, 0.0, 1.0); #undef OS_MESSAGE #undef VALIDATE_CURVE_VALUE return true; } OverlayInputDefinition* OverlayInputDefinition::clone() const { OverlayInputDefinition* result = new OverlayInputDefinition(); cloneTo(result); #define AUTO_CURVE_COPY(curve) result->replace##curve(get##curve().clone()) result->pimpl->globalOrientationApplied = pimpl->globalOrientationApplied; AUTO_CURVE_COPY(ScaleCurve); AUTO_CURVE_COPY(TransXCurve); AUTO_CURVE_COPY(TransYCurve); AUTO_CURVE_COPY(TransZCurve); AUTO_CURVE_COPY(RotationCurve); AUTO_CURVE_COPY(AlphaCurve); #undef AUTO_CURVE_COPY return result; } bool OverlayInputDefinition::operator==(const OverlayInputDefinition& other) const { if (!ReaderInputDefinition::operator==(other)) { return false; } #define FIELD_EQUAL(getter) (getter() == other.getter()) if (!(FIELD_EQUAL(getGlobalOrientationApplied) && FIELD_EQUAL(getScaleCurve) && FIELD_EQUAL(getTransXCurve) && FIELD_EQUAL(getTransYCurve) && FIELD_EQUAL(getTransZCurve) && FIELD_EQUAL(getRotationCurve) && FIELD_EQUAL(getAlphaCurve))) { return false; } return true; #undef FIELD_EQUAL } OverlayInputDefinition::~OverlayInputDefinition() { delete pimpl; } OverlayInputDefinition::Pimpl::~Pimpl() {} Ptv::Value* OverlayInputDefinition::serialize() const { Ptv::Value* res = ReaderInputDefinition::serialize(); res->push("global_orientation_applied", new Parse::JsonValue(getGlobalOrientationApplied())); res->push("scale", getScaleCurve().serialize()); res->push("alpha", getAlphaCurve().serialize()); res->push("transX", getTransXCurve().serialize()); res->push("transY", getTransYCurve().serialize()); res->push("transZ", getTransZCurve().serialize()); res->push("rotation", getRotationCurve().serialize()); return res; } /************ Getters and Setters **********/ bool OverlayInputDefinition::getGlobalOrientationApplied() const { return pimpl->globalOrientationApplied; } void OverlayInputDefinition::setGlobalOrientationApplied(const bool status) { pimpl->globalOrientationApplied = status; } } // namespace Core } // namespace VideoStitch