<commit_before>#include "plugin.h"

#include <QApplication>
#include <QDir>
#include <QFileInfo>
#include <QPluginLoader>
#include <QSettings>

namespace OpenCOR {

Plugin::Plugin(const QString &pFileName,
               const PluginInfo::Type &pGuiOrConsoleType,
               const bool &pForceLoading,
               const PluginInfo::Version &pExpectedVersion,
               QSettings *pSettings, const QString &pPluginsDir,
               const QMap<QString, Plugin *> &pMappedPlugins) :
    mName(name(pFileName)),
    // Note: to get the name of the plugin from its file name, we must remove
    //       the plugin prefix part from it...
    mInfo(),
    mInstance(0),
    mStatus(UndefinedStatus)
{
    // Check whether the plugin physically exists

    if (QFileInfo(pFileName).exists()) {
        // The plugin exists, so retrieve its information

        mInfo = info(pFileName);

        // Now, retrieve the plugin's full dependencies (i.e. both its direct
        // and indirect dependencies)

        mInfo.mFullDependencies << requiredPlugins(pPluginsDir, mName);

        // Try to load the plugin, but only if it uses the right plugin version,
        // if it is either a general plugin or one of the type we are happy
        // with, and if it is manageable or is required by another plugin

        if (    (mInfo.version() == pExpectedVersion)
            && (   (mInfo.type() == PluginInfo::General)
                || (mInfo.type() == pGuiOrConsoleType))
            && (   (mInfo.manageable() && load(pSettings, mName))
                || pForceLoading)) {
            // We are dealing with the right kind of plugin, so check that all
            // of its dependencies, if any, are loaded
            // Note: the reason we only do this on non-Windows systems is that
            //       on Windows a shared library's dependencies must be loaded
            //       before the shared library itself can be loaded while on
            //       Linux / Mac OS X, it's possible to load a shared library
            //       even if its dependencies are not loaded, so...

#ifndef Q_WS_WIN
            bool pluginDependenciesLoaded = true;

            mStatusErrors = "";

            foreach (const QString &dependency, mInfo.dependencies()) {
                Plugin *pluginDependency = pMappedPlugins.value(dependency);

                if (   !pluginDependency
                    || (   pluginDependency
                        && (pluginDependency->status() != Loaded))) {
                    // Either the plugin dependency couldn't be found or it
                    // could be found but it isn't loaded, so...

                    pluginDependenciesLoaded = false;

                    mStatus = MissingDependencies;

                    if (!mStatusErrors.isEmpty())
                        mStatusErrors += "\n";

                    mStatusErrors += " - "+dependency;
                }
            }

            if (nbOfStatusErrors() == 1)
                // There is only one error, so remove the leading " - "

                mStatusErrors = mStatusErrors.remove(0, 3);
#endif

            // Check whether all of the plugin's dependencies, if any, were
            // loaded, and if so then try to load the plugin itself

#ifndef Q_WS_WIN
            if (pluginDependenciesLoaded) {
#endif
                // All the plugin's dependencies, if any, were loaded, so try to
                // load the plugin itself

                QPluginLoader pluginLoader(pFileName);

                if (pluginLoader.load()) {
                    // The plugin has been properly loaded, so...

                    mInstance = pluginLoader.instance();

                    mStatus = Loaded;
                } else {
                    // The plugin couldn't be loaded for some reason (surely,
                    // this should never happen...?!), so...

                    mStatus = NotLoaded;
                    mStatusErrors = pluginLoader.errorString();
                }
#ifndef Q_WS_WIN
            }
#endif
        } else if (mInfo.type() == PluginInfo::UndefinedType) {
            // We couldn't retrieve the plugin information which means that we
            // are not dealing with an OpenCOR plugin or that one or several of
            // the plugin's dependencies weren't loaded, so...

#ifdef Q_WS_WIN
            mStatus = NotPluginOrMissingDependencies;
#else
            mStatus = NotPlugin;
#endif
        } else if (mInfo.version() != pExpectedVersion) {
            // We are dealing with a plugin which relies on a different version,
            // so...

            mStatus = IncompatibleVersion;
        } else if (   (mInfo.type() != PluginInfo::General)
                   && (mInfo.type() != pGuiOrConsoleType)) {
            // We are dealing with a plugin which is not of the type we are
            // happy with (i.e. it's a console plugin but we are running the GUI
            // version of OpenCOR, or it's a GUI plugin but we are running the
            // console version of OpenCOR), so...

            mStatus = NotSuitable;
        } else {
            // If none of the above applies then it means we are dealing with a
            // plugin which is either not wanted or not needed, depending on
            // whether it is manageable

            if (mInfo.manageable())
                mStatus = NotWanted;
            else
                mStatus = NotNeeded;
        }
    } else {
        // The plugin doesn't exist, so...

        mStatus = NotFound;
    }
}

QString Plugin::name() const
{
    // Return the plugin's name

    return mName;
}

PluginInfo Plugin::info() const
{
    // Return the plugin's information

    return mInfo;
}

QObject * Plugin::instance() const
{
    // Return the plugin's insance

    return mInstance;
}

Plugin::Status Plugin::status() const
{
    // Return the plugin's status

    return mStatus;
}

QString Plugin::statusErrors() const
{
    // Return the plugin's status errors

    return mStatusErrors;
}

int Plugin::nbOfStatusErrors() const
{
    // Return the number of plugin's status errors

    return mStatusErrors.count("\n")+1;
}

QString Plugin::name(const QString &pFileName)
{
    // Return the plugin's name based on its file name

    return QFileInfo(pFileName).baseName().remove(0, PluginPrefix.length());
    // Note: we must remove the plugin prefix part from the plugin file name...
}

QString Plugin::fileName(const QString &pDir, const QString &pName)
{
    // Return the plugin's file name based on its name

    return pDir+QDir::separator()+PluginPrefix+pName+PluginExtension;
    // Note: we must add the plugin prefix part to the plugin file name...
}

PluginInfo Plugin::info(const QString &pFileName)
{
    // Return the plugin's information

    typedef PluginInfo (*PluginInfoFunc)();

    PluginInfoFunc pluginInfoFunc = (PluginInfoFunc) QLibrary::resolve(pFileName,
                                                                       QString(name(pFileName)+"PluginInfo").toLatin1().constData());

    // Check whether the plugin information function was found

    if (pluginInfoFunc)
        // The plugin information function was found, so we can extract the
        // information we are after

        return pluginInfoFunc();
    else
        // The plugin information couldn't be found, so...

        return PluginInfo();
}

static const QString SettingsLoad = "Load";

bool Plugin::load(QSettings *pSettings, const QString &pName)
{
    // Retrieve the plugin's loading requirement

    pSettings->beginGroup(pName);
        bool res = pSettings->value(SettingsLoad, true).toBool();
    pSettings->endGroup();

    return res;
}

void Plugin::setLoad(QSettings *pSettings, const QString &pName,
                     const bool &pToBeLoaded)
{
    // Keep track of the plugin's loading requirement

    pSettings->beginGroup(pName);
        pSettings->setValue(SettingsLoad, pToBeLoaded);
    pSettings->endGroup();
}

QStringList Plugin::requiredPlugins(const QString &pPluginsDir,
                                    const QString &pName,
                                    const int &pLevel)
{
    // Return the list of plugins required by a given plugin

    QStringList res;

    // Recursively look for the plugins required by the current plugin

    foreach (const QString &plugin,
             Plugin::info(Plugin::fileName(pPluginsDir, pName)).dependencies())
        res << requiredPlugins(pPluginsDir, plugin, pLevel+1);

    // Add the current plugin to the list, but only if it is not the original
    // plugin for which we want to know what its requirements are

    if (pLevel)
        // We are not dealing with the original plugin, so...

        res << pName;
    else
        // We are done, so remove any duplicate which might be present

        res.removeDuplicates();

    return res;
}

}
<commit_msg>I thought I was done with the loading of plugins, but clearly not. Now, with our new QJson plugin, something wasn't working on Windows. Had I not reverted some code at some point (see commit e1b91e5e728bf5207ddc5c993f7b7bec863598bd and then commit f2393841eec04a6146e4df12212fa5b48cee0920, commit 289e75c49ba9bb7e1845a29b7ed59b15a6f34733 and commit 54e27a4497d9678d26d4a65257c5a350a999d761), it would have worked, but... Anyway, now, everything's fine on all three platforms... with our current set of plugins...<commit_after>#include "plugin.h"

#include <QApplication>
#include <QDir>
#include <QFileInfo>
#include <QPluginLoader>
#include <QSettings>

