Program Listing for File GraphicalSceneMovie.cc

Program Listing for File GraphicalSceneMovie.cc#

Return to documentation for file (doxygen_docs/GeneralKModels/GraphicalSceneMovie.cc)

#include "Karana/GeneralKModels/GraphicalSceneMovie.h"
#include "Karana/KCore/Allocator.h"
#include <filesystem>

/**
 * @file
 * @brief GraphicalSceneMovie implementation.
 */

namespace Karana::Models {

    namespace ks = Karana::Scene;
    namespace km = Karana::Math;

    GraphicalSceneMovie::GraphicalSceneMovie(std::string_view name,
                                             const kc::ks_ptr<kd::ModelManager> &mm,
                                             const kc::ks_ptr<ks::ProxyScene> &scene)
        : KModel<GraphicalSceneMovie, GraphicalSceneMovieParams>(name, mm)
        , _scene(scene) {
        params = std::allocate_shared<GraphicalSceneMovieParams>(
            kc::Allocator<GraphicalSceneMovieParams>{}, std::format("{}_params", name));
    };

    kc::ks_ptr<GraphicalSceneMovie>
    GraphicalSceneMovie::create(std::string_view name,
                                const kc::ks_ptr<kd::ModelManager> &mm,
                                const kc::ks_ptr<ks::ProxyScene> &scene) {
        kc::ks_ptr<GraphicalSceneMovie> gsm = std::allocate_shared<GraphicalSceneMovie>(
            kc::Allocator<GraphicalSceneMovie>{}, name, mm, scene);
        mm->registerModel(gsm);
        return gsm;
    }

    void GraphicalSceneMovie::postModelStep(const km::Ktime &, const km::Vec &) {
        _scene->update(*_scene->graphics());
        _scene->graphics()->renderToFile(
            std::format("{}/{}_{:06d}.png", params->dir, params->filename_prefix, _frame_no));
        _frame_no++;
    }

    GraphicalSceneMovieParams::GraphicalSceneMovieParams(std::string_view name)
        : KModelParams(name) {}

    bool GraphicalSceneMovieParams::isReady() const {
        bool flag = true;
        if (dir == "") {
            kc::warn("Parameter dir is not ready.");
            flag = false;
        }
        if (filename_prefix == "") {
            kc::warn("Parameter filename_prefix is not ready.");
            flag = false;
        }
        return flag;
    }

    std::string GraphicalSceneMovie::getFfmpegCommand() const {
        return std::format(
            "ffmpeg -framerate {} -i {}/{}_%06d.png -c:v libx264 -pix_fmt yuv420p output.mp4",
            1.0 / km::ktimeToSeconds(_period),
            params->dir,
            params->filename_prefix);
    }

    void GraphicalSceneMovie::_registerModel() {
        // Call the parent. This will do the normal registration.
        KModel<GraphicalSceneMovie, GraphicalSceneMovieParams>::_registerModel();

        kc::ks_ptr<Base> dark = shared_from_this();
        auto t = kc::static_pointer_cast<GraphicalSceneMovie>(dark);

        // Also register a first step to run now if we should render the first frame.
        kc::ks_ptr<kd::StatePropagator> sp =
            kc::dynamic_pointer_cast<kd::StatePropagator>(model_manager);
        if (sp and params->render_first_frame) {
            auto te = kd::TimedEvent::create(
                std::format("{}_first_step", name()),
                model_manager->getTime(),
                [t, sp](const km::Ktime &) {
                    if (!std::filesystem::exists(t->params->dir)) {
                        if (not std::filesystem::create_directories(t->params->dir)) {
                            throw std::invalid_argument(
                                std::format("Failed to create directory {}", t->params->dir));
                        }
                    }
                    return t->postModelStep(sp->getTime(), sp->getIntegrator()->getX());
                },
                true);
            sp->registerTimedEvent(te);
        }
    };

    void GraphicalSceneMovie::_unregisterModel() {
        // Call the parent. This will do the normal unregistration.
        KModel<GraphicalSceneMovie, GraphicalSceneMovieParams>::_unregisterModel();

        // Try and unregister this event regardless of the parameter setting.
        // It's possible the parameter has been changed since the last registration.
        kc::ks_ptr<kd::StatePropagator> sp =
            kc::dynamic_pointer_cast<kd::StatePropagator>(model_manager);
        if (sp) {
            sp->unregisterTimedEvent(std::format("{}_first_step", name()), true, true);
        }
    }

    // Destructor included for MacOS builds. Must have a key-function out-of-line to avoid dulpicate
    // symbols.
    GraphicalSceneMovie::~GraphicalSceneMovie(){};

} // namespace Karana::Models