File indexing completed on 2024-12-15 04:01:43

0001 /*
0002  * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best>
0003  *
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  */
0006 
0007 #include <pybind11/operators.h>
0008 
0009 #include "model/document.hpp"
0010 #include "model/visitor.hpp"
0011 
0012 #include "model/shapes/group.hpp"
0013 #include "model/shapes/layer.hpp"
0014 #include "model/shapes/precomp_layer.hpp"
0015 #include "model/shapes/rect.hpp"
0016 #include "model/shapes/ellipse.hpp"
0017 #include "model/shapes/path.hpp"
0018 #include "model/shapes/polystar.hpp"
0019 #include "model/shapes/fill.hpp"
0020 #include "model/shapes/stroke.hpp"
0021 #include "model/shapes/image.hpp"
0022 #include "model/shapes/repeater.hpp"
0023 #include "model/shapes/trim.hpp"
0024 #include "model/shapes/inflate_deflate.hpp"
0025 #include "model/shapes/round_corners.hpp"
0026 #include "model/shapes/offset_path.hpp"
0027 #include "model/shapes/zig_zag.hpp"
0028 
0029 #include "model/assets/assets.hpp"
0030 #include "model/assets/named_color.hpp"
0031 #include "model/assets/composition.hpp"
0032 
0033 #include "command/animation_commands.hpp"
0034 #include "command/undo_macro_guard.hpp"
0035 #include "command/object_list_commands.hpp"
0036 
0037 #include "io/glaxnimate/glaxnimate_format.hpp"
0038 #include "io/raster/raster_format.hpp"
0039 #include "io/raster/raster_mime.hpp"
0040 #include "io/svg/svg_format.hpp"
0041 #include "io/svg/svg_renderer.hpp"
0042 #include "io/rive/rive_format.hpp"
0043 
0044 
0045 #include "plugin/io.hpp"
0046 #include "app_info.hpp"
0047 
0048 #include "miscdefs.hpp"
0049 
0050 using namespace app::scripting::python;
0051 using namespace glaxnimate;
0052 
0053 namespace {
0054 
0055 template<class T, class Base=model::AnimatableBase>
0056 void register_animatable(py::module& m)
0057 {
0058     std::string name = "AnimatedProperty<";
0059 #if QT_VERSION_MAJOR < 6
0060     name += QMetaType::typeName(qMetaTypeId<T>());
0061 #else
0062     name += QMetaType::fromType<T>().name();
0063 #endif
0064     name += ">";
0065     py::class_<model::AnimatedProperty<T>, Base>(m, name.c_str());
0066 }
0067 
0068 static QImage doc_to_image(model::Composition* comp)
0069 {
0070     return io::raster::RasterMime::to_image({comp});
0071 }
0072 
0073 static QByteArray frame_to_svg(model::Composition* comp)
0074 {
0075     QByteArray data;
0076     QBuffer file(&data);
0077     file.open(QIODevice::WriteOnly);
0078 
0079     io::svg::SvgRenderer rend(io::svg::NotAnimated, io::svg::CssFontType::FontFace);
0080     rend.write_main(comp);
0081     rend.write(&file, true);
0082 
0083     return data;
0084 }
0085 
0086 void define_io(py::module& m)
0087 {
0088     py::module io = m.def_submodule("io", "Input/Output utilities");
0089 
0090     py::class_<io::mime::MimeSerializer>(io, "MimeSerializer")
0091         .def_property_readonly("slug", &io::mime::MimeSerializer::slug)
0092         .def_property_readonly("name", &io::mime::MimeSerializer::name)
0093         .def_property_readonly("mime_types", &io::mime::MimeSerializer::mime_types)
0094         .def("serialize", &io::mime::MimeSerializer::serialize)
0095     ;
0096 
0097     const char* to_image_docstring = "Renders the current frame to an image";
0098     py::class_<io::raster::RasterMime, io::mime::MimeSerializer>(io, "RasterMime")
0099         .def_static("render_frame", &io::raster::RasterMime::to_image, to_image_docstring)
0100         .def_static("render_frame", &doc_to_image, to_image_docstring)
0101         .def_static("render_frame", &io::raster::RasterMime::frame_to_image,
0102                     "Renders the given frame to image",
0103                     py::arg("node"), py::arg("frame"))
0104     ;
0105 
0106     using Fac = io::IoRegistry;
0107     py::class_<Fac, std::unique_ptr<Fac, py::nodelete>>(io, "IoRegistry")
0108         .def("importers", &Fac::importers, no_own)
0109         .def("exporters", &Fac::exporters, no_own)
0110         .def("from_extension", &Fac::from_extension, no_own)
0111         .def("from_filename", &Fac::from_filename, no_own)
0112         .def("from_slug", &Fac::from_slug, no_own)
0113         .def("__getitem__", &Fac::from_slug, no_own)
0114         .def("serializers", &Fac::serializers, no_own)
0115         .def("serializer_from_slug", &Fac::serializer_from_slug, no_own)
0116     ;
0117 
0118     io.attr("registry") = std::unique_ptr<Fac, py::nodelete>(&io::IoRegistry::instance());
0119 
0120     auto import_export = register_from_meta<io::ImportExport, QObject>(io, enums<io::ImportExport::Direction>{})
0121         .def("progress_max_changed", &io::ImportExport::progress_max_changed)
0122         .def("progress", &io::ImportExport::progress)
0123     ;
0124     io.attr("Direction") = import_export.attr("Direction");
0125 
0126     register_from_meta<io::glaxnimate::GlaxnimateFormat, io::ImportExport>(io)
0127         .attr("instance") = std::unique_ptr<io::glaxnimate::GlaxnimateFormat, py::nodelete>(io::glaxnimate::GlaxnimateFormat::instance())
0128     ;
0129 
0130     register_from_meta<io::raster::RasterFormat, io::ImportExport>(io)
0131         .def_static("render_frame", &io::raster::RasterMime::to_image, to_image_docstring)
0132         .def_static("render_frame", &doc_to_image, to_image_docstring)
0133     ;
0134 
0135     register_from_meta<io::svg::SvgFormat, io::ImportExport>(io)
0136         .def_static("render_frame", &frame_to_svg, "renders the current frame to SVG")
0137     ;
0138 
0139     register_from_meta<plugin::IoFormat, io::ImportExport>(io);
0140 
0141 
0142     register_from_meta<io::rive::RiveFormat, io::ImportExport>(io)
0143         .def("to_json_data", [](io::rive::RiveFormat& self, const QByteArray& data){
0144             return self.to_json(data).toJson();
0145         }, py::arg("binary_data"))
0146     ;
0147 }
0148 
0149 
0150 void define_animatable(py::module& m)
0151 {
0152     py::class_<model::KeyframeTransition> kt(m, "KeyframeTransition");
0153     kt.attr("Descriptive") = py::enum_<model::KeyframeTransition::Descriptive>(kt, "Descriptive")
0154         .value("Hold", model::KeyframeTransition::Hold)
0155         .value("Linear", model::KeyframeTransition::Linear)
0156         .value("Ease", model::KeyframeTransition::Ease)
0157         .value("Fast", model::KeyframeTransition::Fast)
0158         .value("Custom", model::KeyframeTransition::Custom)
0159     ;
0160     kt
0161         .def(py::init<>())
0162         .def(py::init<const QPointF&, const QPointF&>())
0163         .def_property("hold", &model::KeyframeTransition::hold, &model::KeyframeTransition::set_hold)
0164         .def_property("before", &model::KeyframeTransition::before, &model::KeyframeTransition::set_before)
0165         .def_property("after", &model::KeyframeTransition::after, &model::KeyframeTransition::set_after)
0166         .def_property("before_descriptive", &model::KeyframeTransition::before_descriptive, &model::KeyframeTransition::set_before_descriptive)
0167         .def_property("after_descriptive", &model::KeyframeTransition::after_descriptive, &model::KeyframeTransition::set_after_descriptive)
0168         .def("lerp_factor", &model::KeyframeTransition::lerp_factor)
0169         .def("bezier_parameter", &model::KeyframeTransition::bezier_parameter)
0170     ;
0171 
0172     py::class_<model::KeyframeBase>(m, "Keyframe")
0173         .def_property_readonly("time", &model::KeyframeBase::time)
0174         .def_property_readonly("value", &model::KeyframeBase::value)
0175         .def_property("transition",
0176             &model::KeyframeBase::transition,
0177             &model::KeyframeBase::set_transition,
0178             no_own
0179         )
0180     ;
0181     register_from_meta<model::AnimatableBase, QObject>(m)
0182         .def("keyframe", [](const model::AnimatableBase& a, model::FrameTime t){ return a.keyframe(t); }, no_own, py::arg("time"))
0183         .def("set_keyframe", [](model::AnimatableBase& a, model::FrameTime time, const QVariant& value){
0184             a.object()->document()->undo_stack().push(
0185                 new command::SetKeyframe(&a, time, value, true)
0186             );
0187             return a.keyframe(a.keyframe_index(time));
0188         }, no_own, py::arg("time"), py::arg("value"))
0189         .def("remove_keyframe_at_time", [](model::AnimatableBase& a, model::FrameTime time){
0190             a.object()->document()->undo_stack().push(
0191                 new command::RemoveKeyframeTime(&a, time)
0192             );
0193         }, py::arg("time"))
0194         .def("clear_keyframes", &model::AnimatableBase::clear_keyframes_undoable, py::arg("value") = py::none())
0195     ;
0196 }
0197 
0198 class PyVisitorPublic : public model::Visitor
0199 {
0200 public:
0201     virtual void on_visit_doc(model::Document *){}
0202     virtual void on_visit_node(model::DocumentNode*){}
0203 
0204 private:
0205     void on_visit_document(model::Document * document, model::Composition*) override
0206     {
0207         on_visit_doc(document);
0208     }
0209 
0210     void on_visit(model::DocumentNode * node) override
0211     {
0212         on_visit_node(node);
0213     }
0214 };
0215 
0216 class PyVisitorTrampoline : public PyVisitorPublic
0217 {
0218 public:
0219     void on_visit_doc(model::Document * document) override
0220     {
0221         PYBIND11_OVERLOAD(void, PyVisitorPublic, on_visit_doc, document);
0222     }
0223 
0224     void on_visit(model::DocumentNode * node) override
0225     {
0226         PYBIND11_OVERLOAD_PURE(void, PyVisitorPublic, on_visit_node, node);
0227     }
0228 };
0229 
0230 template<class Owner, class PropT, class ItemT = typename PropT::value_type>
0231 class AddShapeBase
0232 {
0233 public:
0234     using PtrMem = PropT Owner::*;
0235 
0236     AddShapeBase(PtrMem p) noexcept : ptr(p) {}
0237 
0238 protected:
0239     ItemT* create(model::Document* doc, PropT& prop, const QString& clsname, int index) const
0240     {
0241         auto obj = model::Factory::static_build(clsname, doc);
0242         if ( !obj )
0243             return nullptr;
0244 
0245         auto cast = obj->cast<ItemT>();
0246 
0247         if ( !cast )
0248         {
0249             delete obj;
0250             return nullptr;
0251         }
0252 
0253         if constexpr ( std::is_base_of_v<model::DocumentNode, ItemT> )
0254             doc->set_best_name(static_cast<model::DocumentNode*>(cast));
0255         else
0256             cast->name.set(cast->type_name_human());
0257 
0258         doc->push_command(new command::AddObject<ItemT, PropT>(&prop, std::unique_ptr<ItemT>(cast), index));
0259 
0260         return cast;
0261     }
0262 
0263     PtrMem ptr;
0264 };
0265 
0266 template<class Owner, class PropT, class ItemT = typename PropT::value_type>
0267 class AddShapeName : public AddShapeBase<Owner, PropT, ItemT>
0268 {
0269 public:
0270     using AddShapeBase<Owner, PropT, ItemT>::AddShapeBase;
0271 
0272     ItemT* operator() (Owner* owner, const QString& clsname, int index = -1) const
0273     {
0274         return this->create(owner->document(), owner->*(this->ptr), clsname, index);
0275     }
0276 };
0277 
0278 template<class Owner, class PropT, class ItemT = typename PropT::value_type>
0279 class AddShapeClass : public AddShapeBase<Owner, PropT, ItemT>
0280 {
0281 public:
0282     using AddShapeBase<Owner, PropT, ItemT>::AddShapeBase;
0283 
0284     ItemT* operator() (Owner* owner, const py::object& cls, int index = -1) const
0285     {
0286         pybind11::detail::type_caster<QString> cast;
0287         cast.load(cls.attr("__name__"), true);
0288         return this->create(owner->document(), owner->*(this->ptr), cast, index);
0289     }
0290 };
0291 
0292 template<class Owner, class PropT, class ItemT = typename PropT::value_type>
0293 class AddShapeClone
0294 {
0295 public:
0296     using PtrMem = PropT Owner::*;
0297 
0298     AddShapeClone(PtrMem p) noexcept : ptr(p) {}
0299 
0300     ItemT* operator() (Owner* owner, ItemT* object, int index = -1) const
0301     {
0302         if ( !object )
0303             return nullptr;
0304 
0305         std::unique_ptr<ItemT> clone(static_cast<ItemT*>(object->clone().release()));
0306         if ( clone->document() != owner->document() )
0307             clone->transfer(owner->document());
0308 
0309         auto ptr = clone.get();
0310 
0311         owner->push_command(new command::AddObject<ItemT, PropT>(&(owner->*(this->ptr)), std::move(clone), index));
0312 
0313         return ptr;
0314     }
0315 
0316 private:
0317 
0318     PtrMem ptr;
0319 };
0320 
0321 template<
0322     class PyClass,
0323     class PropT = model::ObjectListProperty<model::ShapeElement>,
0324     class Owner = typename PyClass::type,
0325     class ItemT = typename PropT::value_type
0326 >
0327 void define_add_shape(PyClass& cls, PropT Owner::* prop = &Owner::shapes, const std::string& name = "add_shape")
0328 {
0329     cls.def(
0330             name.c_str(),
0331             AddShapeName<Owner, PropT, ItemT>(prop),
0332             no_own,
0333             "Adds a shape from its class name",
0334             py::arg("type_name"),
0335             py::arg("index") = -1
0336         )
0337         .def(
0338             name.c_str(),
0339             AddShapeClone<Owner, PropT, ItemT>(prop),
0340             no_own,
0341             "Adds a shape, note that the input object is cloned, and the clone is returned. The document will have ownership over the clone.",
0342             py::arg("object"),
0343             py::arg("index") = -1
0344         )
0345         .def(
0346             name.c_str(),
0347             AddShapeClass<Owner, PropT, ItemT>(prop),
0348             no_own,
0349             "Adds a shape from its class",
0350             py::arg("cls"),
0351             py::arg("index") = -1
0352         )
0353     ;
0354 }
0355 
0356 template<class Cls, class... Args, class... FwArgs>
0357 auto register_constructible(py::module& module, FwArgs&&... args)
0358 {
0359     return register_from_meta<Cls, Args...>(module, std::forward<FwArgs>(args)...)
0360         .def(py::init([](model::Document* doc) -> std::unique_ptr<Cls> {
0361             if ( !doc )
0362                 return {};
0363             return std::make_unique<Cls>(doc);
0364         }));
0365 }
0366 
0367 } // namespace
0368 
0369 
0370 void register_py_module(py::module& glaxnimate_module)
0371 {
0372     glaxnimate_module.attr("__version__") = AppInfo::instance().version();
0373 
0374     define_utils(glaxnimate_module);
0375     define_log(glaxnimate_module);
0376     py::module detail = define_detail(glaxnimate_module);
0377     define_environment(glaxnimate_module);
0378 
0379     // for some reason some classes aren't seen without this o_O
0380     static std::vector<int> foo = {
0381         qMetaTypeId<model::DocumentNode*>(),
0382         qMetaTypeId<model::NamedColor*>(),
0383         qMetaTypeId<model::Bitmap*>(),
0384         qMetaTypeId<model::Gradient*>(),
0385         qMetaTypeId<model::EmbeddedFont*>(),
0386         qMetaTypeId<io::ImportExport::Direction>(),
0387     };
0388 
0389     py::module model = glaxnimate_module.def_submodule("model", "");
0390     py::class_<model::Object, QObject>(model, "Object")
0391         .def(
0392             "stretch_time",
0393             [](model::Object* object, double multiplier){
0394                 if ( multiplier > 0 )
0395                     object->push_command(new command::StretchTimeCommand(object, multiplier));
0396             },
0397             py::arg("multiplier"),
0398             "Stretches animation timings by the given factor"
0399         )
0400     ;
0401 
0402     py::class_<command::UndoMacroGuard>(model, "UndoMacroGuard")
0403         .def("__enter__", &command::UndoMacroGuard::start)
0404         .def("__exit__", [](command::UndoMacroGuard& g, pybind11::object, pybind11::object, pybind11::object){
0405             g.finish();
0406         })
0407         .def("start", &command::UndoMacroGuard::start)
0408         .def("finish", &command::UndoMacroGuard::finish)
0409         .attr("__doc__") = "Context manager that creates undo macros"
0410     ;
0411 
0412     register_from_meta<model::DocumentNode, model::Object>(model)
0413         .def_property_readonly("users", &model::DocumentNode::users, "List of properties pointing to this object")
0414     ;
0415 
0416     auto document = register_from_meta<model::Document, QObject>(model)
0417         .def(py::init<QString>())
0418         .def(py::init<>())
0419         .def(
0420             "macro",
0421              [](model::Document* document, const QString& str){
0422                 return new command::UndoMacroGuard(str, document, false);
0423             },
0424             py::return_value_policy::take_ownership,
0425             "Context manager to group changes into a single undo command"
0426         )
0427         .def(
0428             "stretch_time",
0429             [](model::Document* document, double multiplier){
0430                 if ( multiplier > 0 )
0431                     document->push_command(new command::StretchTimeCommand(document, multiplier));
0432             },
0433             py::arg("multiplier"),
0434             "Stretches animation timings by the given factor"
0435         )
0436         .def_property("metadata", &model::Document::metadata, &model::Document::set_metadata, no_own)
0437         .def_property("info", &model::Document::info, [](model::Document* doc, const model::Document::DocumentInfo& info){ doc->info() = info; }, no_own)
0438     ;
0439     py::class_<model::Document::DocumentInfo>(document, "DocumentInfo")
0440         .def_readwrite("description", &model::Document::DocumentInfo::description)
0441         .def_readwrite("author", &model::Document::DocumentInfo::author)
0442         .def_readwrite("keywords", &model::Document::DocumentInfo::keywords)
0443     ;
0444 
0445     register_from_meta<model::VisualNode, model::DocumentNode>(model);
0446     register_from_meta<model::AnimationContainer, model::Object>(model);
0447     register_from_meta<model::StretchableTime, model::Object>(model);
0448     register_from_meta<model::Transform, model::Object>(model);
0449     register_from_meta<model::MaskSettings, model::Object>(model);
0450 
0451     py::module shapes = model.def_submodule("shapes", "");
0452     register_from_meta<model::ShapeElement, model::VisualNode>(shapes)
0453         .def("to_path", &model::ShapeElement::to_path)
0454     ;
0455 
0456     py::module defs = model.def_submodule("assets", "");
0457     py::class_<model::AssetBase>(defs, "AssetBase");
0458     auto cls_comp = register_from_meta<model::Composition, model::VisualNode, model::AssetBase>(model);
0459     define_add_shape(cls_comp);
0460 
0461     define_io(glaxnimate_module);
0462 
0463     define_animatable(model);
0464     register_from_meta<model::detail::AnimatedPropertyPosition, model::AnimatableBase>(detail);
0465     register_animatable<QPointF, model::detail::AnimatedPropertyPosition>(detail);
0466     register_animatable<QSizeF>(detail);
0467     register_animatable<QVector2D>(detail);
0468     register_animatable<QColor>(detail);
0469     register_animatable<float>(detail);
0470     register_animatable<QGradientStops>(detail);
0471     register_from_meta<model::detail::AnimatedPropertyBezier, model::AnimatableBase>(detail);
0472     register_animatable<math::bezier::Bezier, model::detail::AnimatedPropertyBezier>(detail);
0473 
0474     py::class_<PyVisitorPublic, PyVisitorTrampoline>(model, "Visitor")
0475         .def(py::init())
0476         .def("visit", (void (PyVisitorPublic::*)(model::Document*, model::Composition*, bool))&PyVisitorPublic::visit, py::arg("document"), py::arg("composition"), py::arg("skip_locked"))
0477         .def("visit", (void (PyVisitorPublic::*)(model::DocumentNode*, bool))&PyVisitorPublic::visit, py::arg("node"), py::arg("skip_locked"))
0478         .def("on_visit_document", &PyVisitorPublic::on_visit_doc)
0479         .def("on_visit_node", &PyVisitorPublic::on_visit_node)
0480     ;
0481 
0482     register_from_meta<model::Asset, model::DocumentNode, model::AssetBase>(defs);
0483     register_from_meta<model::BrushStyle, model::Asset>(defs);
0484     register_constructible<model::NamedColor, model::BrushStyle>(defs);
0485     register_constructible<model::GradientColors, model::Asset>(defs);
0486     register_constructible<model::Gradient, model::BrushStyle>(defs, enums<model::Gradient::GradientType>{});
0487     register_constructible<model::Bitmap, model::Asset>(defs);
0488     register_from_meta<model::EmbeddedFont, model::Asset>(defs);
0489     register_from_meta<model::BitmapList, model::DocumentNode>(defs);
0490     register_from_meta<model::NamedColorList, model::DocumentNode>(defs);
0491     register_from_meta<model::GradientList, model::DocumentNode>(defs);
0492     register_from_meta<model::GradientColorsList, model::DocumentNode>(defs);
0493     register_from_meta<model::CompositionList, model::DocumentNode>(defs);
0494     register_from_meta<model::FontList, model::DocumentNode>(defs);
0495     register_from_meta<model::Assets, model::DocumentNode>(defs);
0496 
0497     register_from_meta<model::Shape, model::ShapeElement>(shapes);
0498     register_from_meta<model::Modifier, model::ShapeElement>(shapes);
0499     register_from_meta<model::Styler, model::ShapeElement>(shapes);
0500 
0501     register_constructible<model::Rect, model::Shape>(shapes);
0502     register_constructible<model::Ellipse, model::Shape>(shapes);
0503     register_constructible<model::PolyStar, model::Shape>(shapes, enums<model::PolyStar::StarType>{});
0504     register_constructible<model::Path, model::Shape>(shapes);
0505 
0506     auto cls_group = register_constructible<model::Group, model::ShapeElement>(shapes);
0507     define_add_shape(cls_group);
0508 
0509     register_constructible<model::Layer, model::Group>(shapes);
0510     register_constructible<model::PreCompLayer, model::ShapeElement>(shapes);
0511     register_constructible<model::Image, model::ShapeElement>(shapes);
0512 
0513     register_constructible<model::Fill, model::Styler>(shapes, enums<model::Fill::Rule>{});
0514     register_constructible<model::Stroke, model::Styler>(shapes, enums<model::Stroke::Cap, model::Stroke::Join>{});
0515     register_constructible<model::Repeater, model::Modifier>(shapes);
0516 
0517     register_from_meta<model::PathModifier, model::Modifier>(shapes);
0518     register_constructible<model::Trim, model::PathModifier>(shapes);
0519     register_constructible<model::InflateDeflate, model::PathModifier>(shapes);
0520     register_constructible<model::RoundCorners, model::PathModifier>(shapes);
0521     register_constructible<model::OffsetPath, model::PathModifier>(shapes);
0522     register_constructible<model::ZigZag, model::PathModifier>(shapes);
0523 }