namespace OpenCOR {

Plugin::Plugin(const QString &pFileName,
               const PluginInfo::Type &pGuiOrConsoleType,
               const bool &pForceLoading,
               const PluginInfo::Version &pExpectedVersion,
               QSettings *pSettings, const QString &pPluginsDir,
               const QMap<QString, Plugin *> &pMappedPlugins) :
    mName(name(pFileName)),
    // Note: to get the name of the plugin from its file name, we must remove
    //       the plugin prefix part from it...
    mInfo(),
    mInstance(0),
    mStatus(UndefinedStatus)
{
    // Check whether the plugin physically exists

    if (QFileInfo(pFileName).exists()) {
        // The plugin exists, so retrieve its information

        mInfo = info(pFileName);

        // Now, retrieve the plugin's full dependencies (i.e. both its direct
        // and indirect dependencies)

        mInfo.mFullDependencies << requiredPlugins(pPluginsDir, mName);

        // Try to load the plugin, but only if it uses the right plugin version,
        // if it is either a general plugin or one of the type we are happy
        // with, and if it is manageable or is required by another plugin

        if (    (mInfo.version() == pExpectedVersion)
            && (   (mInfo.type() == PluginInfo::General)
                || (mInfo.type() == pGuiOrConsoleType))
            && (   (mInfo.manageable() && load(pSettings, mName))
                || pForceLoading)) {
            // We are dealing with the right kind of plugin, so check that all
            // of its dependencies, if any, are loaded
            // Note: the reason we only do this on non-Windows systems is that
            //       on Windows a shared library's dependencies must be loaded
            //       before the shared library itself can be loaded while on
            //       Linux / Mac OS X, it's possible to load a shared library
            //       even if its dependencies are not loaded, so...

#ifndef Q_WS_WIN
            bool pluginDependenciesLoaded = true;

            mStatusErrors = "";

            foreach (const QString &dependency, mInfo.dependencies()) {
                Plugin *pluginDependency = pMappedPlugins.value(dependency);

                if (   !pluginDependency
                    || (   pluginDependency
                        && (pluginDependency->status() != Loaded))) {
                    // Either the plugin dependency couldn't be found or it
                    // could be found but it isn't loaded, so...

                    pluginDependenciesLoaded = false;

                    mStatus = MissingDependencies;

                    if (!mStatusErrors.isEmpty())
                        mStatusErrors += "\n";

                    mStatusErrors += " - "+dependency;
                }
            }

            if (nbOfStatusErrors() == 1)
                // There is only one error, so remove the leading " - "

                mStatusErrors = mStatusErrors.remove(0, 3);
#endif

            // Check whether all of the plugin's dependencies, if any, were
            // loaded, and if so then try to load the plugin itself

#ifndef Q_WS_WIN
            if (pluginDependenciesLoaded) {
#endif
                // All the plugin's dependencies, if any, were loaded, so try to
                // load the plugin itself

                QPluginLoader pluginLoader(pFileName);

                if (pluginLoader.load()) {
                    // The plugin has been properly loaded, so...

                    mInstance = pluginLoader.instance();

                    mStatus = Loaded;
                } else {
                    // The plugin couldn't be loaded for some reason (surely,
                    // this should never happen...?!), so...

                    mStatus = NotLoaded;
                    mStatusErrors = pluginLoader.errorString();
                }
#ifndef Q_WS_WIN
            }
#endif
        } else if (mInfo.type() == PluginInfo::UndefinedType) {
            // We couldn't retrieve the plugin information which means that we
            // are not dealing with an OpenCOR plugin or that one or several of
            // the plugin's dependencies weren't loaded, so...

#ifdef Q_WS_WIN
            mStatus = NotPluginOrMissingDependencies;
#else
            mStatus = NotPlugin;
#endif
        } else if (mInfo.version() != pExpectedVersion) {
            // We are dealing with a plugin which relies on a different version,
            // so...

            mStatus = IncompatibleVersion;
        } else if (   (mInfo.type() != PluginInfo::General)
                   && (mInfo.type() != pGuiOrConsoleType)) {
            // We are dealing with a plugin which is not of the type we are
            // happy with (i.e. it's a console plugin but we are running the GUI
            // version of OpenCOR, or it's a GUI plugin but we are running the
            // console version of OpenCOR), so...

            mStatus = NotSuitable;
        } else {
            // If none of the above applies then it means we are dealing with a
            // plugin which is either not wanted or not needed, depending on
            // whether it is manageable

            if (mInfo.manageable())
                mStatus = NotWanted;
            else
                mStatus = NotNeeded;
        }
    } else {
        // The plugin doesn't exist, so...

        mStatus = NotFound;
    }
}

QString Plugin::name() const
{
    // Return the plugin's name

    return mName;
}

PluginInfo Plugin::info() const
{
    // Return the plugin's information

    return mInfo;
}

QObject * Plugin::instance() const
{
    // Return the plugin's insance

    return mInstance;
}

Plugin::Status Plugin::status() const
{
    // Return the plugin's status

    return mStatus;
}

QString Plugin::statusErrors() const
{
    // Return the plugin's status errors

    return mStatusErrors;
}

int Plugin::nbOfStatusErrors() const
{
    // Return the number of plugin's status errors

    return mStatusErrors.count("\n")+1;
}

QString Plugin::name(const QString &pFileName)
{
    // Return the plugin's name based on its file name

    return QFileInfo(pFileName).baseName().remove(0, PluginPrefix.length());
    // Note: we must remove the plugin prefix part from the plugin file name...
}

QString Plugin::fileName(const QString &pDir, const QString &pName)
{
    // Return the plugin's file name based on its name

    return pDir+QDir::separator()+PluginPrefix+pName+PluginExtension;
    // Note: we must add the plugin prefix part to the plugin file name...
}

PluginInfo Plugin::info(const QString &pFileName)
{
    // Return the plugin's information
    // Note: to retrieve a plugin's information, we must, on both Windows and
    //       Linux, be able to load any plugin on which a plugin depends. On
    //       Windows, we do this (by keeping track of the current directory
    //       and) by going to the directory where our plugins are located.
    //       However, this approach doesn't work on Linux, so instead we rely
    //       on LD_LIBRARY_PATH to get the result we are after (in fact, it is
    //       probably a better approach!), so...

    typedef PluginInfo (*PluginInfoFunc)();

#ifdef Q_WS_WIN
    QString origPath = QDir::currentPath();

    QDir::setCurrent(QFileInfo(pFileName).absolutePath());
#endif

    PluginInfoFunc pluginInfoFunc = (PluginInfoFunc) QLibrary::resolve(pFileName,
                                                                       QString(name(pFileName)+"PluginInfo").toLatin1().constData());

#ifdef Q_WS_WIN
    QDir::setCurrent(origPath);
#endif

    // Check whether the plugin information function was found

    if (pluginInfoFunc)
        // The plugin information function was found, so we can extract the
        // information we are after

        return pluginInfoFunc();
    else
        // The plugin information couldn't be found, so...

        return PluginInfo();
}

static const QString SettingsLoad = "Load";

bool Plugin::load(QSettings *pSettings, const QString &pName)
{
    // Retrieve the plugin's loading requirement

    pSettings->beginGroup(pName);
        bool res = pSettings->value(SettingsLoad, true).toBool();
    pSettings->endGroup();

    return res;
}

void Plugin::setLoad(QSettings *pSettings, const QString &pName,
                     const bool &pToBeLoaded)
{
    // Keep track of the plugin's loading requirement

    pSettings->beginGroup(pName);
        pSettings->setValue(SettingsLoad, pToBeLoaded);
    pSettings->endGroup();
}

QStringList Plugin::requiredPlugins(const QString &pPluginsDir,
                                    const QString &pName,
                                    const int &pLevel)
{
    // Return the list of plugins required by a given plugin

    QStringList res;

    // Recursively look for the plugins required by the current plugin

    foreach (const QString &plugin,
             Plugin::info(Plugin::fileName(pPluginsDir, pName)).dependencies())
        res << requiredPlugins(pPluginsDir, plugin, pLevel+1);

    // Add the current plugin to the list, but only if it is not the original
    // plugin for which we want to know what its requirements are

    if (pLevel)
        // We are not dealing with the original plugin, so...

        res << pName;
    else
        // We are done, so remove any duplicate which might be present

        res.removeDuplicates();

    return res;
}

}
<|endoftext|>


<commit_before>#ifndef IMPL_AX_SYSTEM_HPP
#define IMPL_AX_SYSTEM_HPP

#include <cstddef>
#include <stdexcept>
#include <memory>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <queue>

#include "prelude.hpp"
#include "string.hpp"
#include "option.hpp"
#include "address.hpp"
#include "addressable.hpp"
#include "castable.hpp"
#include "eventable.hpp"

// NOTE: included in this file is an optimized entity-component-system prototype, an interesting seed for a fast C++
// game engine.

namespace ax
{
    class entity;

    struct component
    {
        CONSTRAINT(component);
        bool active;
    };

    struct entity_t : public ax::component
    {
        std::unordered_map<ax::name_t, ax::component*> components;
    };

    struct transform : public ax::component
    {
        // NOTE: of course, we'd instead use math library types here.
        float x_pos;
        float y_pos;
        float x_size;
        float y_size;
        float rotation;
    };

    class system : public castable
    {
    public:

        CONSTRAINT(system);
        virtual ax::component& add_component(const ax::address& address) = 0;
        virtual bool remove_component(const ax::address& address) = 0;
        virtual ax::component* try_get_component(const ax::address& address) = 0;

    protected:

        ENABLE_CAST(ax::system, ax::castable);
    };

    template<typename T>
    class system_t : public ax::system
    {
    public:

        CONSTRAINT(system_t);

        system_t(std::size_t capacity = 128)
        {
            CONSTRAIN(T, ax::component);
            components.reserve(capacity);
            component_map.reserve(capacity);
        }

        T& add_component(const ax::address& address, const T& component)
        {
            if (free_list.empty())
            {
                components.push_back(component);
                VAR& component_found = components.back();
                component_found.active = false;
                return component_found;
            }
            else
            {
                VAL index = free_list.front();
                free_list.pop();
                component_map.insert_or_assign(address, index);
                VAR& component_found = components.at(index);
                component_found = component;
                component_found.active = true; // ensure active after assignment
                return component_found;
            }
        }

        T& add_component(const ax::address& address) override
        {
            return ax::system_t<T>::add_component(address, T());
        }

        bool remove_component(const ax::address& address) override
        {
            VAL index_iter = component_map.find(address);
            if (index_iter != component_map.end())
            {
                VAL index = index_iter->second;
                if (index == components.size() - 1)
                {
                    components.erase(components.end() - 1);
                }
                else
                {
                    free_list.push(index);
                    VAR& component = components.at(index);
                    component.active = false;
                }
                component_map.erase(address);
            }
        }

        T* try_get_component(const ax::address& address) override
        {
            VAL index_iter = component_map.find(address);
            if (index_iter != component_map.end())
            {
                VAL index = index_iter->second;
                return &components.at(index);
            }
            return nullptr;
        }

    protected:

        ENABLE_CAST(ax::system_t<T>, ax::system);

    private:

        std::vector<T> components;
        std::unordered_map<ax::address, std::size_t> component_map;
        std::queue<std::size_t> free_list;
    };

    class world : public ax::eventable<world>
    {
    public:

        template<typename T>
        T* try_add_component(const ax::name_t& system_name, const ax::address& address, const T& component = T())
        {
            VAL& entities_iter = systems.find("entities");
            if (entities_iter != systems.end())
            {
                VAL& entities = ax::cast<ax::system_t<ax::entity_t>>(entities_iter->second);
                VAR* entity_opt = entities->try_get_component(address);
                if (entity_opt)
                {
                    VAL& system_iter = systems.find(system_name);
                    if (system_iter != systems.end())
                    {
                        VAL& system = ax::cast<ax::system_t<T>>(system_iter->second);
                        VAR& result = system->add_component(address, component);
                        entity_opt->components.insert_or_assign(system_name, &result);
                        return &result;
                    }
                }
            }
            return nullptr;
        }

        ax::transform* try_get_transform(const ax::address& address);
        ax::entity_t* try_get_entity(const ax::address& address);
        bool entity_exists(const ax::address& address);
        ax::component* try_add_component(const ax::name_t& system_name, const ax::address& address);
        bool try_remove_component(const ax::name_t& system_name, const ax::address& address);
        std::shared_ptr<ax::entity> create_entity(const ax::address& address);
        bool destroy_entity(const ax::address& address);

    private:

        ax::entity_t* try_add_entity(const ax::address& address);
        bool try_remove_entity(const ax::address& address);
        std::unordered_map<ax::name_t, std::shared_ptr<ax::system>> systems;
    };

    class entity : public addressable
    {
    public:

        entity(const ax::entity& entity) :
            entity(entity.get_address(), entity.world)
        { };

        entity(const ax::address& address, ax::world& world) :
            ax::addressable(address),
            world(world)
        { }

        template<typename T>
        const T* try_get_component(const ax::name_t& name) const { return world.try_get_component<T>(name, get_address()); }

        template<typename T>
        T* try_get_component(const ax::name_t& name) { return world.try_get_component<T>(name, get_address()); }

        template<typename T>
        const T& get_component(const ax::name_t& name) const { return *try_get_component<T>(name); }

        template<typename T>
        T& get_component(const ax::name_t& name) { return *try_get_component<T>(name); }

        ax::transform& get_transform() const { return *world.try_get_transform(get_address()); }
        ax::transform& set_transform(const ax::transform& transform) { return get_transform() = transform; }
        float get_x_pos() const { return get_transform().x_pos; }
        float set_x_pos(float value) { return get_transform().x_pos = value; }
        float get_y_pos() const { return get_transform().y_pos; }
        float set_y_pos(float value) { return get_transform().y_pos = value; }
        float get_rotation() const { return get_transform().rotation; }
        float set_rotation(float value) { return get_transform().rotation = value; }
        bool exists() const { return world.entity_exists(get_address()); }

    private:

        ax::world& world;
    };
}

#endif
<commit_msg>Minor clean-up.<commit_after>#ifndef IMPL_AX_SYSTEM_HPP
#define IMPL_AX_SYSTEM_HPP

#include <cstddef>
#include <stdexcept>
#include <memory>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <queue>

#include "prelude.hpp"
#include "string.hpp"
#include "option.hpp"
#include "address.hpp"
#include "addressable.hpp"
#include "castable.hpp"
#include "eventable.hpp"

// NOTE: included in this file is an optimized entity-component-system prototype, an interesting seed for a fast C++
// game engine.

namespace ax
{
    class entity;

    struct component
    {
        CONSTRAINT(component);
        bool active;
    };

    struct entity_t : public ax::component
    {
        std::unordered_map<ax::name_t, ax::component*> components;
    };

    struct transform : public ax::component
    {
        // NOTE: of course, we'd instead use math library types here.
        float x_pos;
        float y_pos;
        float x_size;
        float y_size;
        float rotation;
    };

    class system : public ax::castable
    {
    public:

        CONSTRAINT(system);
        virtual ax::component& add_component(const ax::address& address) = 0;
        virtual bool remove_component(const ax::address& address) = 0;
        virtual ax::component* try_get_component(const ax::address& address) = 0;

    protected:

        ENABLE_CAST(ax::system, ax::castable);
    };

    template<typename T>
    class system_t : public ax::system
    {
    public:

        CONSTRAINT(system_t);

        system_t(std::size_t capacity = 128)
        {
            CONSTRAIN(T, ax::component);
            components.reserve(capacity);
            component_map.reserve(capacity);
        }

        T& add_component(const ax::address& address, const T& component)
        {
            if (free_list.empty())
            {
                components.push_back(component);
                VAR& component_found = components.back();
                component_found.active = false;
                return component_found;
            }
            else
            {
                VAL index = free_list.front();
                free_list.pop();
                component_map.insert_or_assign(address, index);
                VAR& component_found = components.at(index);
                component_found = component;
                component_found.active = true; // ensure active after assignment
                return component_found;
            }
        }

        T& add_component(const ax::address& address) override
        {
            return ax::system_t<T>::add_component(address, T());
        }

        bool remove_component(const ax::address& address) override
        {
            VAL index_iter = component_map.find(address);
            if (index_iter != component_map.end())
            {
                VAL index = index_iter->second;
                if (index == components.size() - 1)
                {
                    components.erase(components.end() - 1);
                }
                else
                {
                    free_list.push(index);
                    VAR& component = components.at(index);
                    component.active = false;
                }
                component_map.erase(address);
            }
        }

        T* try_get_component(const ax::address& address) override
        {
            VAL index_iter = component_map.find(address);
            if (index_iter != component_map.end())
            {
                VAL index = index_iter->second;
                return &components.at(index);
            }
            return nullptr;
        }

    protected:

        ENABLE_CAST(ax::system_t<T>, ax::system);

    private:

        std::vector<T> components;
        std::unordered_map<ax::address, std::size_t> component_map;
        std::queue<std::size_t> free_list;
    };

    class world : public ax::eventable<world>
    {
    public:

        template<typename T>
        T* try_add_component(const ax::name_t& system_name, const ax::address& address, const T& component = T())
        {
            VAL& entities_iter = systems.find("entities");
            if (entities_iter != systems.end())
            {
                VAL& entities = ax::cast<ax::system_t<ax::entity_t>>(entities_iter->second);
                VAR* entity_opt = entities->try_get_component(address);
                if (entity_opt)
                {
                    VAL& system_iter = systems.find(system_name);
                    if (system_iter != systems.end())
                    {
                        VAL& system = ax::cast<ax::system_t<T>>(system_iter->second);
                        VAR& result = system->add_component(address, component);
                        entity_opt->components.insert_or_assign(system_name, &result);
                        return &result;
                    }
                }
            }
            return nullptr;
        }

        ax::transform* try_get_transform(const ax::address& address);
        ax::entity_t* try_get_entity(const ax::address& address);
        bool entity_exists(const ax::address& address);
        ax::component* try_add_component(const ax::name_t& system_name, const ax::address& address);
        bool try_remove_component(const ax::name_t& system_name, const ax::address& address);
        std::shared_ptr<ax::entity> create_entity(const ax::address& address);
        bool destroy_entity(const ax::address& address);

    private:

        ax::entity_t* try_add_entity(const ax::address& address);
        bool try_remove_entity(const ax::address& address);
        std::unordered_map<ax::name_t, std::shared_ptr<ax::system>> systems;
    };

    class entity : public addressable
    {
    public:

        entity(const ax::entity& entity) :
            entity(entity.get_address(), entity.world)
        { };

        entity(const ax::address& address, ax::world& world) :
            ax::addressable(address),
            world(world)
        { }

        template<typename T>
        const T* try_get_component(const ax::name_t& name) const { return world.try_get_component<T>(name, get_address()); }

        template<typename T>
        T* try_get_component(const ax::name_t& name) { return world.try_get_component<T>(name, get_address()); }

        template<typename T>
        const T& get_component(const ax::name_t& name) const { return *try_get_component<T>(name); }

        template<typename T>
        T& get_component(const ax::name_t& name) { return *try_get_component<T>(name); }

        ax::transform& get_transform() const { return *world.try_get_transform(get_address()); }
        ax::transform& set_transform(const ax::transform& transform) { return get_transform() = transform; }
        float get_x_pos() const { return get_transform().x_pos; }
        float set_x_pos(float value) { return get_transform().x_pos = value; }
        float get_y_pos() const { return get_transform().y_pos; }
        float set_y_pos(float value) { return get_transform().y_pos = value; }
        float get_rotation() const { return get_transform().rotation; }
        float set_rotation(float value) { return get_transform().rotation = value; }
        bool exists() const { return world.entity_exists(get_address()); }

    private:

        ax::world& world;
    };
}

#endif
<|endoftext|>


<commit_before>//
// Created by xetql on 23.02.18.
//

#ifndef LIBLJ_MLPACTIONVALUEAPPROXIMATOR_HPP
#define LIBLJ_MLPACTIONVALUEAPPROXIMATOR_HPP
#include <mlpack/core.hpp>

#include <mlpack/core/optimizers/rmsprop/rmsprop.hpp>
#include <mlpack/core/optimizers/sgd/update_policies/vanilla_update.hpp>
#include <mlpack/methods/ann/layer/layer.hpp>
#include <mlpack/methods/ann/ffn.hpp>

#include <armadillo>

#include "FunctionApproximator.hpp"

namespace librl { namespace approximator {

template<class ModelType, class OptimizerType = mlpack::optimization::RMSProp>
class MLPActionValueApproximator : public ActionValueApproximator<arma::vec, int> {
    ModelType* model;
    OptimizerType opt;
    int max_actions;
public:

    MLPActionValueApproximator(
            ModelType* model,
            OptimizerType& optimizer,
            int max_actions,
            double alpha) :
            model(model),
            opt(optimizer),
            max_actions(max_actions),
            ActionValueApproximator<arma::vec, int>(alpha) {}

    void reset() {};

    /**
     * There is no learning parameter when using neural network as a function
     * approximation
     * @param alpha
     */
    void set_learning_parameter(double alpha) {}

    /**
     * Get argmax_a Q(s,a), it may contains several indices
     * Query NN
     * @param state
     * @return argmax_a Q(s,a)
     */
    int argmax(arma::vec state, std::vector<int> available_actions = std::vector<int>() ) const {

        arma::vec responses = arma::zeros(max_actions);
        const_cast<ModelType*>(model)->Predict(state, responses);
        return arma::index_max(responses);

/*
        fann_type in[net.get_num_input()];
        fann_type *run_out;

        for (size_t i = 0; i < net.get_num_input(); ++i) in[i] = state_as_vect[i];

        //predict value for state
        run_out = net.run(in);

        //find argmax in prediction value which is available in that state
*/
        return 1;
    }

    /**
     * Setter of the Q function
     * @param state
     * @param action
     * @param value
     */
    void Q(arma::vec state, int action, double value) {
        arma::vec responses = arma::zeros(max_actions);
        responses(action) = value;
        model->Train(state, responses, opt);
/*
        //verify sizes of input and topology
        assert(state_as_vect.size() == net.get_num_input());

        fann_type in[net.get_num_input()];
        fann_type *run_out, desired_out[net.get_num_output()];
        for (size_t i = 0; i < net.get_num_input(); ++i) {
            in[i] = state_as_vect[i];
        }

        //predict value for state
        run_out = net.run(in);

        for (size_t i = 0; i < net.get_num_output(); ++i) {
            if (i == action) desired_out[action] = value;
            else desired_out[i] = run_out[i];
        }

        net.train(in, desired_out);

        //std::cout <<"MSE " <<net.get_MSE() << std::endl;
        */
    }

    /**
     * @brief Get max Q(s,a)
     */
    double max(arma::vec state) const {
        arma::vec responses = arma::zeros(max_actions);
        const_cast<ModelType*>(model)->Predict(state, responses);
        return arma::max(responses);
        /*
        //verify sizes of input and topology
        assert(state_as_vect.size() == net.get_num_input());

        fann_type in[net.get_num_input()];
        fann_type *run_out;
        for (size_t i = 0; i < net.get_num_input(); ++i) in[i] = state_as_vect[i];
        //predict value for state
        run_out = net.run(in);
        //find argmax in prediction value which is available in that state
        return filtered_max(run_out, state, this->available_actions);*/
        return 0.0;
    }
    /**
    * @brief Get Q(s,a)
    */
    double Q(arma::vec state, int action) const {
        arma::vec responses = arma::zeros(max_actions);
        const_cast<ModelType*>(model)->Predict(state, responses);
        return responses(action);
        /*
        //verify sizes of input and topology
        assert(state_as_vect.size() == net.get_num_input());
        fann_type in[net.get_num_input()];
        fann_type *out;
        for (size_t i = 0; i < net.get_num_input(); ++i) in[i] = state_as_vect[i];
        //predict value for state
        out = net.run(in);
        //find argmax in prediction value which is available in that state
        return out[action];*/
        return 0.0;
    }
};

}}
#endif //LIBLJ_MLPACTIONVALUEAPPROXIMATOR_HPP
<commit_msg>added MLP function approximator<commit_after>//
// Created by xetql on 23.02.18.
//

#ifndef LIBLJ_MLPACTIONVALUEAPPROXIMATOR_HPP
#define LIBLJ_MLPACTIONVALUEAPPROXIMATOR_HPP
#include <mlpack/core.hpp>

#include <mlpack/core/optimizers/rmsprop/rmsprop.hpp>
#include <mlpack/core/optimizers/sgd/update_policies/vanilla_update.hpp>
#include <mlpack/methods/ann/layer/layer.hpp>
#include <mlpack/methods/ann/ffn.hpp>

#include <armadillo>

#include "FunctionApproximator.hpp"

namespace librl { namespace approximator {

template<class ModelType, class OptimizerType = mlpack::optimization::RMSProp>
class MLPActionValueApproximator : public ActionValueApproximator<arma::vec, int> {
    ModelType* model;
    OptimizerType opt;
    int max_actions;
public:

    MLPActionValueApproximator(
            ModelType* model,
            OptimizerType& optimizer,
            int max_actions,
            double alpha) :
            model(model),
            opt(optimizer),
            max_actions(max_actions),
            ActionValueApproximator<arma::vec, int>(alpha) {}

    void reset() {model.ResetParameters();};

    /**
     * There is no learning parameter when using neural network as a function
     * approximation
     * @param alpha
     */
    void set_learning_parameter(double alpha) {}

    /**
     * Get argmax_a Q(s,a), it may contains several indices
     * Query NN
     * @param state
     * @return argmax_a Q(s,a)
     */
    int argmax(arma::vec state, std::vector<int> available_actions = std::vector<int>() ) const {
        arma::vec responses = arma::zeros(max_actions);
        const_cast<ModelType*>(model)->Predict(state, responses);
        return arma::index_max(responses);
    }

    /**
     * Setter of the Q function
     * @param state
     * @param action
     * @param value
     */
    void Q(arma::vec state, int action, double value) {
        arma::vec responses = arma::zeros(max_actions);
        responses(action) = value;
        model->Train(state, responses, opt);
    }

    /**
     * @brief Get max Q(s,a)
     */
    double max(arma::vec state) const {
        arma::vec responses = arma::zeros(max_actions);
        const_cast<ModelType*>(model)->Predict(state, responses);//I thank a lot mlpack devs. for the non cost function.
        return arma::max(responses);
    }
    /**
    * @brief Get Q(s,a)
    */
    double Q(arma::vec state, int action) const {
        arma::vec responses = arma::zeros(max_actions);
        const_cast<ModelType*>(model)->Predict(state, responses);//I thank a lot mlpack devs. for the non cost function.
        return responses(action);
    }
};

}}
#endif //LIBLJ_MLPACTIONVALUEAPPROXIMATOR_HPP
<|endoftext|>


<commit_before>#ifndef IMPL_AX_VECTOR_HPP
#define IMPL_AX_VECTOR_HPP

#include <cstddef>
#include <algorithm>
#include <vector>
#include <memory>

#include "prelude.hpp"

namespace ax
{
    // A vector with small size optimization.
    template<typename T, typename A = std::allocator<T>, std::size_t N = 4>
    class vector
    {
    public:

        CONSTRAINT(vector);
        using value_type = T;
        using size_type = std::size_t;
        using iterator = T*;
        using const_iterator = const T*;
        using allocator = A; // TODO: see if this is a meaningful trait name
        template<typename T2, typename A2, std::size_t N2>
        using reify = ax::vector<T2, A2, N2>;

        vector() : vector({ }) { }

        vector(std::initializer_list<T> il) :
            vector(il.begin(), il.end()) { }

        vector(const T* begin, const T* end)
        {
            VAR size = static_cast<std::size_t>(end - begin);
            if (size > N)
            {
                small_end = small_begin - 1;
                big_vector.insert(big_vector.begin(), begin, end);
            }
            else
            {
                small_end = small_begin;
                for (VAR i = 0_z; i < N && begin != end; ++i)
                {
                    *small_end = *begin;
                    ++small_end;
                    ++begin;
                }
            }
        }

        vector(const ax::vector<T, A, N>& that)
        {
            if (that.small_end < that.small_begin)
            {
                small_end = small_begin - 1;
                big_vector = that.big_vector;
            }
            else
            {
                small_end = small_begin;
                VAL size = that.size();
                for (VAR i = 0_z; i < size; ++i)
                {
                    *small_end = that.small_begin[i];
                    ++small_end;
                }
            }
        }

        vector(ax::vector<T, A, N>&& that)
        {
            if (that.small_end < that.small_begin)
            {
                small_end = small_begin - 1;
                big_vector = std::move(that.big_vector);
            }
            else
            {
                small_end = small_begin;
                VAL size = that.size();
                for (VAR i = 0_z; i < size; ++i)
                {
                    *small_end = that.small_begin[i];
                    ++small_end;
                }
            }
        }

        ~vector() = default;

        ax::vector<T, A, N>& operator=(const ax::vector<T, A, N>& that)
        {
            big_vector = that.big_vector;
            VAL size = that.size();
            for (VAR i = 0_z; i < size; ++i)
            {
                *small_end = that.small_begin[i];
                ++small_end;
            }
            return *this;
        }

        ax::vector<T, A, N>& operator=(ax::vector<T, A, N>&& that)
        {
            big_vector = std::move(that.big_vector);
            VAL size = that.size();
            for (VAR i = 0_z; i < size; ++i)
            {
                *small_end = that.small_begin[i];
                ++small_end;
            }
            return *this;
        }

        bool operator==(const ax::vector<T, A, N>& that) const
        {
            VAL size = this->size();
            if (size > N) return big_vector == that.big_vector;
            VAR result = true;
            for (VAR i = 0_z; result && i < size; ++i)
                result = small_begin[i] == that.small_begin[i];
            return result;
        }

        bool operator!=(const ax::vector<T, A, N>& that) const
        {
            return !(*this == that);
        }

        T* begin() { return big() ? big_vector.data() : small_begin; }
        T* end() { return big() ? big_vector.data() + big_vector.size() : small_end; }
        const T* cbegin() const { return const_cast<ax::vector<T, A, N>*>(this)->begin(); }
        const T* cend() const { return const_cast<ax::vector<T, A, N>*>(this)->end(); }
        const T* begin() const { return cbegin(); }
        const T* end() const { return cend(); }

        void push_back(const T& item)
        {
            if (big())
            {
                if (size() == N) big_vector.insert(big_vector.begin(), small_begin, small_end);
                big_vector.push_back(item);
                small_end = small_begin - 1;
            }
            else
            {
                new (small_end) T(item);
                ++small_end;
            }
        }

        void pop_back()
        {
            if (big()) return big_vector.pop_back();
            if (size() != 0)
            {
                (small_end - 1)->~T();
                --small_end;
                return;
            }
            throw std::range_error("Cannot pop_back empty ax::vector.");
        }

        T* erase(const T* iter)
        {
            if (big())
            {
                VAL pos = iter - big_vector.data();
                VAL& iter = big_vector.begin() + pos;
                return &*big_vector.erase(iter);
            }
            small_end = std::remove(small_begin, small_end, *iter);
            return small_end;
        }

        T& at(std::size_t pos)
        {
            if (big()) return big_vector.at(pos);
            if (pos < size()) return *(small_begin + pos);
            throw std::out_of_range("ax::vector indexed out of range.");
        }

        const T& at(std::size_t pos) const
        {
            return const_cast<ax::vector<T, A, N>*>(this)->at(pos);
        }

        void assign(std::size_t pos, const T& item)
        {
            if (big()) return big_vector.assign(pos, item);
            if (pos < size()) return small_begin + pos;
            throw std::out_of_range("ax::vector indexed out of range.");
        }

        T& front()
        {
            if (big()) return big_vector.front();
            if (size() > 0) return *small_begin;
            throw std::out_of_range("ax::vector indexed out of range.");
        }

        T& back()
        {
            if (big()) return big_vector.back();
            if (size() > 0) return *(small_end - 1);
            throw std::out_of_range("ax::vector indexed out of range.");
        }

        std::size_t capacity() const
        {
            if (big()) return big_vector.capacity();
            return N;
        }

        void clear()
        {
            if (big()) big_vector.clear();
            else small_end = small_begin;
        }

        T* data()
        {
            if (big()) return big_vector.data();
            return small_begin;
        }

        const T* data() const
        {
            return const_cast<ax::vector<T, A, N>*>(this)->data();
        }

        std::size_t size() const
        {
            return end() - begin();
        }

        inline bool big() const
        {
            return small_end < small_begin;
        }

    private:

        T small_begin[N];
        T* small_end;
        std::vector<T, A> big_vector;
    };
}

#endif
<commit_msg>Intermediate commit.<commit_after>#ifndef IMPL_AX_VECTOR_HPP
#define IMPL_AX_VECTOR_HPP

#include <cstddef>
#include <algorithm>
#include <vector>
#include <memory>

#include "prelude.hpp"

namespace ax
{
    // A vector with small size optimization.
    template<typename T, typename A = std::allocator<T>, std::size_t N = 4>
    class vector
    {
    public:

        CONSTRAINT(vector);
        using value_type = T;
        using size_type = std::size_t;
        using iterator = T*;
        using const_iterator = const T*;
        using allocator = A; // TODO: see if this is a meaningful trait name
        template<typename T2, typename A2, std::size_t N2>
        using reify = ax::vector<T2, A2, N2>;

        vector() : vector({ }) { }

        vector(std::initializer_list<T> il) :
            vector(il.begin(), il.end()) { }

        vector(const T* begin, const T* end)
        {
            VAR size = static_cast<std::size_t>(end - begin);
            if (size > N)
            {
                small_end = small_begin - 1;
                big_vector.insert(big_vector.begin(), begin, end);
            }
            else
            {
                small_end = small_begin;
                for (VAR i = 0_z; i < N && begin != end; ++i)
                {
                    *small_end = *begin;
                    ++small_end;
                    ++begin;
                }
            }
        }

        vector(const ax::vector<T, A, N>& that)
        {
            if (that.small_end < that.small_begin)
            {
                small_end = small_begin - 1;
                big_vector = that.big_vector;
            }
            else
            {
                small_end = small_begin;
                VAL size = that.size();
                for (VAR i = 0_z; i < size; ++i)
                {
                    *small_end = that.small_begin[i];
                    ++small_end;
                }
            }
        }

        vector(ax::vector<T, A, N>&& that)
        {
            if (that.small_end < that.small_begin)
            {
                small_end = small_begin - 1;
                big_vector = std::move(that.big_vector);
            }
            else
            {
                small_end = small_begin;
                VAL size = that.size();
                for (VAR i = 0_z; i < size; ++i)
                {
                    *small_end = that.small_begin[i];
                    ++small_end;
                }
            }
        }

        ~vector() = default;

        ax::vector<T, A, N>& operator=(const ax::vector<T, A, N>& that)
        {
            big_vector = that.big_vector;
            VAL size = that.size();
            for (VAR i = 0_z; i < size; ++i)
            {
                *small_end = that.small_begin[i];
                ++small_end;
            }
            return *this;
        }

        ax::vector<T, A, N>& operator=(ax::vector<T, A, N>&& that)
        {
            big_vector = std::move(that.big_vector);
            VAL size = that.size();
            for (VAR i = 0_z; i < size; ++i)
            {
                *small_end = that.small_begin[i];
                ++small_end;
            }
            return *this;
        }

        bool operator==(const ax::vector<T, A, N>& that) const
        {
            VAL size = this->size();
            if (size > N) return big_vector == that.big_vector;
            VAR result = true;
            for (VAR i = 0_z; result && i < size; ++i)
                result = small_begin[i] == that.small_begin[i];
            return result;
        }

        bool operator!=(const ax::vector<T, A, N>& that) const
        {
            return !(*this == that);
        }

        T* begin() { return big() ? big_vector.data() : small_begin; }
        T* end() { return big() ? big_vector.data() + big_vector.size() : small_end; }
        const T* cbegin() const { return const_cast<ax::vector<T, A, N>*>(this)->begin(); }
        const T* cend() const { return const_cast<ax::vector<T, A, N>*>(this)->end(); }
        const T* begin() const { return cbegin(); }
        const T* end() const { return cend(); }

        void push_back(const T& item)
        {
            if (big())
            {
                big_vector.push_back(item);
            }
            else if (size() == N)
            {
                big_vector.insert(big_vector.begin(), small_begin, small_end);
                big_vector.push_back(item);
                small_end = small_begin - 1;
            }
            else
            {
                new (small_end) T(item);
                ++small_end;
            }
        }

        void pop_back()
        {
            if (big()) return big_vector.pop_back();
            if (size() != 0)
            {
                (small_end - 1)->~T();
                --small_end;
                return;
            }
            throw std::range_error("Cannot pop_back empty ax::vector.");
        }

        T* erase(const T* iter)
        {
            if (big())
            {
                VAL pos = iter - big_vector.data();
                VAL& iter = big_vector.begin() + pos;
                return &*big_vector.erase(iter);
            }
            small_end = std::remove(small_begin, small_end, *iter);
            return small_end;
        }

        T& at(std::size_t pos)
        {
            if (big()) return big_vector.at(pos);
            if (pos < size()) return *(small_begin + pos);
            throw std::out_of_range("ax::vector indexed out of range.");
        }

        const T& at(std::size_t pos) const
        {
            return const_cast<ax::vector<T, A, N>*>(this)->at(pos);
        }

        void assign(std::size_t pos, const T& item)
        {
            if (big()) return big_vector.assign(pos, item);
            if (pos < size()) return small_begin + pos;
            throw std::out_of_range("ax::vector indexed out of range.");
        }

        T& front()
        {
            if (big()) return big_vector.front();
            if (size() > 0) return *small_begin;
            throw std::out_of_range("ax::vector indexed out of range.");
        }

        T& back()
        {
            if (big()) return big_vector.back();
            if (size() > 0) return *(small_end - 1);
            throw std::out_of_range("ax::vector indexed out of range.");
        }

        std::size_t capacity() const
        {
            if (big()) return big_vector.capacity();
            return N;
        }

        void clear()
        {
            if (big()) big_vector.clear();
            else small_end = small_begin;
        }

        T* data()
        {
            if (big()) return big_vector.data();
            return small_begin;
        }

        const T* data() const
        {
            return const_cast<ax::vector<T, A, N>*>(this)->data();
        }

        std::size_t size() const
        {
            return end() - begin();
        }

        inline bool big() const
        {
            return small_end < small_begin;
        }

    private:

        T small_begin[N];
        T* small_end;
        std::vector<T, A> big_vector;
    };
}

#endif
<|endoftext|>


<commit_before>/**
 * @file   TickLoopTest.cpp
 * @brief  TickLoop class tester.
 * @author zer0
 * @date   2016-04-29
 */

#ifndef __INCLUDE_LIBTBAG__LIBTBAG_LOOP_TICKLOOP_HPP__
#define __INCLUDE_LIBTBAG__LIBTBAG_LOOP_TICKLOOP_HPP__

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif

#include <libtbag/config.h>
#include <libtbag/Noncopyable.hpp>
#include <libtbag/Time.hpp>

#include <atomic>
#include <chrono>
#include <thread>
#include <functional>

// -------------------
NAMESPACE_LIBTBAG_OPEN
// -------------------

namespace loop {

/**
 * TickLoop class prototype.
 *
 * @author zer0
 * @date   2016-04-29
 */
template <typename Unit = std::chrono::milliseconds>
class TickLoop : public Noncopyable
{
public:
    using SystemClock = std::chrono::system_clock;
    using TimePoint   = SystemClock::time_point;
    using TimeUnit    = Unit;
    using Callback    = std::function<void(void)>;

protected:
    Callback _callback;
    TimeUnit _time_step;

// Sync object.
private:
    std::atomic_bool _exit;
    std::atomic_int  _result_code;

public:
    TickLoop(Callback const & callback, TimeUnit step)
            : _callback(callback)
            , _time_step(step)
            , _exit(false)
            , _result_code(0) {
        __EMPTY_BLOCK__
    }

    ~TickLoop() {
        __EMPTY_BLOCK__
    }

public:
    int run() {
        TimePoint start;
        TimeUnit  duration;

        while (_exit.load() == false) {
            start = SystemClock::now();
            _callback();
            duration = Time::getElapsedTime(start);

            if (duration < _time_step) {
                std::this_thread::yield(); // hint of thread schedule.
                std::this_thread::sleep_for(_time_step - duration);
            }
        }

        return _result_code;
    }

public:
    inline TimeUnit getTimeStep() const noexcept {
        return this->_time_step;
    }

// Sync object.
public:
    inline void setExit(bool exit = true) noexcept {
        this->_exit = exit;
    }

    inline void exit() noexcept {
        this->setExit(true);
    }

    inline void setResultCode(int code) noexcept {
        this->_result_code = code;
    }
};

} // namespace loop

// --------------------
NAMESPACE_LIBTBAG_CLOSE
// --------------------

#endif // __INCLUDE_LIBTBAG__LIBTBAG_LOOP_TICKLOOP_HPP__

<commit_msg>Fix the doxygen warning.<commit_after>/**
 * @file   TickLoop.hpp
 * @brief  TickLoop class tester.
 * @author zer0
 * @date   2016-04-29
 */

#ifndef __INCLUDE_LIBTBAG__LIBTBAG_LOOP_TICKLOOP_HPP__
#define __INCLUDE_LIBTBAG__LIBTBAG_LOOP_TICKLOOP_HPP__

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif

#include <libtbag/config.h>
#include <libtbag/Noncopyable.hpp>
#include <libtbag/Time.hpp>

#include <atomic>
#include <chrono>
#include <thread>
#include <functional>

// -------------------
NAMESPACE_LIBTBAG_OPEN
// -------------------

namespace loop {

/**
 * TickLoop class prototype.
 *
 * @author zer0
 * @date   2016-04-29
 */
template <typename Unit = std::chrono::milliseconds>
class TickLoop : public Noncopyable
{
public:
    using SystemClock = std::chrono::system_clock;
    using TimePoint   = SystemClock::time_point;
    using TimeUnit    = Unit;
    using Callback    = std::function<void(void)>;

protected:
    Callback _callback;
    TimeUnit _time_step;

// Sync object.
private:
    std::atomic_bool _exit;
    std::atomic_int  _result_code;

public:
    TickLoop(Callback const & callback, TimeUnit step)
            : _callback(callback)
            , _time_step(step)
            , _exit(false)
            , _result_code(0) {
        __EMPTY_BLOCK__
    }

    ~TickLoop() {
        __EMPTY_BLOCK__
    }

public:
    int run() {
        TimePoint start;
        TimeUnit  duration;

        while (_exit.load() == false) {
            start = SystemClock::now();
            _callback();
            duration = Time::getElapsedTime(start);

            if (duration < _time_step) {
                std::this_thread::yield(); // hint of thread schedule.
                std::this_thread::sleep_for(_time_step - duration);
            }
        }

        return _result_code;
    }

public:
    inline TimeUnit getTimeStep() const noexcept {
        return this->_time_step;
    }

// Sync object.
public:
    inline void setExit(bool exit = true) noexcept {
        this->_exit = exit;
    }

    inline void exit() noexcept {
        this->setExit(true);
    }

    inline void setResultCode(int code) noexcept {
        this->_result_code = code;
    }
};

} // namespace loop

// --------------------
NAMESPACE_LIBTBAG_CLOSE
// --------------------

#endif // __INCLUDE_LIBTBAG__LIBTBAG_LOOP_TICKLOOP_HPP__

<|endoftext|>


<commit_before>/*
 * CloneLayer.cpp
 *
 *  Created on: May 11, 2011
 *      Author: garkenyon
 */

#include "HyPerLayer.hpp"
#include "GapLayer.hpp"

// GapLayer can be used to implement gap junctions
namespace PV {
GapLayer::GapLayer(const char * name, HyPerCol * hc, LIFGap * originalLayer) :
   HyPerLayer(name, hc, MAX_CHANNELS)
{
   initialize(originalLayer);
}

GapLayer::~GapLayer()
{
    clayer->V = NULL;
}

int GapLayer::initialize(LIFGap * originalLayer)
{
   int status_init = HyPerLayer::initialize(TypeNonspiking);
   this->spikingFlag = false;
   sourceLayer = originalLayer;
   free(clayer->V);
   clayer->V = sourceLayer->getV();
   return status_init;
}

// use LIFGap as source layer instead (LIFGap updates gap juctions more accurately)
#ifdef OBSOLETE
int GapLayer::updateV() {
   pvdata_t * V = getV();
   pvdata_t * GSynExc = getChannel(CHANNEL_EXC);
   pvdata_t exp_deltaT = 1.0f - exp(-this->getParent()->getDeltaTime() / sourceLayer->getLIFParams()->tau);
   for( int k=0; k<getNumNeurons(); k++ ) {
      V[k] += GSynExc[k] * exp_deltaT;  //!!! uses base tau, not the true time-dep tau
   }
   return PV_SUCCESS;
}
#endif

//!!!TODO: add param in LIFGap for spikelet amplitude
int GapLayer::setActivity() {

   HyPerLayer::setActivity();

   // extended activity may not be current but this is alright since only local activity is used
   // !!! will break (non-deterministic) if layers are updated simultaneously--fix is to use datastore
   if (sourceLayer->getSpikingFlag()) { // probably not needed since numActive will be zero for non-spiking
      pvdata_t * sourceActivity = sourceLayer->getCLayer()->activity->data;
      pvdata_t * localActivity = getCLayer()->activity->data;
      unsigned int * activeNdx = sourceLayer->getCLayer()->activeIndices;
      for (unsigned int kActive = 0; kActive < sourceLayer->getCLayer()->numActive; kActive++) {
         int kex = activeNdx[kActive];
         localActivity[kex] += 50 * sourceActivity[kex]; // add 50 mV spike to local membrane potential
      }
   }
   return PV_SUCCESS;
}

} // end namespace PV

<commit_msg>only 'OBSOLETE' the statements within updateV, not updateV<commit_after>/*
 * CloneLayer.cpp
 *
 *  Created on: May 11, 2011
 *      Author: garkenyon
 */

#include "HyPerLayer.hpp"
#include "GapLayer.hpp"

// CloneLayer can be used to implement gap junctions
namespace PV {
GapLayer::GapLayer(const char * name, HyPerCol * hc, LIFGap * originalLayer) :
   HyPerLayer(name, hc, MAX_CHANNELS)
{
   initialize(originalLayer);
}

GapLayer::~GapLayer()
{
    clayer->V = NULL;
}

int GapLayer::initialize(LIFGap * originalLayer)
{
   int status_init = HyPerLayer::initialize(TypeNonspiking);
   this->spikingFlag = false;
   sourceLayer = originalLayer;
   free(clayer->V);
   clayer->V = sourceLayer->getV();
   return status_init;
}

// use LIFGap as source layer instead (LIFGap updates gap juctions more accurately)
int GapLayer::updateV() {
#ifdef OBSOLETE
   pvdata_t * V = getV();
   pvdata_t * GSynExc = getChannel(CHANNEL_EXC);
   pvdata_t exp_deltaT = 1.0f - exp(-this->getParent()->getDeltaTime() / sourceLayer->getLIFParams()->tau);
   for( int k=0; k<getNumNeurons(); k++ ) {
      V[k] += GSynExc[k] * exp_deltaT;  //!!! uses base tau, not the true time-dep tau
#endif
   return PV_SUCCESS;
}

//!!!TODO: add param in LIFGap for spikelet amplitude
int GapLayer::setActivity() {

   HyPerLayer::setActivity(); // this copies the potential into the activity buffer

   // extended activity may not be current but this is alright since only local activity is used
   // !!! will break (non-deterministic) if layers are updated simultaneously--fix is to use datastore
   if (sourceLayer->getSpikingFlag()) { // probably not needed since numActive will be zero for non-spiking
      pvdata_t * sourceActivity = sourceLayer->getCLayer()->activity->data;
      pvdata_t * localActivity = getCLayer()->activity->data;
      unsigned int * activeNdx = sourceLayer->getCLayer()->activeIndices;
      for (unsigned int kActive = 0; kActive < sourceLayer->getCLayer()->numActive; kActive++) {
         int kex = activeNdx[kActive];
         localActivity[kex] += 50 * sourceActivity[kex]; // add 50 mV spike to local membrane potential
      }
   }
   return PV_SUCCESS;
}

} // end namespace PV

<|endoftext|>


<commit_before>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2012-2015 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <inviwo/core/common/inviwoapplication.h>
#include <inviwo/core/processors/canvasprocessor.h>
#include <inviwo/core/util/canvas.h>
#include <inviwo/core/util/datetime.h>
#include <inviwo/core/util/stringconversion.h>
#include <inviwo/core/processors/canvasprocessorwidget.h>
#include <inviwo/core/processors/processorwidget.h>
#include <inviwo/core/network/processornetworkevaluator.h>
#include <inviwo/core/io/datawriterfactory.h>
#include <inviwo/core/util/filesystem.h>
#include <inviwo/core/util/fileextension.h>

namespace inviwo {

CanvasProcessor::CanvasProcessor()
    : Processor()
    , inport_("inport")
    , dimensions_("dimensions", "Canvas Size", ivec2(256, 256), ivec2(128, 128), ivec2(4096, 4096),
                  ivec2(1, 1), VALID)
    , enableCustomInputDimensions_("enableCustomInputDimensions", "Separate Image Size", false,
                                   VALID)
    , customInputDimensions_("customInputDimensions", "Image Size", ivec2(256, 256),
                             ivec2(128, 128), ivec2(4096, 4096), ivec2(1, 1), VALID)
    , keepAspectRatio_("keepAspectRatio", "Lock Aspect Ratio", true, VALID)
    , aspectRatioScaling_("aspectRatioScaling", "Image Scale", 1.f, 0.1f, 4.f, 0.01f, VALID)
    , visibleLayer_("visibleLayer", "Visible Layer")
    , colorLayer_("colorLayer_", "Color Layer ID", 0, 0, 0)
    , saveLayerDirectory_("layerDir", "Output Directory", "", "image")
    , saveLayerButton_("saveLayer", "Save Image Layer", VALID)
    , inputSize_("inputSize", "Input Dimension Parameters")
    , previousImageSize_(customInputDimensions_)
    , evaluator_(nullptr)
    , canvasWidget_(nullptr)
    , queuedRequest_(false) {
    addPort(inport_);
    addProperty(inputSize_);

    dimensions_.onChange(this, &CanvasProcessor::resizeCanvas);
    inputSize_.addProperty(dimensions_);

    enableCustomInputDimensions_.onChange(this, &CanvasProcessor::sizeChanged);
    inputSize_.addProperty(enableCustomInputDimensions_);

    customInputDimensions_.onChange(this, &CanvasProcessor::sizeChanged);
    customInputDimensions_.setVisible(false);
    inputSize_.addProperty(customInputDimensions_);

    keepAspectRatio_.onChange(this, &CanvasProcessor::sizeChanged);
    keepAspectRatio_.setVisible(false);
    inputSize_.addProperty(keepAspectRatio_);

    aspectRatioScaling_.onChange(this, &CanvasProcessor::sizeChanged);
    aspectRatioScaling_.setVisible(false);
    inputSize_.addProperty(aspectRatioScaling_);

    visibleLayer_.addOption("color", "Color layer", COLOR_LAYER);
    visibleLayer_.addOption("depth", "Depth layer", DEPTH_LAYER);
    visibleLayer_.addOption("picking", "Picking layer", PICKING_LAYER);
    visibleLayer_.set(COLOR_LAYER);
    addProperty(visibleLayer_);
    addProperty(colorLayer_);
    addProperty(saveLayerDirectory_);

    saveLayerButton_.onChange(this, &CanvasProcessor::saveImageLayer);
    addProperty(saveLayerButton_);

    colorLayer_.setSerializationMode(PropertySerializationMode::ALL);

    visibleLayer_.onChange([&](){
        int layers = static_cast<int>(inport_.getData()->getNumberOfColorLayers());
        colorLayer_.setVisible(layers != 0 && visibleLayer_.get() == COLOR_LAYER);
    });

    inport_.onChange([&](){
        int layers = static_cast<int>(inport_.getData()->getNumberOfColorLayers());
        colorLayer_.setVisible(layers != 0 && visibleLayer_.get() == COLOR_LAYER);
        colorLayer_.setMaxValue(layers - 1);
    });

    setAllPropertiesCurrentStateAsDefault();
}

CanvasProcessor::~CanvasProcessor() {}

void CanvasProcessor::initialize() {
    Processor::initialize();
    if (processorWidget_) {
        canvasWidget_ = dynamic_cast<CanvasProcessorWidget*>(processorWidget_);
        canvasWidget_->getCanvas()->setEventPropagator(this);
    }
    evaluator_ = InviwoApplication::getPtr()->getProcessorNetworkEvaluator();
    sizeChanged();
}

void CanvasProcessor::deinitialize() {
    if (processorWidget_) {
        processorWidget_->hide();
        canvasWidget_->getCanvas()->setEventPropagator(nullptr);
    }
    canvasWidget_ = nullptr;
    Processor::deinitialize();
}

// Called by dimensions onChange.
void CanvasProcessor::resizeCanvas() {
    NetworkLock lock;
    if (canvasWidget_ && canvasWidget_->getDimensions() != dimensions_.get()) {
        canvasWidget_->setDimensions(dimensions_.get());
    }
    inputSize_.invalidate(VALID, &dimensions_);
}

void CanvasProcessor::setCanvasSize(ivec2 dim) {
    NetworkLock lock;
    dimensions_.set(dim);
    sizeChanged();
}

ivec2 CanvasProcessor::getCanvasSize() const { return dimensions_.get(); }

bool CanvasProcessor::getUseCustomDimensions() const { return enableCustomInputDimensions_; }
ivec2 CanvasProcessor::getCustomDimensions() const { return customInputDimensions_; }

void CanvasProcessor::sizeChanged() {
    NetworkLock lock;

    customInputDimensions_.setVisible(enableCustomInputDimensions_);
    customInputDimensions_.setReadOnly(keepAspectRatio_);
    keepAspectRatio_.setVisible(enableCustomInputDimensions_);
    aspectRatioScaling_.setVisible(enableCustomInputDimensions_ && keepAspectRatio_);

    if (keepAspectRatio_) customInputDimensions_ = calcSize();
    ResizeEvent resizeEvent(uvec2(0));
    if (enableCustomInputDimensions_) {
        resizeEvent.setSize(static_cast<uvec2>(customInputDimensions_.get()));
        resizeEvent.setPreviousSize(static_cast<uvec2>(previousImageSize_));
        previousImageSize_ = customInputDimensions_;
    } else {
        resizeEvent.setSize(static_cast<uvec2>(dimensions_.get()));
        resizeEvent.setPreviousSize(static_cast<uvec2>(previousImageSize_));
        previousImageSize_ = dimensions_;
    }

    inputSize_.invalidate(VALID, &customInputDimensions_);
    inport_.propagateResizeEvent(&resizeEvent);
}

ivec2 CanvasProcessor::calcSize() {
    ivec2 size = dimensions_;

    int maxDim, minDim;

    if (size.x >= size.y) {
        maxDim = 0;
        minDim = 1;
    } else {
        maxDim = 1;
        minDim = 0;
    }

    float ratio = static_cast<float>(size[minDim]) / static_cast<float>(size[maxDim]);
    size[maxDim] = static_cast<int>(static_cast<float>(size[maxDim]) * aspectRatioScaling_);
    size[minDim] = static_cast<int>(static_cast<float>(size[maxDim]) * ratio);

    return size;
}

void CanvasProcessor::saveImageLayer() {
    std::string snapshotPath(saveLayerDirectory_.get() + "/" + toLower(getIdentifier()) + "-" +
                             currentDateTime() + ".png");
    saveImageLayer(snapshotPath);
}

void CanvasProcessor::saveImageLayer(std::string snapshotPath) {
    const Image* image = inport_.getData();
    if (image) {
        const Layer* layer = nullptr;
        if (visibleLayer_.get() == COLOR_LAYER){
            layer = image->getColorLayer(colorLayer_.get());
        }
        else{
            layer = image->getLayer(static_cast<LayerType>(visibleLayer_.get()));
        }
        if (layer) {
            std::string fileExtension = filesystem::getFileExtension(snapshotPath);
            DataWriterType<Layer>* writer = nullptr;
            bool deleteWriter = true;
            if (Canvas::generalLayerWriter_ && fileExtension == "png") {
                writer = Canvas::generalLayerWriter_;
                deleteWriter = false;
            } else {
                writer =
                    DataWriterFactory::getPtr()->getWriterForTypeAndExtension<Layer>(fileExtension);
            }

            if (writer) {
                try {
                    writer->setOverwrite(true);
                    writer->writeData(layer, snapshotPath);
                    LogInfo("Canvas layer exported to disk: " << snapshotPath);
                    if (deleteWriter) delete writer;
                }
                catch (DataWriterException const& e) {
                    LogError(e.getMessage());
                }
            } else {
                LogError(
                    "Error: Cound not find a writer for the specified extension and data type");
            }
        } else {
            LogError("Error: Cound not find color layer to write out");
        }
    } else if (snapshotPath.empty()) {
        LogWarn("Error: Please specify a file to write to");
    } else if (!image) {
        LogWarn("Error: Please connect an image to export");
    }
}

std::vector<unsigned char>* CanvasProcessor::getLayerAsCodedBuffer(LayerType layerType,
                                                                   const std::string& type,
                                                                   size_t idx) {
    if (!inport_.hasData()) return nullptr;
    const Image* image = inport_.getData();
    const Layer* layer = image->getLayer(layerType, idx);

    if (layer) {
        std::unique_ptr<DataWriterType<Layer>> writer{
            DataWriterFactory::getPtr()->getWriterForTypeAndExtension<Layer>(type)};

        if (writer) {
            try {
                return writer->writeDataToBuffer(layer, type);
            }
            catch (DataWriterException const& e) {
                LogError(e.getMessage());
            }
        } else {
            LogError("Error: Cound not find a writer for the specified data type");
        }
    } else {
        LogError("Error: Cound not find layer to write");
    }

    return nullptr;
}

std::vector<unsigned char>* CanvasProcessor::getColorLayerAsCodedBuffer(const std::string& type,
                                                                        size_t idx) {
    return getLayerAsCodedBuffer(LayerType::COLOR_LAYER, type, idx);
}

std::vector<unsigned char>* CanvasProcessor::getDepthLayerAsCodedBuffer(const std::string& type) {
    return getLayerAsCodedBuffer(LayerType::DEPTH_LAYER, type);
}

std::vector<unsigned char>* CanvasProcessor::getPickingLayerAsCodedBuffer(const std::string& type) {
    return getLayerAsCodedBuffer(LayerType::PICKING_LAYER, type);
}

std::vector<unsigned char>* CanvasProcessor::getVisibleLayerAsCodedBuffer(const std::string& type) {
    if (visibleLayer_.get() == COLOR_LAYER){
        getColorLayerAsCodedBuffer(type, colorLayer_.get());
    }
    return getLayerAsCodedBuffer(static_cast<LayerType>(visibleLayer_.get()), type);
}

void CanvasProcessor::process() {
    if (canvasWidget_ && canvasWidget_->getCanvas()) {
        canvasWidget_->getCanvas()->activate();
        LayerType layerType = static_cast<LayerType>(visibleLayer_.get());
        if (visibleLayer_.get() == COLOR_LAYER){
            canvasWidget_->getCanvas()->render(inport_.getData(), COLOR_LAYER, colorLayer_.get());
        }
        else{
            canvasWidget_->getCanvas()->render(inport_.getData(), layerType, 0);
        }//*/
    }
}

void CanvasProcessor::doIfNotReady() {
    if (canvasWidget_ && canvasWidget_->getCanvas()) {
        canvasWidget_->getCanvas()->activate();
        canvasWidget_->getCanvas()->render(nullptr, static_cast<LayerType>(visibleLayer_.get()));
    }
}

void CanvasProcessor::triggerQueuedEvaluation() {
    if (queuedRequest_) {
        performEvaluateRequest();
        queuedRequest_ = false;
    }
}

void CanvasProcessor::performEvaluationAtNextShow() { queuedRequest_ = true; }

void CanvasProcessor::performEvaluateRequest() {
    if (processorWidget_) {
        if (processorWidget_->isVisible()) {
            notifyObserversRequestEvaluate(this);
        } else {
            performEvaluationAtNextShow();
        }
    } else {
        notifyObserversRequestEvaluate(this);
    }
}

bool CanvasProcessor::isReady() const {
    return Processor::isReady() && processorWidget_ && processorWidget_->isVisible();
}

bool CanvasProcessor::propagateResizeEvent(ResizeEvent* event, Outport* source) {
    // avoid continues evaluation when port dimensions changes
    NetworkLock lock;

    dimensions_.set(event->size());

    if (enableCustomInputDimensions_) {
        sizeChanged();
    } else {
        inport_.propagateResizeEvent(event);

        // Make sure this processor is invalidated.
        invalidate(INVALID_OUTPUT);
    }

    return false;
}

}  // namespace
<commit_msg>CanvasProcessor: Crash Fix<commit_after>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2012-2015 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <inviwo/core/common/inviwoapplication.h>
#include <inviwo/core/processors/canvasprocessor.h>
#include <inviwo/core/util/canvas.h>
#include <inviwo/core/util/datetime.h>
#include <inviwo/core/util/stringconversion.h>
#include <inviwo/core/processors/canvasprocessorwidget.h>
#include <inviwo/core/processors/processorwidget.h>
#include <inviwo/core/network/processornetworkevaluator.h>
#include <inviwo/core/io/datawriterfactory.h>
#include <inviwo/core/util/filesystem.h>
#include <inviwo/core/util/fileextension.h>

namespace inviwo {

CanvasProcessor::CanvasProcessor()
    : Processor()
    , inport_("inport")
    , dimensions_("dimensions", "Canvas Size", ivec2(256, 256), ivec2(128, 128), ivec2(4096, 4096),
                  ivec2(1, 1), VALID)
    , enableCustomInputDimensions_("enableCustomInputDimensions", "Separate Image Size", false,
                                   VALID)
    , customInputDimensions_("customInputDimensions", "Image Size", ivec2(256, 256),
                             ivec2(128, 128), ivec2(4096, 4096), ivec2(1, 1), VALID)
    , keepAspectRatio_("keepAspectRatio", "Lock Aspect Ratio", true, VALID)
    , aspectRatioScaling_("aspectRatioScaling", "Image Scale", 1.f, 0.1f, 4.f, 0.01f, VALID)
    , visibleLayer_("visibleLayer", "Visible Layer")
    , colorLayer_("colorLayer_", "Color Layer ID", 0, 0, 0)
    , saveLayerDirectory_("layerDir", "Output Directory", "", "image")
    , saveLayerButton_("saveLayer", "Save Image Layer", VALID)
    , inputSize_("inputSize", "Input Dimension Parameters")
    , previousImageSize_(customInputDimensions_)
    , evaluator_(nullptr)
    , canvasWidget_(nullptr)
    , queuedRequest_(false) {
    addPort(inport_);
    addProperty(inputSize_);

    dimensions_.onChange(this, &CanvasProcessor::resizeCanvas);
    inputSize_.addProperty(dimensions_);

    enableCustomInputDimensions_.onChange(this, &CanvasProcessor::sizeChanged);
    inputSize_.addProperty(enableCustomInputDimensions_);

    customInputDimensions_.onChange(this, &CanvasProcessor::sizeChanged);
    customInputDimensions_.setVisible(false);
    inputSize_.addProperty(customInputDimensions_);

    keepAspectRatio_.onChange(this, &CanvasProcessor::sizeChanged);
    keepAspectRatio_.setVisible(false);
    inputSize_.addProperty(keepAspectRatio_);

    aspectRatioScaling_.onChange(this, &CanvasProcessor::sizeChanged);
    aspectRatioScaling_.setVisible(false);
    inputSize_.addProperty(aspectRatioScaling_);

    visibleLayer_.addOption("color", "Color layer", COLOR_LAYER);
    visibleLayer_.addOption("depth", "Depth layer", DEPTH_LAYER);
    visibleLayer_.addOption("picking", "Picking layer", PICKING_LAYER);
    visibleLayer_.set(COLOR_LAYER);
    addProperty(visibleLayer_);
    addProperty(colorLayer_);
    addProperty(saveLayerDirectory_);

    saveLayerButton_.onChange(this, &CanvasProcessor::saveImageLayer);
    addProperty(saveLayerButton_);

    colorLayer_.setSerializationMode(PropertySerializationMode::ALL);

    visibleLayer_.onChange([&](){
        if (inport_.hasData()){
            int layers = static_cast<int>(inport_.getData()->getNumberOfColorLayers());
            colorLayer_.setVisible(layers != 0 && visibleLayer_.get() == COLOR_LAYER);
        }
        colorLayer_.setVisible(visibleLayer_.get() == COLOR_LAYER);
    });

    inport_.onChange([&](){
        int layers = static_cast<int>(inport_.getData()->getNumberOfColorLayers());
        colorLayer_.setVisible(layers != 0 && visibleLayer_.get() == COLOR_LAYER);
        colorLayer_.setMaxValue(layers - 1);
    });

    setAllPropertiesCurrentStateAsDefault();
}

CanvasProcessor::~CanvasProcessor() {}

void CanvasProcessor::initialize() {
    Processor::initialize();
    if (processorWidget_) {
        canvasWidget_ = dynamic_cast<CanvasProcessorWidget*>(processorWidget_);
        canvasWidget_->getCanvas()->setEventPropagator(this);
    }
    evaluator_ = InviwoApplication::getPtr()->getProcessorNetworkEvaluator();
    sizeChanged();
}

void CanvasProcessor::deinitialize() {
    if (processorWidget_) {
        processorWidget_->hide();
        canvasWidget_->getCanvas()->setEventPropagator(nullptr);
    }
    canvasWidget_ = nullptr;
    Processor::deinitialize();
}

// Called by dimensions onChange.
void CanvasProcessor::resizeCanvas() {
    NetworkLock lock;
    if (canvasWidget_ && canvasWidget_->getDimensions() != dimensions_.get()) {
        canvasWidget_->setDimensions(dimensions_.get());
    }
    inputSize_.invalidate(VALID, &dimensions_);
}

void CanvasProcessor::setCanvasSize(ivec2 dim) {
    NetworkLock lock;
    dimensions_.set(dim);
    sizeChanged();
}

ivec2 CanvasProcessor::getCanvasSize() const { return dimensions_.get(); }

bool CanvasProcessor::getUseCustomDimensions() const { return enableCustomInputDimensions_; }
ivec2 CanvasProcessor::getCustomDimensions() const { return customInputDimensions_; }

void CanvasProcessor::sizeChanged() {
    NetworkLock lock;

    customInputDimensions_.setVisible(enableCustomInputDimensions_);
    customInputDimensions_.setReadOnly(keepAspectRatio_);
    keepAspectRatio_.setVisible(enableCustomInputDimensions_);
    aspectRatioScaling_.setVisible(enableCustomInputDimensions_ && keepAspectRatio_);

    if (keepAspectRatio_) customInputDimensions_ = calcSize();
    ResizeEvent resizeEvent(uvec2(0));
    if (enableCustomInputDimensions_) {
        resizeEvent.setSize(static_cast<uvec2>(customInputDimensions_.get()));
        resizeEvent.setPreviousSize(static_cast<uvec2>(previousImageSize_));
        previousImageSize_ = customInputDimensions_;
    } else {
        resizeEvent.setSize(static_cast<uvec2>(dimensions_.get()));
        resizeEvent.setPreviousSize(static_cast<uvec2>(previousImageSize_));
        previousImageSize_ = dimensions_;
    }

    inputSize_.invalidate(VALID, &customInputDimensions_);
    inport_.propagateResizeEvent(&resizeEvent);
}

ivec2 CanvasProcessor::calcSize() {
    ivec2 size = dimensions_;

    int maxDim, minDim;

    if (size.x >= size.y) {
        maxDim = 0;
        minDim = 1;
    } else {
        maxDim = 1;
        minDim = 0;
    }

    float ratio = static_cast<float>(size[minDim]) / static_cast<float>(size[maxDim]);
    size[maxDim] = static_cast<int>(static_cast<float>(size[maxDim]) * aspectRatioScaling_);
    size[minDim] = static_cast<int>(static_cast<float>(size[maxDim]) * ratio);

    return size;
}

void CanvasProcessor::saveImageLayer() {
    std::string snapshotPath(saveLayerDirectory_.get() + "/" + toLower(getIdentifier()) + "-" +
                             currentDateTime() + ".png");
    saveImageLayer(snapshotPath);
}

void CanvasProcessor::saveImageLayer(std::string snapshotPath) {
    const Image* image = inport_.getData();
    if (image) {
        const Layer* layer = nullptr;
        if (visibleLayer_.get() == COLOR_LAYER){
            layer = image->getColorLayer(colorLayer_.get());
        }
        else{
            layer = image->getLayer(static_cast<LayerType>(visibleLayer_.get()));
        }
        if (layer) {
            std::string fileExtension = filesystem::getFileExtension(snapshotPath);
            DataWriterType<Layer>* writer = nullptr;
            bool deleteWriter = true;
            if (Canvas::generalLayerWriter_ && fileExtension == "png") {
                writer = Canvas::generalLayerWriter_;
                deleteWriter = false;
            } else {
                writer =
                    DataWriterFactory::getPtr()->getWriterForTypeAndExtension<Layer>(fileExtension);
            }

            if (writer) {
                try {
                    writer->setOverwrite(true);
                    writer->writeData(layer, snapshotPath);
                    LogInfo("Canvas layer exported to disk: " << snapshotPath);
                    if (deleteWriter) delete writer;
                }
                catch (DataWriterException const& e) {
                    LogError(e.getMessage());
                }
            } else {
                LogError(
                    "Error: Cound not find a writer for the specified extension and data type");
            }
        } else {
            LogError("Error: Cound not find color layer to write out");
        }
    } else if (snapshotPath.empty()) {
        LogWarn("Error: Please specify a file to write to");
    } else if (!image) {
        LogWarn("Error: Please connect an image to export");
    }
}

std::vector<unsigned char>* CanvasProcessor::getLayerAsCodedBuffer(LayerType layerType,
                                                                   const std::string& type,
                                                                   size_t idx) {
    if (!inport_.hasData()) return nullptr;
    const Image* image = inport_.getData();
    const Layer* layer = image->getLayer(layerType, idx);

    if (layer) {
        std::unique_ptr<DataWriterType<Layer>> writer{
            DataWriterFactory::getPtr()->getWriterForTypeAndExtension<Layer>(type)};

        if (writer) {
            try {
                return writer->writeDataToBuffer(layer, type);
            }
            catch (DataWriterException const& e) {
                LogError(e.getMessage());
            }
        } else {
            LogError("Error: Cound not find a writer for the specified data type");
        }
    } else {
        LogError("Error: Cound not find layer to write");
    }

    return nullptr;
}

std::vector<unsigned char>* CanvasProcessor::getColorLayerAsCodedBuffer(const std::string& type,
                                                                        size_t idx) {
    return getLayerAsCodedBuffer(LayerType::COLOR_LAYER, type, idx);
}

std::vector<unsigned char>* CanvasProcessor::getDepthLayerAsCodedBuffer(const std::string& type) {
    return getLayerAsCodedBuffer(LayerType::DEPTH_LAYER, type);
}

std::vector<unsigned char>* CanvasProcessor::getPickingLayerAsCodedBuffer(const std::string& type) {
    return getLayerAsCodedBuffer(LayerType::PICKING_LAYER, type);
}

std::vector<unsigned char>* CanvasProcessor::getVisibleLayerAsCodedBuffer(const std::string& type) {
    if (visibleLayer_.get() == COLOR_LAYER){
        getColorLayerAsCodedBuffer(type, colorLayer_.get());
    }
    return getLayerAsCodedBuffer(static_cast<LayerType>(visibleLayer_.get()), type);
}

void CanvasProcessor::process() {
    if (canvasWidget_ && canvasWidget_->getCanvas()) {
        canvasWidget_->getCanvas()->activate();
        LayerType layerType = static_cast<LayerType>(visibleLayer_.get());
        if (visibleLayer_.get() == COLOR_LAYER){
            canvasWidget_->getCanvas()->render(inport_.getData(), COLOR_LAYER, colorLayer_.get());
        }
        else{
            canvasWidget_->getCanvas()->render(inport_.getData(), layerType, 0);
        }//*/
    }
}

void CanvasProcessor::doIfNotReady() {
    if (canvasWidget_ && canvasWidget_->getCanvas()) {
        canvasWidget_->getCanvas()->activate();
        canvasWidget_->getCanvas()->render(nullptr, static_cast<LayerType>(visibleLayer_.get()));
    }
}

void CanvasProcessor::triggerQueuedEvaluation() {
    if (queuedRequest_) {
        performEvaluateRequest();
        queuedRequest_ = false;
    }
}

void CanvasProcessor::performEvaluationAtNextShow() { queuedRequest_ = true; }

void CanvasProcessor::performEvaluateRequest() {
    if (processorWidget_) {
        if (processorWidget_->isVisible()) {
            notifyObserversRequestEvaluate(this);
        } else {
            performEvaluationAtNextShow();
        }
    } else {
        notifyObserversRequestEvaluate(this);
    }
}

bool CanvasProcessor::isReady() const {
    return Processor::isReady() && processorWidget_ && processorWidget_->isVisible();
}

bool CanvasProcessor::propagateResizeEvent(ResizeEvent* event, Outport* source) {
    // avoid continues evaluation when port dimensions changes
    NetworkLock lock;

    dimensions_.set(event->size());

    if (enableCustomInputDimensions_) {
        sizeChanged();
    } else {
        inport_.propagateResizeEvent(event);

        // Make sure this processor is invalidated.
        invalidate(INVALID_OUTPUT);
    }

    return false;
}

}  // namespace
<|endoftext|>