<commit_before>#include "pch.h"
#include "DspLimiter.h"

#include <string>

namespace SaneAudioRenderer
{
    namespace
    {
        float f(int64_t leftPos, float leftValue, int64_t rightPos, float rightValue, uint64_t pos)
        {
            return leftValue + (float)(pos - leftPos) / (rightPos - leftPos) * (rightValue - leftValue);
        }

        float f(const std::pair<int64_t, float>& left, const std::pair<int64_t, float>& right, uint64_t pos)
        {
            return f(left.first, left.second, right.first, right.second, pos);
        }
    }

    void DspLimiter::Initialize(uint32_t rate, bool exclusive)
    {
        m_limit = (exclusive ? 1.0f : 0.98f);
        m_attackFrames = rate / 1000; // 1ms
        m_releaseFrames = rate / 100; // 10ms
        m_windowFrames = m_attackFrames * 2 + m_releaseFrames + 1;

        m_buffer.clear();
        m_bufferFrameCount = 0;
        m_bufferFirstFrame = 0;

        m_peaks.clear();
    }

    void DspLimiter::Process(DspChunk& chunk)
    {
        if (!chunk.IsEmpty() && chunk.GetFormat() == DspFormat::Float)
        {
            m_bufferFrameCount += chunk.GetFrameCount();
            m_buffer.push_back(std::move(chunk));

            AnalyzeLastChunk();

            size_t bufferFrontFrames = m_buffer.front().GetFrameCount();
            if (m_bufferFrameCount - bufferFrontFrames >= m_windowFrames)
            {
                ModifyFirstChunk();

                m_bufferFrameCount -= bufferFrontFrames;
                m_bufferFirstFrame += bufferFrontFrames;
                chunk = std::move(m_buffer.front());
                m_buffer.pop_front();
            }
        }
    }

    void DspLimiter::Finish(DspChunk& chunk)
    {
        Process(chunk);
    }

    void DspLimiter::AnalyzeLastChunk()
    {
        assert(m_limit > 0);

        const DspChunk& chunk = m_buffer.back();
        assert(chunk.GetFormat() == DspFormat::Float);

        const uint64_t chunkFirstFrame = m_bufferFirstFrame + m_bufferFrameCount - chunk.GetFrameCount();
        const uint32_t channels = chunk.GetChannelCount();

        auto data = reinterpret_cast<const float*>(chunk.GetConstData());
        for (size_t frame = 0, frameCount = chunk.GetFrameCount(); frame < frameCount; frame++)
        {
            float sample = 0.0f;
            for (size_t i = 0; i < channels; i++)
                sample = fmax(fabs(data[frame * channels + i]), sample);

            if (sample > m_limit)
            {
                if (m_peaks.empty())
                {
                    uint64_t start = chunkFirstFrame + frame > m_attackFrames ?
                                         chunkFirstFrame + frame - m_attackFrames : 0;
                    m_peaks.emplace_back(start, m_limit);
                    m_peaks.emplace_back(chunkFirstFrame + frame, sample);
                    m_peaks.emplace_back(chunkFirstFrame + frame + m_releaseFrames, m_limit);
                    DbgOutString((std::wstring(L"start ") + std::to_wstring(chunkFirstFrame + frame) + L" " + std::to_wstring(sample) + L"\n").c_str());
                }
                else
                {
                    auto& back = m_peaks.back();
                    auto& nextToBack = m_peaks[m_peaks.size() - 2];
                    assert(back.second == m_limit);
                    if (frame + chunkFirstFrame > back.first ||
                        f(nextToBack, back, chunkFirstFrame + frame) < sample)
                    {
                        if (m_peaks.size() > 2 &&
                            sample > nextToBack.second &&
                            nextToBack.first > chunkFirstFrame + frame - m_attackFrames &&
                            f(m_peaks[m_peaks.size() - 3].first, m_peaks[m_peaks.size() - 3].second, chunkFirstFrame + frame, sample, nextToBack.first) > nextToBack.second)
                        {
                            DbgOutString((std::wstring(L"replace ") + std::to_wstring(chunkFirstFrame + frame) + L" " + std::to_wstring(sample) + L"\n").c_str());
                            nextToBack.first = chunkFirstFrame + frame;
                            nextToBack.second = sample;
                            back.first = chunkFirstFrame + frame + m_releaseFrames;
                        }
                        else
                        {
                            DbgOutString((std::wstring(L"add ") + std::to_wstring(chunkFirstFrame + frame) + L" " + std::to_wstring(sample) + L"\n").c_str());
                            back.first = chunkFirstFrame + frame;
                            back.second = sample;
                            m_peaks.emplace_back(chunkFirstFrame + frame + m_releaseFrames, m_limit);
                        }
                    }
                    else
                    {
                        DbgOutString((std::wstring(L"consume ") + std::to_wstring(chunkFirstFrame + frame) + L" " + std::to_wstring(sample) + L"\n").c_str());
                    }
                }
            }
        }
    }

    void DspLimiter::ModifyFirstChunk()
    {
        if (!m_peaks.empty())
        {
            DspChunk& chunk = m_buffer.front();
            assert(chunk.GetFormat() == DspFormat::Float);

            const uint64_t chunkFirstFrame = m_bufferFirstFrame;
            const size_t chunkFrameCount = chunk.GetFrameCount();
            const uint32_t channels = chunk.GetChannelCount();

            const size_t firstFrameOffset = chunkFirstFrame > m_peaks.front().first ?
                                                0 : m_peaks.front().first - chunkFirstFrame;

            auto data = reinterpret_cast<float*>(chunk.GetData());
            for (size_t frame = firstFrameOffset; frame < chunkFrameCount; frame++)
            {
                assert(m_peaks.size() >= 2);
                const auto& left = m_peaks.front();
                const auto& right = m_peaks[1];
                assert(right.first > left.first);

                float divisor = f(left, right, chunkFirstFrame + frame);

                for (size_t i = 0; i < channels; i++)
                {
                    float& sample = data[frame * channels + i];
                    sample *= (double)m_limit / divisor;
                    assert(fabs(sample) <= m_limit);
                }

                if (right.first <= frame + chunkFirstFrame)
                {
                    assert(right.first == frame + chunkFirstFrame);
                    m_peaks.pop_front();
                    if (m_peaks.size() == 1)
                    {
                        DbgOutString(L"clear\n");
                        m_peaks.clear();
                        break;
                    }
                }
            }
        }
    }
}
<commit_msg>Increase limiter dsp attack time<commit_after>#include "pch.h"
#include "DspLimiter.h"

#include <string>

namespace SaneAudioRenderer
{
    namespace
    {
        float f(int64_t leftPos, float leftValue, int64_t rightPos, float rightValue, uint64_t pos)
        {
            return leftValue + (float)(pos - leftPos) / (rightPos - leftPos) * (rightValue - leftValue);
        }

        float f(const std::pair<int64_t, float>& left, const std::pair<int64_t, float>& right, uint64_t pos)
        {
            return f(left.first, left.second, right.first, right.second, pos);
        }
    }

    void DspLimiter::Initialize(uint32_t rate, bool exclusive)
    {
        m_limit = (exclusive ? 1.0f : 0.98f);
        m_attackFrames = rate / 100; // 10ms
        m_releaseFrames = rate / 100; // 10ms
        m_windowFrames = m_attackFrames * 2 + m_releaseFrames + 1;

        m_buffer.clear();
        m_bufferFrameCount = 0;
        m_bufferFirstFrame = 0;

        m_peaks.clear();
    }

    void DspLimiter::Process(DspChunk& chunk)
    {
        if (!chunk.IsEmpty() && chunk.GetFormat() == DspFormat::Float)
        {
            m_bufferFrameCount += chunk.GetFrameCount();
            m_buffer.push_back(std::move(chunk));

            AnalyzeLastChunk();

            size_t bufferFrontFrames = m_buffer.front().GetFrameCount();
            if (m_bufferFrameCount - bufferFrontFrames >= m_windowFrames)
            {
                ModifyFirstChunk();

                m_bufferFrameCount -= bufferFrontFrames;
                m_bufferFirstFrame += bufferFrontFrames;
                chunk = std::move(m_buffer.front());
                m_buffer.pop_front();
            }
        }
    }

    void DspLimiter::Finish(DspChunk& chunk)
    {
        Process(chunk);
    }

    void DspLimiter::AnalyzeLastChunk()
    {
        assert(m_limit > 0);

        const DspChunk& chunk = m_buffer.back();
        assert(chunk.GetFormat() == DspFormat::Float);

        const uint64_t chunkFirstFrame = m_bufferFirstFrame + m_bufferFrameCount - chunk.GetFrameCount();
        const uint32_t channels = chunk.GetChannelCount();

        auto data = reinterpret_cast<const float*>(chunk.GetConstData());
        for (size_t frame = 0, frameCount = chunk.GetFrameCount(); frame < frameCount; frame++)
        {
            float sample = 0.0f;
            for (size_t i = 0; i < channels; i++)
                sample = fmax(fabs(data[frame * channels + i]), sample);

            if (sample > m_limit)
            {
                if (m_peaks.empty())
                {
                    uint64_t start = chunkFirstFrame + frame > m_attackFrames ?
                                         chunkFirstFrame + frame - m_attackFrames : 0;
                    m_peaks.emplace_back(start, m_limit);
                    m_peaks.emplace_back(chunkFirstFrame + frame, sample);
                    m_peaks.emplace_back(chunkFirstFrame + frame + m_releaseFrames, m_limit);
                    DbgOutString((std::wstring(L"start ") + std::to_wstring(chunkFirstFrame + frame) + L" " + std::to_wstring(sample) + L"\n").c_str());
                }
                else
                {
                    auto& back = m_peaks.back();
                    auto& nextToBack = m_peaks[m_peaks.size() - 2];
                    assert(back.second == m_limit);
                    if (frame + chunkFirstFrame > back.first ||
                        f(nextToBack, back, chunkFirstFrame + frame) < sample)
                    {
                        if (m_peaks.size() > 2 &&
                            sample > nextToBack.second &&
                            nextToBack.first > chunkFirstFrame + frame - m_attackFrames &&
                            f(m_peaks[m_peaks.size() - 3].first, m_peaks[m_peaks.size() - 3].second, chunkFirstFrame + frame, sample, nextToBack.first) > nextToBack.second)
                        {
                            DbgOutString((std::wstring(L"replace ") + std::to_wstring(chunkFirstFrame + frame) + L" " + std::to_wstring(sample) + L"\n").c_str());
                            nextToBack.first = chunkFirstFrame + frame;
                            nextToBack.second = sample;
                            back.first = chunkFirstFrame + frame + m_releaseFrames;
                        }
                        else
                        {
                            DbgOutString((std::wstring(L"add ") + std::to_wstring(chunkFirstFrame + frame) + L" " + std::to_wstring(sample) + L"\n").c_str());
                            back.first = chunkFirstFrame + frame;
                            back.second = sample;
                            m_peaks.emplace_back(chunkFirstFrame + frame + m_releaseFrames, m_limit);
                        }
                    }
                    else
                    {
                        DbgOutString((std::wstring(L"consume ") + std::to_wstring(chunkFirstFrame + frame) + L" " + std::to_wstring(sample) + L"\n").c_str());
                    }
                }
            }
        }
    }

    void DspLimiter::ModifyFirstChunk()
    {
        if (!m_peaks.empty())
        {
            DspChunk& chunk = m_buffer.front();
            assert(chunk.GetFormat() == DspFormat::Float);

            const uint64_t chunkFirstFrame = m_bufferFirstFrame;
            const size_t chunkFrameCount = chunk.GetFrameCount();
            const uint32_t channels = chunk.GetChannelCount();

            const size_t firstFrameOffset = chunkFirstFrame > m_peaks.front().first ?
                                                0 : m_peaks.front().first - chunkFirstFrame;

            auto data = reinterpret_cast<float*>(chunk.GetData());
            for (size_t frame = firstFrameOffset; frame < chunkFrameCount; frame++)
            {
                assert(m_peaks.size() >= 2);
                const auto& left = m_peaks.front();
                const auto& right = m_peaks[1];
                assert(right.first > left.first);

                float divisor = f(left, right, chunkFirstFrame + frame);

                for (size_t i = 0; i < channels; i++)
                {
                    float& sample = data[frame * channels + i];
                    sample *= (double)m_limit / divisor;
                    assert(fabs(sample) <= m_limit);
                }

                if (right.first <= frame + chunkFirstFrame)
                {
                    assert(right.first == frame + chunkFirstFrame);
                    m_peaks.pop_front();
                    if (m_peaks.size() == 1)
                    {
                        DbgOutString(L"clear\n");
                        m_peaks.clear();
                        break;
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>
#ifndef EC_MANAGER_HPP
#define EC_MANAGER_HPP

#define EC_INIT_ENTITIES_SIZE 256
#define EC_GROW_SIZE_AMOUNT 256

#include <cstddef>
#include <tuple>
#include <utility>

#include "Meta/Combine.hpp"
#include "Bitset.hpp"

namespace EC
{
    template <typename ComponentsList, typename TagsList>
    struct Manager
    {
    public:
        using Combined = EC::Meta::Combine<ComponentsList, TagsList>;
        using BitsetType = EC::Bitset<ComponentsList, TagsList>;

    private:
        template <typename... Types>
        struct Storage
        {
            using type = std::tuple<std::vector<Types>... >;
        };
        using ComponentsStorage = typename EC::Meta::Morph<ComponentsList, Storage<> >::type;
        // Entity: isAlive, dataIndex, ComponentsTags Info
        using EntitiesTupleType = std::tuple<bool, std::size_t, BitsetType>;
        using EntitiesType = std::vector<EntitiesTupleType>;

        EntitiesType entities;
        ComponentsStorage componentsStorage;
        std::size_t currentCapacity = 0;
        std::size_t currentSize = 0;

    public:
        Manager()
        {
            resize(EC_INIT_ENTITIES_SIZE);
        }

    private:
        void resize(std::size_t newCapacity)
        {
            if(currentCapacity >= newCapacity)
            {
                return;
            }

            EC::Meta::forEach<ComponentsList>([this, newCapacity] (auto t) {
                std::get<std::vector<decltype(t)> >(this->componentsStorage).resize(newCapacity);
            });

            entities.resize(newCapacity);
            for(std::size_t i = currentCapacity; i < newCapacity; ++i)
            {
                entities[i] = std::make_tuple(false, i, BitsetType{});
            }

            currentCapacity = newCapacity;
        }

    public:
        std::size_t addEntity()
        {
            if(currentSize == currentCapacity)
            {
                resize(currentCapacity + EC_GROW_SIZE_AMOUNT);
            }

            std::get<bool>(entities[currentSize]) = true;

            return currentSize++;
        }

        void deleteEntity(std::size_t index)
        {
            std::get<bool>(entities.at(index)) = false;
        }

        bool hasEntity(std::size_t index) const
        {
            return index < currentSize;
        }

        bool isAlive(std::size_t index) const
        {
            return hasEntity(index) && std::get<bool>(entities.at(index));
        }

        const EntitiesTupleType& getEntityInfo(std::size_t index) const
        {
            return entities.at(index);
        }

        template <typename Component>
        Component& getEntityData(std::size_t index)
        {
            return std::get<std::vector<Component> >(componentsStorage).at(std::get<std::size_t>(entities.at(index)));
        }

        template <typename Component>
        bool hasComponent(std::size_t index) const
        {
            return std::get<BitsetType>(entities.at(index)).template getComponentBit<Component>();
        }

        template <typename Tag>
        bool hasTag(std::size_t index) const
        {
            return std::get<BitsetType>(entities.at(index)).template getTagBit<Tag>();
        }

        void cleanup()
        {
            if(currentSize == 0)
            {
                return;
            }

            std::size_t rhs = currentSize - 1;
            std::size_t lhs = 0;

            while(lhs < rhs)
            {
                while(!std::get<bool>(entities[rhs]))
                {
                    --rhs;
                    if(rhs == 0)
                    {
                        currentSize = 0;
                        return;
                    }
                }
                if(lhs >= rhs)
                {
                    break;
                }
                else if(!std::get<bool>(entities[lhs]))
                {
                    // lhs is marked for deletion
                    // swap lhs entity with rhs entity
                    std::swap(entities[lhs], entities.at(rhs));

                    // clear deleted bitset
                    std::get<BitsetType>(entities[rhs]).reset();

                    // inc/dec pointers
                    ++lhs; --rhs;
                }
                else
                {
                    ++lhs;
                }
            }
            currentSize = rhs + 1;
        }

        template <typename Component, typename... Args>
        void addComponent(std::size_t entityID, Args&&... args)
        {
            if(!hasEntity(entityID) || !isAlive(entityID))
            {
                return;
            }

            Component component(args...);

            std::get<BitsetType>(entities[entityID]).template getComponentBit<Component>() = true;
            std::get<std::vector<Component> >(componentsStorage)[std::get<std::size_t>(entities[entityID])] = component;
        }

        template <typename Component>
        void removeComponent(std::size_t entityID)
        {
            if(!hasEntity(entityID) || !isAlive(entityID))
            {
                return;
            }

            std::get<BitsetType>(entities[entityID]).template getComponentBit<Component>() = false;
        }

        template <typename Tag>
        void addTag(std::size_t entityID)
        {
            if(!hasEntity(entityID) || !isAlive(entityID))
            {
                return;
            }

            std::get<BitsetType>(entities[entityID]).template getTagBit<Tag>() = true;
        }

        template <typename Tag>
        void removeTag(std::size_t entityID)
        {
            if(!hasEntity(entityID) || !isAlive(entityID))
            {
                return;
            }

            std::get<BitsetType>(entities[entityID]).template getTagBit<Tag>() = false;
        }

    private:
        template <typename... Types>
        struct ForMatchingSignatureHelper
        {
            template <typename CType, typename Function>
            static void call(std::size_t entityID, CType& ctype, Function&& function)
            {
                function(
                    entityID,
                    ctype.template getEntityData<Types>(entityID)...
                );
            }
        };

    public:
        template <typename Signature, typename Function>
        void forMatchingSignature(Function&& function)
        {
            using SignatureComponents = typename EC::Meta::Matching<Signature, ComponentsList>::type;
            using Helper = EC::Meta::Morph<SignatureComponents, ForMatchingSignatureHelper<> >;

            BitsetType signatureBitset = BitsetType::template generateBitset<Signature>();
            for(std::size_t i = 0; i < currentSize; ++i)
            {
                if(!std::get<bool>(entities[i]))
                {
                    continue;
                }

                if((signatureBitset & std::get<BitsetType>(entities[i])) == signatureBitset)
                {
                    Helper::call(i, *this, std::forward<Function>(function));
                }
            }
        }

    };
}

#endif

/*! \class EC::Manager
    \brief Manages an EntityComponent system.
*/

/*! \fn EC::Manager::Manager()
    \brief Initializes the manager with a default capacity.

    The default capacity is set with macro EC_INIT_ENTITIES_SIZE,
    and will grow by amounts of EC_GROW_SIZE_AMOUNT when needed.
*/

/*! \fn std::size_t EC::Manager::addEntity()
    \brief Adds an entity to the system, returning the ID of the entity.

    WARNING: The ID of an entity may change after calls to cleanup().
    Only use the ID given during usage of forMatchingSignature().
*/

/*! \fn void EC::Manager::deleteEntity(std::size_t index)
    \brief Marks an entity for deletion.

    A deleted Entity is not actually deleted until cleanup() is called.
    While an Entity is "deleted" but still in the system, calls to forMatchingSignature()
    will ignore the Entity.
*/

/*! \fn bool EC::Manager::hasEntity(std::size_t index) const
    \brief Checks if the Entity with the given ID is in the system.

    Note that deleted Entities that haven't yet been cleaned up (via cleanup()) are
    considered still in the system.
*/

/*! \fn bool EC::Manager::isAlive(std::size_t index) const
    \brief Checks if the Entity is not marked as deleted.

    Note that invalid Entities (Entities where calls to hasEntity() returns false)
    will return false.
*/

/*! \fn const EntitiesTupleType& EC::Manager::getEntityInfo(std::size_t index) const
    \brief Returns a const reference to an Entity's info.

    An Entity's info is a std::tuple with a bool, std::size_t, and a bitset.
    \n The bool determines if the Entity is alive.
    \n The std::size_t is the ID to this Entity's data in the system.
    \n The bitset shows what Components and Tags belong to the Entity.
*/

/*! \fn Component& EC::Manager::getEntityData(std::size_t index)
    \brief Returns a reference to a component belonging to the given Entity.

    This function will return a reference to a Component regardless of whether or
    not the Entity actually owns the reference. If the Entity doesn't own the Component,
    changes to the Component will not affect any Entity. It is recommended to use
    hasComponent() to determine if the Entity actually owns that Component.
*/

/*! \fn bool EC::Manager::hasComponent(std::size_t index) const
    \brief Checks whether or not the given Entity has the given Component.

    Example:
    \code{.cpp}
        manager.hasComponent<C0>(entityID);
    \endcode
*/

/*! \fn bool EC::Manager::hasTag(std::size_t index) const
    \brief Checks whether or not the given Entity has the given Tag.

    Example:
    \code{.cpp}
        manager.hasTag<T0>(entityID);
    \endcode
*/

/*! \fn void EC::Manager::cleanup()
    \brief Does garbage collection on Entities.

    Does housekeeping on the vector containing Entities that will result in
    entity IDs changing if some Entities were marked for deletion.
    <b>This function should be called periodically to correctly handle deletion of entities.</b>
*/

/*! \fn void EC::Manager::addComponent(std::size_t entityID, Args&&... args)
    \brief Adds a component to the given Entity.

    Additional parameters given to this function will construct the Component with those
    parameters.

    Example:
    \code{.cpp}
        struct C0
        {
            C0(int a, char b) : a(a), b(b)
            {}

            int a;
            char b;
        }

        manager.addComponent<C0>(entityID, 10, 'd');
    \endcode
*/

/*! \fn void EC::Manager::removeComponent(std::size_t entityID)
    \brief Removes the given Component from the given Entity.

    If the Entity does not have the Component given, nothing will change.

    Example:
    \code{.cpp}
        manager.removeComponent<C0>(entityID);
    \endcode
*/

/*! \fn void EC::Manager::addTag(std::size_t entityID)
    \brief Adds the given Tag to the given Entity.

    Example:
    \code{.cpp}
        manager.addTag<T0>(entityID);
    \endcode
*/

/*! \fn void EC::Manager::removeTag(std::size_t entityID)
    \brief Removes the given Tag from the given Entity.

    If the Entity does not have the Tag given, nothing will change.

    Example:
    \code{.cpp}
        manager.removeTag<T0>(entityID);
    \endcode
*/

/*! \fn void EC::Manager::forMatchingSignature(Function&& function)
    \brief Calls the given function on all Entities matching the given Signature.

    The function object given to this function must accept std::size_t as its first
    parameter and Component references for the rest of the parameters. Tags specified in the
    Signature are only used as filters and will not be given as a parameter to the function.

    Example:
    \code{.cpp}
        manager.forMatchingSignature<TypeList<C0, C1, T0>>([] (std::size_t ID, C0& component0, C1& component1) {
            // Lambda function contents here.
        });
    \endcode
    Note, the ID given to the function is not permanent. An entity's ID may change when cleanup() is called.
*/

<commit_msg>Fix to EC::Manager::cleanup<commit_after>
#ifndef EC_MANAGER_HPP
#define EC_MANAGER_HPP

#define EC_INIT_ENTITIES_SIZE 256
#define EC_GROW_SIZE_AMOUNT 256

#include <cstddef>
#include <tuple>
#include <utility>

#include "Meta/Combine.hpp"
#include "Bitset.hpp"

namespace EC
{
    template <typename ComponentsList, typename TagsList>
    struct Manager
    {
    public:
        using Combined = EC::Meta::Combine<ComponentsList, TagsList>;
        using BitsetType = EC::Bitset<ComponentsList, TagsList>;

    private:
        template <typename... Types>
        struct Storage
        {
            using type = std::tuple<std::vector<Types>... >;
        };
        using ComponentsStorage = typename EC::Meta::Morph<ComponentsList, Storage<> >::type;
        // Entity: isAlive, dataIndex, ComponentsTags Info
        using EntitiesTupleType = std::tuple<bool, std::size_t, BitsetType>;
        using EntitiesType = std::vector<EntitiesTupleType>;

        EntitiesType entities;
        ComponentsStorage componentsStorage;
        std::size_t currentCapacity = 0;
        std::size_t currentSize = 0;

    public:
        Manager()
        {
            resize(EC_INIT_ENTITIES_SIZE);
        }

    private:
        void resize(std::size_t newCapacity)
        {
            if(currentCapacity >= newCapacity)
            {
                return;
            }

            EC::Meta::forEach<ComponentsList>([this, newCapacity] (auto t) {
                std::get<std::vector<decltype(t)> >(this->componentsStorage).resize(newCapacity);
            });

            entities.resize(newCapacity);
            for(std::size_t i = currentCapacity; i < newCapacity; ++i)
            {
                entities[i] = std::make_tuple(false, i, BitsetType{});
            }

            currentCapacity = newCapacity;
        }

    public:
        std::size_t addEntity()
        {
            if(currentSize == currentCapacity)
            {
                resize(currentCapacity + EC_GROW_SIZE_AMOUNT);
            }

            std::get<bool>(entities[currentSize]) = true;

            return currentSize++;
        }

        void deleteEntity(std::size_t index)
        {
            std::get<bool>(entities.at(index)) = false;
        }

        bool hasEntity(std::size_t index) const
        {
            return index < currentSize;
        }

        bool isAlive(std::size_t index) const
        {
            return hasEntity(index) && std::get<bool>(entities.at(index));
        }

        const EntitiesTupleType& getEntityInfo(std::size_t index) const
        {
            return entities.at(index);
        }

        template <typename Component>
        Component& getEntityData(std::size_t index)
        {
            return std::get<std::vector<Component> >(componentsStorage).at(std::get<std::size_t>(entities.at(index)));
        }

        template <typename Component>
        bool hasComponent(std::size_t index) const
        {
            return std::get<BitsetType>(entities.at(index)).template getComponentBit<Component>();
        }

        template <typename Tag>
        bool hasTag(std::size_t index) const
        {
            return std::get<BitsetType>(entities.at(index)).template getTagBit<Tag>();
        }

        void cleanup()
        {
            if(currentSize == 0)
            {
                return;
            }

            std::size_t rhs = currentSize - 1;
            std::size_t lhs = 0;

            while(lhs < rhs)
            {
                while(!std::get<bool>(entities[rhs]))
                {
                    if(rhs == 0)
                    {
                        currentSize = 0;
                        return;
                    }
                    --rhs;
                }
                if(lhs >= rhs)
                {
                    break;
                }
                else if(!std::get<bool>(entities[lhs]))
                {
                    // lhs is marked for deletion
                    // swap lhs entity with rhs entity
                    std::swap(entities[lhs], entities.at(rhs));

                    // clear deleted bitset
                    std::get<BitsetType>(entities[rhs]).reset();

                    // inc/dec pointers
                    ++lhs; --rhs;
                }
                else
                {
                    ++lhs;
                }
            }
            currentSize = rhs + 1;
        }

        template <typename Component, typename... Args>
        void addComponent(std::size_t entityID, Args&&... args)
        {
            if(!hasEntity(entityID) || !isAlive(entityID))
            {
                return;
            }

            Component component(args...);

            std::get<BitsetType>(entities[entityID]).template getComponentBit<Component>() = true;
            std::get<std::vector<Component> >(componentsStorage)[std::get<std::size_t>(entities[entityID])] = component;
        }

        template <typename Component>
        void removeComponent(std::size_t entityID)
        {
            if(!hasEntity(entityID) || !isAlive(entityID))
            {
                return;
            }

            std::get<BitsetType>(entities[entityID]).template getComponentBit<Component>() = false;
        }

        template <typename Tag>
        void addTag(std::size_t entityID)
        {
            if(!hasEntity(entityID) || !isAlive(entityID))
            {
                return;
            }

            std::get<BitsetType>(entities[entityID]).template getTagBit<Tag>() = true;
        }

        template <typename Tag>
        void removeTag(std::size_t entityID)
        {
            if(!hasEntity(entityID) || !isAlive(entityID))
            {
                return;
            }

            std::get<BitsetType>(entities[entityID]).template getTagBit<Tag>() = false;
        }

    private:
        template <typename... Types>
        struct ForMatchingSignatureHelper
        {
            template <typename CType, typename Function>
            static void call(std::size_t entityID, CType& ctype, Function&& function)
            {
                function(
                    entityID,
                    ctype.template getEntityData<Types>(entityID)...
                );
            }
        };

    public:
        template <typename Signature, typename Function>
        void forMatchingSignature(Function&& function)
        {
            using SignatureComponents = typename EC::Meta::Matching<Signature, ComponentsList>::type;
            using Helper = EC::Meta::Morph<SignatureComponents, ForMatchingSignatureHelper<> >;

            BitsetType signatureBitset = BitsetType::template generateBitset<Signature>();
            for(std::size_t i = 0; i < currentSize; ++i)
            {
                if(!std::get<bool>(entities[i]))
                {
                    continue;
                }

                if((signatureBitset & std::get<BitsetType>(entities[i])) == signatureBitset)
                {
                    Helper::call(i, *this, std::forward<Function>(function));
                }
            }
        }

    };
}

#endif

/*! \class EC::Manager
    \brief Manages an EntityComponent system.
*/

/*! \fn EC::Manager::Manager()
    \brief Initializes the manager with a default capacity.

    The default capacity is set with macro EC_INIT_ENTITIES_SIZE,
    and will grow by amounts of EC_GROW_SIZE_AMOUNT when needed.
*/

/*! \fn std::size_t EC::Manager::addEntity()
    \brief Adds an entity to the system, returning the ID of the entity.

    WARNING: The ID of an entity may change after calls to cleanup().
    Only use the ID given during usage of forMatchingSignature().
*/

/*! \fn void EC::Manager::deleteEntity(std::size_t index)
    \brief Marks an entity for deletion.

    A deleted Entity is not actually deleted until cleanup() is called.
    While an Entity is "deleted" but still in the system, calls to forMatchingSignature()
    will ignore the Entity.
*/

/*! \fn bool EC::Manager::hasEntity(std::size_t index) const
    \brief Checks if the Entity with the given ID is in the system.

    Note that deleted Entities that haven't yet been cleaned up (via cleanup()) are
    considered still in the system.
*/

/*! \fn bool EC::Manager::isAlive(std::size_t index) const
    \brief Checks if the Entity is not marked as deleted.

    Note that invalid Entities (Entities where calls to hasEntity() returns false)
    will return false.
*/

/*! \fn const EntitiesTupleType& EC::Manager::getEntityInfo(std::size_t index) const
    \brief Returns a const reference to an Entity's info.

    An Entity's info is a std::tuple with a bool, std::size_t, and a bitset.
    \n The bool determines if the Entity is alive.
    \n The std::size_t is the ID to this Entity's data in the system.
    \n The bitset shows what Components and Tags belong to the Entity.
*/

/*! \fn Component& EC::Manager::getEntityData(std::size_t index)
    \brief Returns a reference to a component belonging to the given Entity.

    This function will return a reference to a Component regardless of whether or
    not the Entity actually owns the reference. If the Entity doesn't own the Component,
    changes to the Component will not affect any Entity. It is recommended to use
    hasComponent() to determine if the Entity actually owns that Component.
*/

/*! \fn bool EC::Manager::hasComponent(std::size_t index) const
    \brief Checks whether or not the given Entity has the given Component.

    Example:
    \code{.cpp}
        manager.hasComponent<C0>(entityID);
    \endcode
*/

/*! \fn bool EC::Manager::hasTag(std::size_t index) const
    \brief Checks whether or not the given Entity has the given Tag.

    Example:
    \code{.cpp}
        manager.hasTag<T0>(entityID);
    \endcode
*/

/*! \fn void EC::Manager::cleanup()
    \brief Does garbage collection on Entities.

    Does housekeeping on the vector containing Entities that will result in
    entity IDs changing if some Entities were marked for deletion.
    <b>This function should be called periodically to correctly handle deletion of entities.</b>
*/

/*! \fn void EC::Manager::addComponent(std::size_t entityID, Args&&... args)
    \brief Adds a component to the given Entity.

    Additional parameters given to this function will construct the Component with those
    parameters.

    Example:
    \code{.cpp}
        struct C0
        {
            C0(int a, char b) : a(a), b(b)
            {}

            int a;
            char b;
        }

        manager.addComponent<C0>(entityID, 10, 'd');
    \endcode
*/

/*! \fn void EC::Manager::removeComponent(std::size_t entityID)
    \brief Removes the given Component from the given Entity.

    If the Entity does not have the Component given, nothing will change.

    Example:
    \code{.cpp}
        manager.removeComponent<C0>(entityID);
    \endcode
*/

/*! \fn void EC::Manager::addTag(std::size_t entityID)
    \brief Adds the given Tag to the given Entity.

    Example:
    \code{.cpp}
        manager.addTag<T0>(entityID);
    \endcode
*/

/*! \fn void EC::Manager::removeTag(std::size_t entityID)
    \brief Removes the given Tag from the given Entity.

    If the Entity does not have the Tag given, nothing will change.

    Example:
    \code{.cpp}
        manager.removeTag<T0>(entityID);
    \endcode
*/

/*! \fn void EC::Manager::forMatchingSignature(Function&& function)
    \brief Calls the given function on all Entities matching the given Signature.

    The function object given to this function must accept std::size_t as its first
    parameter and Component references for the rest of the parameters. Tags specified in the
    Signature are only used as filters and will not be given as a parameter to the function.

    Example:
    \code{.cpp}
        manager.forMatchingSignature<TypeList<C0, C1, T0>>([] (std::size_t ID, C0& component0, C1& component1) {
            // Lambda function contents here.
        });
    \endcode
    Note, the ID given to the function is not permanent. An entity's ID may change when cleanup() is called.
*/

<|endoftext|>


<commit_before>//
// Created by Bart on 4/21/2015.
//

#include "FileSystem.h"

FileSystem::FileSystem() {
    // Stub
}

FileSystem::~FileSystem() {
    // Stub
}

FSResult FileSystem::loadDirectory(char *directory) {
    // Does this directory exist... or is it even a directory?
    if (!exists(directory) || !is_directory(directory)) {
        return FileSystem::E_INVALID_DIRECTORY;
    }

    // Discover main_file_cache.dat2 (the '/' operator glues two paths)
    path mainFile = path(directory) / string("main_file_cache.dat2");
    if (!exists(mainFile)) {
        return E_NO_MAINFILE;
    }

    // Load all the *.idx files
    map<int, path*> validIndices; // Map to hold our .idx file paths
    for (directory_entry e : make_iterator_range(directory_iterator(directory))) { // Loop
        path current = e.path();
        string extension = current.extension().string();

        if (starts_with(extension, ".idx") && extension.size() > 4) { // Is this an index descriptor?
            int idxId = atoi(extension.substr(4).c_str()); // Substring from 4, parse as integer.

            // Check if the range is valid. If so, store it in our map.
            if (idxId >= 0 && idxId <= 255) {
                validIndices[idxId] = new path(current); // Copy, otherwise it falls out of scope
            }
        }
    }

    // Set this instance's map to the allocated one
    this->validIndices = validIndices;

    return FileSystem::RESULT_OK; // All good.
}
<commit_msg>Free up allocated path objects<commit_after>//
// Created by Bart on 4/21/2015.
//

#include "FileSystem.h"

FileSystem::FileSystem() {
    // Stub
}

FileSystem::~FileSystem() {
    // Free up the path instances created with 'new'
    for (auto pair : validIndices) {
        delete pair.second;
    }
}

FSResult FileSystem::loadDirectory(char *directory) {
    // Does this directory exist... or is it even a directory?
    if (!exists(directory) || !is_directory(directory)) {
        return FileSystem::E_INVALID_DIRECTORY;
    }

    // Discover main_file_cache.dat2 (the '/' operator glues two paths)
    path mainFile = path(directory) / string("main_file_cache.dat2");
    if (!exists(mainFile)) {
        return E_NO_MAINFILE;
    }

    // Load all the *.idx files
    map<int, path*> validIndices; // Map to hold our .idx file paths
    for (directory_entry e : make_iterator_range(directory_iterator(directory))) { // Loop
        path current = e.path();
        string extension = current.extension().string();

        if (starts_with(extension, ".idx") && extension.size() > 4) { // Is this an index descriptor?
            int idxId = atoi(extension.substr(4).c_str()); // Substring from 4, parse as integer.

            // Check if the range is valid. If so, store it in our map.
            if (idxId >= 0 && idxId <= 255) {
                validIndices[idxId] = new path(current); // Copy, otherwise it falls out of scope
            }
        }
    }

    // Set this instance's map to the allocated one
    this->validIndices = validIndices;

    return FileSystem::RESULT_OK; // All good.
}
<|endoftext|>


<commit_before>//
//  FileManager.hpp
//  CatchLib
//
//  Created by John Barbero Unenge on 9/17/12.
//  Copyright (c) 2012 John Barbero Unenge. All rights reserved.
//

#include "Logger.hpp"
#include "GLRenderer.hpp"

GLRenderer::GLRenderer()
{
	Log(LOG_INFO, "GLRenderer", "Constructed a GLRenderer!");

}
GLRenderer::~GLRenderer()
{
	Log(LOG_INFO, "GLRenderer", "Destroyed a GLRenderer!");
}

void GLRenderer::init(int width, int height)
{
	Log(LOG_INFO, "GLRenderer", "Initialized GLRenderer");
}

void GLRenderer::render()
{
	Log(LOG_INFO, "GLRenderer", "Rendering!");
}
<commit_msg>Made the GLRenderer use the InputManager.<commit_after>//
//  FileManager.hpp
//  CatchLib
//
//  Created by John Barbero Unenge on 9/17/12.
//  Copyright (c) 2012 John Barbero Unenge. All rights reserved.
//

#include "Logger.hpp"
#include "GLRenderer.hpp"
#include "InputManager.hpp"

GLRenderer::GLRenderer()
{
	Log(LOG_INFO, "GLRenderer", "Constructed a GLRenderer!");

}
GLRenderer::~GLRenderer()
{
	Log(LOG_INFO, "GLRenderer", "Destroyed a GLRenderer!");
}

void GLRenderer::init(int width, int height)
{
	Log(LOG_INFO, "GLRenderer", "Initialized GLRenderer");
    InputManager::getSharedManager()->passInputEvent(TOUCH, 1, 3);
}

void GLRenderer::render()
{
	Log(LOG_INFO, "GLRenderer", "Rendering!");
}
<|endoftext|>


<commit_before>#ifndef PLATER_HPP
#define PLATER_HPP
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
    #include <wx/wx.h>
#endif

#include <wx/notebook.h>

#include <stack>

#include "libslic3r.h"
#include "Model.hpp"
#include "Print.hpp"
#include "Config.hpp"

#include "Plater/PlaterObject.hpp"
#include "Plater/Plate2D.hpp"
#include "Plater/Plate3D.hpp"
#include "Plater/Preview3D.hpp"
#include "Settings.hpp"

#include "MainFrame.hpp"

namespace Slic3r { namespace GUI {

using UndoOperation = int;
using obj_index = unsigned int;

class PlaterObject;
class Plate2D;
class MainFrame;

/// Extension of wxPanel class to handle the main plater.
/// 2D, 3D, preview, etc tabs.
class Plater : public wxPanel
{
public:
    Plater(wxWindow* parent, const wxString& title, std::shared_ptr<Settings> _settings);

    /// User-level function called through external interface.
    /// Pops file dialog.
    void add();

    /// Arrange models via a simple packing mechanism based on bounding boxes.
    void arrange();

    /// Ask if there are any unsaved changes.
    bool prompt_unsaved_changes() { return true; }
private:
    std::shared_ptr<Slic3r::Print> print {std::make_shared<Print>(Slic3r::Print())};
    std::shared_ptr<Slic3r::Model> model {std::make_shared<Model>(Slic3r::Model())};
    std::shared_ptr<Settings> settings {};

    std::shared_ptr<Slic3r::Config> config { Slic3r::Config::new_from_defaults(
        {"bed_shape", "complete_objects", "extruder_clearance_radius", "skirts", "skirt_distance",
        "brim_width", "serial_port", "serial_speed", "host_type", "print_host", "octoprint_apikey",
        "shortcuts", "filament_colour", "duplicate_distance"})};

    bool processed {false};

    std::vector<PlaterObject> objects {}; //< Main object vector.

    size_t object_identifier {0U}; //< Counter for adding objects to Slic3r. Increment after adding each object.

    std::stack<UndoOperation> undo {};
    std::stack<UndoOperation> redo {};

    wxNotebook* preview_notebook {new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxSize(335,335), wxNB_BOTTOM)};
    wxBoxSizer* right_sizer {new wxBoxSizer(wxVERTICAL)};

    wxToolBar* htoolbar {nullptr}; //< toolbar for non-MSW platforms.
    wxBoxSizer* btoolbar {nullptr}; //< button-based toolbar for Windows

    Plate2D* canvas2D {nullptr}; //< 2D plater canvas
    Plate3D* canvas3D {nullptr}; //< 3D plater canvas

    Preview3D* preview3D {nullptr}; //< 3D Preview

    /// Handles the actual load of the file from the dialog handoff.
    std::vector<int> load_file(const std::string file, const int obj_idx_to_load = -1);

    const std::string LogChannel {"GUI_Plater"}; //< Which log these messages should go to.

    /// Populate the PlaterObject vector.
    std::vector<int> load_model_objects(ModelObject* model_object);
    std::vector<int> load_model_objects(ModelObjectPtrs model_objects);

    bool scaled_down {false};
    bool outside_bounds {false};

    /// Method to get the top-level window and cast it as a MainFrame.
    MainFrame* GetFrame();

    void select_object(size_t& obj_idx) { };
    int get_object_index(size_t object_id);

    /// Get the center of the configured bed's bounding box.
    Slic3r::Pointf bed_centerf() {
        const auto& bed_shape { Slic3r::Polygon::new_scale(this->config->get<ConfigOptionPoints>("bed_shape").values) };
        const auto& bed_center {BoundingBox(bed_shape).center()};
        return Slic3r::Pointf::new_unscale(bed_center);
    }

    /// Build thumbnails for the models
    void make_thumbnail(size_t idx);

    /// Complete thumbnail transformation and refresh canvases
    void on_thumbnail_made(size_t idx);

    /// Issue a repaint event to all of the canvasses.
    void refresh_canvases();

    /// Run everything that needs to happen when models change.
    /// Includes updating canvases, reloading menus, etc.
    void on_model_change(bool force_autocenter = false);
};

} } // Namespace Slic3r::GUI

#endif  // PLATER_HPP
<commit_msg>added a little bit of whitespace to Plater.hpp<commit_after>#ifndef PLATER_HPP
#define PLATER_HPP
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
    #include <wx/wx.h>
#endif

#include <wx/notebook.h>

#include <stack>

#include "libslic3r.h"
#include "Model.hpp"
#include "Print.hpp"
#include "Config.hpp"

#include "Plater/PlaterObject.hpp"
#include "Plater/Plate2D.hpp"
#include "Plater/Plate3D.hpp"
#include "Plater/Preview3D.hpp"
#include "Settings.hpp"

#include "MainFrame.hpp"

namespace Slic3r { namespace GUI {

using UndoOperation = int;
using obj_index = unsigned int;

class PlaterObject;
class Plate2D;
class MainFrame;

/// Extension of wxPanel class to handle the main plater.
/// 2D, 3D, preview, etc tabs.
class Plater : public wxPanel
{
public:
    Plater(wxWindow* parent, const wxString& title, std::shared_ptr<Settings> _settings);

    /// User-level function called through external interface.
    /// Pops file dialog.
    void add();

    /// Arrange models via a simple packing mechanism based on bounding boxes.
    void arrange();

    /// Ask if there are any unsaved changes.
    bool prompt_unsaved_changes() { return true; }

private:
    std::shared_ptr<Slic3r::Print> print {std::make_shared<Print>(Slic3r::Print())};
    std::shared_ptr<Slic3r::Model> model {std::make_shared<Model>(Slic3r::Model())};
    std::shared_ptr<Settings> settings {};

    std::shared_ptr<Slic3r::Config> config { Slic3r::Config::new_from_defaults(
        {"bed_shape", "complete_objects", "extruder_clearance_radius", "skirts", "skirt_distance",
        "brim_width", "serial_port", "serial_speed", "host_type", "print_host", "octoprint_apikey",
        "shortcuts", "filament_colour", "duplicate_distance"})};

    bool processed {false};

    std::vector<PlaterObject> objects {}; //< Main object vector.

    size_t object_identifier {0U}; //< Counter for adding objects to Slic3r. Increment after adding each object.

    std::stack<UndoOperation> undo {};
    std::stack<UndoOperation> redo {};

    wxNotebook* preview_notebook {new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxSize(335,335), wxNB_BOTTOM)};
    wxBoxSizer* right_sizer {new wxBoxSizer(wxVERTICAL)};

    wxToolBar* htoolbar {nullptr}; //< toolbar for non-MSW platforms.
    wxBoxSizer* btoolbar {nullptr}; //< button-based toolbar for Windows

    Plate2D* canvas2D {nullptr}; //< 2D plater canvas
    Plate3D* canvas3D {nullptr}; //< 3D plater canvas

    Preview3D* preview3D {nullptr}; //< 3D Preview

    /// Handles the actual load of the file from the dialog handoff.
    std::vector<int> load_file(const std::string file, const int obj_idx_to_load = -1);

    const std::string LogChannel {"GUI_Plater"}; //< Which log these messages should go to.

    /// Populate the PlaterObject vector.
    std::vector<int> load_model_objects(ModelObject* model_object);
    std::vector<int> load_model_objects(ModelObjectPtrs model_objects);

    bool scaled_down {false};
    bool outside_bounds {false};

    /// Method to get the top-level window and cast it as a MainFrame.
    MainFrame* GetFrame();

    void select_object(size_t& obj_idx) { };
    int get_object_index(size_t object_id);

    /// Get the center of the configured bed's bounding box.
    Slic3r::Pointf bed_centerf() {
        const auto& bed_shape { Slic3r::Polygon::new_scale(this->config->get<ConfigOptionPoints>("bed_shape").values) };
        const auto& bed_center {BoundingBox(bed_shape).center()};
        return Slic3r::Pointf::new_unscale(bed_center);
    }

    /// Build thumbnails for the models
    void make_thumbnail(size_t idx);

    /// Complete thumbnail transformation and refresh canvases
    void on_thumbnail_made(size_t idx);

    /// Issue a repaint event to all of the canvasses.
    void refresh_canvases();

    /// Run everything that needs to happen when models change.
    /// Includes updating canvases, reloading menus, etc.
    void on_model_change(bool force_autocenter = false);
};

} } // Namespace Slic3r::GUI

#endif  // PLATER_HPP
<|endoftext|>


<commit_before><commit_msg>Http cache: Fix a leak on RemovePendingCallbackFromNewEntry<commit_after><|endoftext|>


<commit_before>#include <iostream>
#include <vector>

#include <opencv2/imgproc/imgproc.hpp>

#include "MainWindow.h"
#include "BookDataDialog.h"
#include <Exceptions.h>

MainWindow::MainWindow(void) :
  start_stop_button("Start"),
  find_button("Find book"),
  add_book_button("Add book"),
  stream_source(STREAM_SOURCE::CAMERA),
  source_label("Source:"),
  run(false),
  find(false),
  showing_image_thread(nullptr)
{
  set_title("Book reogition");
  set_size_request(800, 600);

  makeMenu();
  setFilters();

  Gtk::Menu_Helpers::MenuList& list_upd = source_menu.items();
  list_upd.push_back(
    Gtk::Menu_Helpers::MenuElem("From camera", sigc::bind(sigc::mem_fun(*this,
    &MainWindow::on_source_menu_changed), STREAM_SOURCE::CAMERA)));
  list_upd.push_back(
    Gtk::Menu_Helpers::MenuElem("From file", sigc::bind(sigc::mem_fun(*this,
    &MainWindow::on_source_menu_changed), STREAM_SOURCE::FILE)));

  dispatcher.connect([&]()
  {
    showing_image_mutex.lock();
    pixbuf = Gdk::Pixbuf::create_from_data(image.data, Gdk::COLORSPACE_RGB, false, 8, image.cols, image.rows, image.step);
    image_place.set(pixbuf);
    showing_image_mutex.unlock();
  });

  option_menu.set_menu(source_menu);

  start_stop_button.set_sensitive(false);
  add_book_button.set_sensitive(false);

  add(main_box);
  main_box.pack_start(*(m_refUIManager->get_widget("/MenuBar")), Gtk::PACK_SHRINK);
  main_box.pack_start(image_event_box);
  main_box.pack_end(buttons_box, Gtk::PACK_SHRINK);

  image_event_box.add(image_place);

  buttons_box.pack_end(start_stop_button);
  buttons_box.pack_start(source_label, Gtk::PACK_SHRINK);
  buttons_box.pack_start(option_menu, Gtk::PACK_SHRINK);
  buttons_box.pack_start(find_button, Gtk::PACK_SHRINK);
  buttons_box.pack_start(add_book_button, Gtk::PACK_SHRINK);

  start_stop_button.signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_start_stop_button_clicked));
  find_button.signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_find_button_clicked));
  add_book_button.signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_add_book_button_clicked));
  image_event_box.signal_button_press_event().connect(sigc::mem_fun(*this, &MainWindow::on_on_image_place_clicked));

  //showing
  main_box.show();
  main_box.show_all_children(true);

  //control
  recognizer.setDatabase(&db);
}

MainWindow::~MainWindow(void)
{
}

void MainWindow::makeMenu()
{
  ui = "<ui>\n"
    "<menubar name='MenuBar'>\n"
    "<menu action='MenuFile'>\n"
    "<menuitem action='LoadDatabase' />\n"
    "<menuitem action='SaveDatabase' />\n"
    "<menuitem action='ClearDatabase' />\n"
    "<menuitem action='Quit' />\n"
    "</menu>\n"
    "</menubar>\n"
    "</ui>\n";

  m_refActionGroup = Gtk::ActionGroup::create();
  m_refActionGroup->add(Gtk::Action::create("MenuFile", "_File"));
  m_refActionGroup->add(Gtk::Action::create("Quit", "Exit"), sigc::mem_fun(*this, &MainWindow::on_quit_menu_item_clicked));
  m_refActionGroup->add(Gtk::Action::create("LoadDatabase", "_Load database"), sigc::mem_fun(*this, &MainWindow::on_load_database_menu_item_clicked));
  m_refActionGroup->add(Gtk::Action::create("SaveDatabase", "_Store database"), sigc::mem_fun(*this, &MainWindow::on_save_database_menu_item_clicked));
  m_refActionGroup->add(Gtk::Action::create("ClearDatabase", "_Clear database"), sigc::mem_fun(*this, &MainWindow::on_clear_database_menu_item_clicked));

  m_refUIManager = Gtk::UIManager::create();
  m_refUIManager->insert_action_group(m_refActionGroup);
  add_accel_group(m_refUIManager->get_accel_group());
  m_refUIManager->add_ui_from_string(ui);
}

void MainWindow::on_quit_menu_item_clicked()
{
  if (!canQuit())
  {
    hide();
  }
}

bool MainWindow::on_delete_event(GdkEventAny* event)
{
  //TODO: Dialog
  return canQuit();
}

bool MainWindow::canQuit()
{
  if (run)
  {
    Gtk::MessageDialog("You cannot quit during run").run();
    return true;
  }
  if (!db.isSaved())
  {
    Gtk::MessageDialog dialog(*this, "Do you want to store databese before quit?", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
    dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);

    switch (dialog.run())
    {
    case Gtk::RESPONSE_YES:
      on_save_database_menu_item_clicked();
      break;
    case Gtk::RESPONSE_CANCEL:
      return true;
    }
  }
  return false;
}

void MainWindow::on_start_stop_button_clicked()
{
  if (run) //stopping
  {
    find = run = false;
    showing_image_thread->join();
    delete showing_image_thread;
    showing_image_thread = nullptr;
    start_stop_button.set_label("Start");
    option_menu.set_sensitive(true);
    add_book_button.set_sensitive(true);
  }
  else //starting
  {
    run = true;
    //showing_image_thread = Glib::Thread::create(sigc::mem_fun(*this, &MainWindow::showing_frames), true);
    showing_image_thread = new std::thread(&MainWindow::showing_frames, this);
    start_stop_button.set_label("Stop");
    option_menu.set_sensitive(false);
    add_book_button.set_sensitive(false);
  }
}

void MainWindow::on_load_database_menu_item_clicked()
{
  if (run) //running
  {
    Gtk::MessageDialog("You can't load database while running").run();
    return;
  }
  Gtk::FileChooserDialog dialog("Choose a file", Gtk::FILE_CHOOSER_ACTION_OPEN);
  dialog.set_transient_for(*this);

  dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
  dialog.add_button("OK", Gtk::RESPONSE_OK);

  dialog.add_filter(xml_filter);
  dialog.add_filter(all_filter);

  if (dialog.run() == Gtk::RESPONSE_OK)
  {
    try {
      db.load(dialog.get_filename());
    } catch (BR::Exception * ex) {
      Gtk::MessageDialog(ex->getFullMessage().c_str()).run();
      return;
    }
  }
}

void MainWindow::on_save_database_menu_item_clicked()
{
  Gtk::FileChooserDialog dialog("Choose a file", Gtk::FILE_CHOOSER_ACTION_SAVE);
  dialog.set_transient_for(*this);

  dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
  dialog.add_button("OK", Gtk::RESPONSE_OK);

  dialog.add_filter(xml_filter);
  dialog.add_filter(all_filter);

  if (dialog.run() == Gtk::RESPONSE_OK)
  {
    std::string file = dialog.get_filename();
    if (file.substr(file.length() - 4) != ".xml")
    {
      file = file + ".xml";
    }
    db.save(file);
  }
}

void MainWindow::on_clear_database_menu_item_clicked()
{
  if (Gtk::MessageDialog(*this, "Do you want to clear current database?", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO).run() == Gtk::RESPONSE_YES)
  {
    db.clear();
  }
}

void MainWindow::on_source_menu_changed(STREAM_SOURCE source)
{
  start_stop_button.set_sensitive(true);
  add_book_button.set_sensitive(false);
  switch (source)
  {
  case STREAM_SOURCE::CAMERA:
    recognizer.open(0);
    break;
  case STREAM_SOURCE::FILE:
    Gtk::FileChooserDialog dialog("Choose a video file", Gtk::FILE_CHOOSER_ACTION_SAVE);
    dialog.set_transient_for(*this);
    dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
    dialog.add_button("OK", Gtk::RESPONSE_OK);
    dialog.add_filter(video_filter);
    dialog.add_filter(all_filter);
    if (dialog.run() == Gtk::RESPONSE_OK)
    {
      recognizer.open(dialog.get_filename());
    }
    break;
  }
}

void MainWindow::setFilters()
{
  xml_filter.set_name("XML files");
  xml_filter.add_mime_type("text/xml");
  xml_filter.add_mime_type("application/xml");

  all_filter.set_name("Any files");
  all_filter.add_pattern("*");

  video_filter.set_name("Video files");
  video_filter.add_mime_type("video/mpeg");
  video_filter.add_mime_type("video/mp4");
  video_filter.add_mime_type("video/quicktime");
  video_filter.add_mime_type("video/x-ms-wmv");
  video_filter.add_mime_type("video/ogg");
  video_filter.add_mime_type("video/webm");
  video_filter.add_mime_type("video/x-flv");
}

void MainWindow::showing_frames()
{
  cv::Mat tmp;
  try
  {
  while (run)
  {
    gdk_threads_enter();
    getFrame(false);
    gdk_threads_leave();
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }
  }
  catch (...)
  {
    gdk_threads_leave();
  }
}

bool MainWindow::on_on_image_place_clicked(GdkEventButton * button)
{
  if (!read_image)
  {
    return true;
  }
  static int count = 0;
  static std::vector<cv::Point2f> points(4);
  points[count] = cv::Point2f(button->x, button->y);
  if (++count == 4)
  {
    count = 0;
    read_image = false;
    BookDataDialog bdd;
    if (bdd.run() == Gtk::RESPONSE_OK)
    {
      db.fixAndAdd(image, points, bdd.getTitle(), bdd.getAuthor(), bdd.getISBN());
      Gtk::MessageDialog("Book was added to database").run();
    }
  }

  return true;
}

void MainWindow::on_add_book_button_clicked()
{
  read_image = true;
}

void MainWindow::on_find_button_clicked()
{
  on_start_stop_button_clicked();
  getFrame(true);
  recognizer.hold(false);
}

void MainWindow::getFrame(bool find_book)
{
  recognizer.next(find_book);
  cv::Mat tmp = recognizer.getCurrentFrame(true);
  cv::Mat tmp2;
  cv::cvtColor(tmp, tmp2, CV_BGR2RGB);
  cv::resize(tmp2, image, cv::Size(image_event_box.get_width(), image_event_box.get_height()));
  pixbuf = Gdk::Pixbuf::create_from_data(image.data, Gdk::COLORSPACE_RGB, false, 8, image.cols, image.rows, image.step);
  image_place.set(pixbuf);
  image_place.queue_draw();
  //showing_image_mutex.unlock();
  //dispatcher.emit();
  //std::this_thread::yield();
}

<commit_msg>fix? #1<commit_after>#include <iostream>
#include <vector>

#include <opencv2/imgproc/imgproc.hpp>

#include "MainWindow.h"
#include "BookDataDialog.h"
#include <Exceptions.h>

MainWindow::MainWindow(void) :
  start_stop_button("Start"),
  find_button("Find book"),
  add_book_button("Add book"),
  stream_source(STREAM_SOURCE::CAMERA),
  source_label("Source:"),
  run(false),
  find(false),
  showing_image_thread(nullptr)
{
  set_title("Book reogition");
  set_size_request(800, 600);

  makeMenu();
  setFilters();

  Gtk::Menu_Helpers::MenuList& list_upd = source_menu.items();
  list_upd.push_back(
    Gtk::Menu_Helpers::MenuElem("From camera", sigc::bind(sigc::mem_fun(*this,
    &MainWindow::on_source_menu_changed), STREAM_SOURCE::CAMERA)));
  list_upd.push_back(
    Gtk::Menu_Helpers::MenuElem("From file", sigc::bind(sigc::mem_fun(*this,
    &MainWindow::on_source_menu_changed), STREAM_SOURCE::FILE)));

  dispatcher.connect([&]()
  {
    showing_image_mutex.lock();
    pixbuf = Gdk::Pixbuf::create_from_data(image.data, Gdk::COLORSPACE_RGB, false, 8, image.cols, image.rows, image.step);
    image_place.set(pixbuf);
    showing_image_mutex.unlock();
  });

  option_menu.set_menu(source_menu);

  start_stop_button.set_sensitive(false);
  add_book_button.set_sensitive(false);

  add(main_box);
  main_box.pack_start(*(m_refUIManager->get_widget("/MenuBar")), Gtk::PACK_SHRINK);
  main_box.pack_start(image_event_box);
  main_box.pack_end(buttons_box, Gtk::PACK_SHRINK);

  image_event_box.add(image_place);

  buttons_box.pack_end(start_stop_button);
  buttons_box.pack_start(source_label, Gtk::PACK_SHRINK);
  buttons_box.pack_start(option_menu, Gtk::PACK_SHRINK);
  buttons_box.pack_start(find_button, Gtk::PACK_SHRINK);
  buttons_box.pack_start(add_book_button, Gtk::PACK_SHRINK);

  start_stop_button.signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_start_stop_button_clicked));
  find_button.signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_find_button_clicked));
  add_book_button.signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_add_book_button_clicked));
  image_event_box.signal_button_press_event().connect(sigc::mem_fun(*this, &MainWindow::on_on_image_place_clicked));

  //showing
  main_box.show();
  main_box.show_all_children(true);

  //control
  recognizer.setDatabase(&db);
}

MainWindow::~MainWindow(void)
{
}

void MainWindow::makeMenu()
{
  ui = "<ui>\n"
    "<menubar name='MenuBar'>\n"
    "<menu action='MenuFile'>\n"
    "<menuitem action='LoadDatabase' />\n"
    "<menuitem action='SaveDatabase' />\n"
    "<menuitem action='ClearDatabase' />\n"
    "<menuitem action='Quit' />\n"
    "</menu>\n"
    "</menubar>\n"
    "</ui>\n";

  m_refActionGroup = Gtk::ActionGroup::create();
  m_refActionGroup->add(Gtk::Action::create("MenuFile", "_File"));
  m_refActionGroup->add(Gtk::Action::create("Quit", "Exit"), sigc::mem_fun(*this, &MainWindow::on_quit_menu_item_clicked));
  m_refActionGroup->add(Gtk::Action::create("LoadDatabase", "_Load database"), sigc::mem_fun(*this, &MainWindow::on_load_database_menu_item_clicked));
  m_refActionGroup->add(Gtk::Action::create("SaveDatabase", "_Store database"), sigc::mem_fun(*this, &MainWindow::on_save_database_menu_item_clicked));
  m_refActionGroup->add(Gtk::Action::create("ClearDatabase", "_Clear database"), sigc::mem_fun(*this, &MainWindow::on_clear_database_menu_item_clicked));

  m_refUIManager = Gtk::UIManager::create();
  m_refUIManager->insert_action_group(m_refActionGroup);
  add_accel_group(m_refUIManager->get_accel_group());
  m_refUIManager->add_ui_from_string(ui);
}

void MainWindow::on_quit_menu_item_clicked()
{
  if (!canQuit())
  {
    hide();
  }
}

bool MainWindow::on_delete_event(GdkEventAny* event)
{
  //TODO: Dialog
  return canQuit();
}

bool MainWindow::canQuit()
{
  if (run)
  {
    Gtk::MessageDialog("You cannot quit during run").run();
    return true;
  }
  if (!db.isSaved())
  {
    Gtk::MessageDialog dialog(*this, "Do you want to store databese before quit?", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO);
    dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);

    switch (dialog.run())
    {
    case Gtk::RESPONSE_YES:
      on_save_database_menu_item_clicked();
      break;
    case Gtk::RESPONSE_CANCEL:
      return true;
    }
  }
  return false;
}

void MainWindow::on_start_stop_button_clicked()
{
  if (run) //stopping
  {
    find = run = false;
    std::this_thread::sleep_for(std::chrono::milliseconds(100)); //wait for other thread to finish
    showing_image_thread->join();
    delete showing_image_thread;
    showing_image_thread = nullptr;
    start_stop_button.set_label("Start");
    option_menu.set_sensitive(true);
    add_book_button.set_sensitive(true);
  }
  else //starting
  {
    run = true;
    //showing_image_thread = Glib::Thread::create(sigc::mem_fun(*this, &MainWindow::showing_frames), true);
    showing_image_thread = new std::thread(&MainWindow::showing_frames, this);
    start_stop_button.set_label("Stop");
    option_menu.set_sensitive(false);
    add_book_button.set_sensitive(false);
  }
}

void MainWindow::on_load_database_menu_item_clicked()
{
  if (run) //running
  {
    Gtk::MessageDialog("You can't load database while running").run();
    return;
  }
  Gtk::FileChooserDialog dialog("Choose a file", Gtk::FILE_CHOOSER_ACTION_OPEN);
  dialog.set_transient_for(*this);

  dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
  dialog.add_button("OK", Gtk::RESPONSE_OK);

  dialog.add_filter(xml_filter);
  dialog.add_filter(all_filter);

  if (dialog.run() == Gtk::RESPONSE_OK)
  {
    try {
      db.load(dialog.get_filename());
    } catch (BR::Exception * ex) {
      Gtk::MessageDialog(ex->getFullMessage().c_str()).run();
      return;
    }
  }
}

void MainWindow::on_save_database_menu_item_clicked()
{
  Gtk::FileChooserDialog dialog("Choose a file", Gtk::FILE_CHOOSER_ACTION_SAVE);
  dialog.set_transient_for(*this);

  dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
  dialog.add_button("OK", Gtk::RESPONSE_OK);

  dialog.add_filter(xml_filter);
  dialog.add_filter(all_filter);

  if (dialog.run() == Gtk::RESPONSE_OK)
  {
    std::string file = dialog.get_filename();
    if (file.substr(file.length() - 4) != ".xml")
    {
      file = file + ".xml";
    }
    db.save(file);
  }
}

void MainWindow::on_clear_database_menu_item_clicked()
{
  if (Gtk::MessageDialog(*this, "Do you want to clear current database?", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO).run() == Gtk::RESPONSE_YES)
  {
    db.clear();
  }
}

void MainWindow::on_source_menu_changed(STREAM_SOURCE source)
{
  start_stop_button.set_sensitive(true);
  add_book_button.set_sensitive(false);
  switch (source)
  {
  case STREAM_SOURCE::CAMERA:
    recognizer.open(0);
    break;
  case STREAM_SOURCE::FILE:
    Gtk::FileChooserDialog dialog("Choose a video file", Gtk::FILE_CHOOSER_ACTION_SAVE);
    dialog.set_transient_for(*this);
    dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
    dialog.add_button("OK", Gtk::RESPONSE_OK);
    dialog.add_filter(video_filter);
    dialog.add_filter(all_filter);
    if (dialog.run() == Gtk::RESPONSE_OK)
    {
      recognizer.open(dialog.get_filename());
    }
    break;
  }
}

void MainWindow::setFilters()
{
  xml_filter.set_name("XML files");
  xml_filter.add_mime_type("text/xml");
  xml_filter.add_mime_type("application/xml");

  all_filter.set_name("Any files");
  all_filter.add_pattern("*");

  video_filter.set_name("Video files");
  video_filter.add_mime_type("video/mpeg");
  video_filter.add_mime_type("video/mp4");
  video_filter.add_mime_type("video/quicktime");
  video_filter.add_mime_type("video/x-ms-wmv");
  video_filter.add_mime_type("video/ogg");
  video_filter.add_mime_type("video/webm");
  video_filter.add_mime_type("video/x-flv");
}

void MainWindow::showing_frames()
{
  cv::Mat tmp;
  try
  {
  while (run)
  {
    gdk_threads_enter();
    getFrame(false);
    gdk_threads_leave();
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }
  }
  catch (...)
  {
    gdk_threads_leave();
  }
}

bool MainWindow::on_on_image_place_clicked(GdkEventButton * button)
{
  if (!read_image)
  {
    return true;
  }
  static int count = 0;
  static std::vector<cv::Point2f> points(4);
  points[count] = cv::Point2f(button->x, button->y);
  if (++count == 4)
  {
    count = 0;
    read_image = false;
    BookDataDialog bdd;
    if (bdd.run() == Gtk::RESPONSE_OK)
    {
      db.fixAndAdd(image, points, bdd.getTitle(), bdd.getAuthor(), bdd.getISBN());
      Gtk::MessageDialog("Book was added to database").run();
    }
  }

  return true;
}

void MainWindow::on_add_book_button_clicked()
{
  read_image = true;
}

void MainWindow::on_find_button_clicked()
{
  on_start_stop_button_clicked();
  getFrame(true);
  recognizer.hold(false);
}

void MainWindow::getFrame(bool find_book)
{
  recognizer.next(find_book);
  cv::Mat tmp = recognizer.getCurrentFrame(true);
  cv::Mat tmp2;
  cv::cvtColor(tmp, tmp2, CV_BGR2RGB);
  cv::resize(tmp2, image, cv::Size(image_event_box.get_width(), image_event_box.get_height()));
  pixbuf = Gdk::Pixbuf::create_from_data(image.data, Gdk::COLORSPACE_RGB, false, 8, image.cols, image.rows, image.step);
  image_place.set(pixbuf);
  image_place.queue_draw();
  //showing_image_mutex.unlock();
  //dispatcher.emit();
  //std::this_thread::yield();
}

<|endoftext|>


<commit_before>#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "Horse.h"
#include "../World.h"
#include "../EffectID.h"
#include "../Entities/Player.h"
#include "../UI/HorseWindow.h"

cHorse::cHorse(int Type, int Color, int Style, int TameTimes) :
	Super("Horse", mtHorse, "entity.horse.hurt", "entity.horse.death", "entity.horse.ambient", 1.4, 1.6),
	cEntityWindowOwner(this),
	m_bHasChest(false),
	m_bIsEating(false),
	m_bIsRearing(false),
	m_bIsMouthOpen(false),
	m_bIsTame(false),
	m_Type(Type),
	m_Color(Color),
	m_Style(Style),
	m_TimesToTame(TameTimes),
	m_TameAttemptTimes(0),
	m_RearTickCount(0),
	m_MaxSpeed(14.0)
{
}

cHorse::~cHorse()
{
	auto Window = GetWindow();
	if (Window != nullptr)
	{
		Window->OwnerDestroyed();
	}
}

void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
	Super::Tick(a_Dt, a_Chunk);
	if (!IsTicking())
	{
		// The base class tick destroyed us
		return;
	}

	auto & Random = GetRandomProvider();

	if (!m_bIsMouthOpen)
	{
		if (Random.RandBool(0.02))
		{
			m_bIsMouthOpen = true;
		}
	}
	else
	{
		if (Random.RandBool(0.10))
		{
			m_bIsMouthOpen = false;
		}
	}

	if ((m_Attachee != nullptr) && (!m_bIsTame))
	{
		if (m_TameAttemptTimes < m_TimesToTame)
		{
			if (Random.RandBool(0.02))
			{
				m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, GetPosition().Floor(), int(SmokeDirection::SOUTH_EAST));
				m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, GetPosition().Floor(), int(SmokeDirection::SOUTH_WEST));
				m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, GetPosition().Floor(), int(SmokeDirection::NORTH_EAST));
				m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, GetPosition().Floor(), int(SmokeDirection::NORTH_WEST));

				m_World->BroadcastSoundEffect("entity.horse.angry", GetPosition(), 1.0f, 1.0f);
				m_Attachee->Detach();
				m_bIsRearing = true;
			}
		}
		else
		{
			m_World->BroadcastParticleEffect("heart", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5);
			m_bIsTame = true;
		}
	}

	if (m_bIsRearing)
	{
		if (m_RearTickCount == 20)
		{
			m_bIsRearing = false;
			m_RearTickCount = 0;
		}
		else
		{
			m_RearTickCount++;
		}
	}

	m_World->BroadcastEntityMetadata(*this);
}

void cHorse::OnRightClicked(cPlayer & a_Player)
{
	Super::OnRightClicked(a_Player);

	if (m_bIsTame)
	{
		if (a_Player.IsCrouched())
		{
			PlayerOpenWindow(a_Player);
			return;
		}

		auto EquipedItemType = a_Player.GetEquippedItem().m_ItemType;

		if (
			!IsSaddled() &&
			(
				(EquipedItemType == E_ITEM_SADDLE) ||
				ItemCategory::IsHorseArmor(EquipedItemType)
			)
		)
		{
			// Player is holding a horse inventory item, open the window:
			PlayerOpenWindow(a_Player);
		}
		else
		{
			a_Player.AttachTo(this);
		}
	}
	else if (a_Player.GetEquippedItem().IsEmpty())
	{
		// Check if leashed / unleashed to player before try to ride
		if (!m_IsLeashActionJustDone)
		{
			if (m_Attachee != nullptr)
			{
				if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID())
				{
					a_Player.Detach();
					return;
				}

				if (m_Attachee->IsPlayer())
				{
					return;
				}

				m_Attachee->Detach();
			}

			m_TameAttemptTimes++;
			a_Player.AttachTo(this);
		}
	}
	else
	{
		m_bIsRearing = true;
		m_RearTickCount = 0;
		m_World->BroadcastSoundEffect("entity.horse.angry", GetPosition(), 1.0f, 0.8f);
	}
}

void cHorse::SetHorseSaddle(cItem a_Saddle)
{
	if (a_Saddle.m_ItemType == E_ITEM_SADDLE)
	{
		m_World->BroadcastSoundEffect("entity.horse.saddle", GetPosition(), 1.0f, 0.8f);
	}
	else if (!a_Saddle.IsEmpty())
	{
		return;  // Invalid item
	}

	m_Saddle = std::move(a_Saddle);
	m_World->BroadcastEntityMetadata(*this);
}

void cHorse::SetHorseArmor(cItem a_Armor)
{
	if (ItemCategory::IsHorseArmor(a_Armor.m_ItemType))
	{
		m_World->BroadcastSoundEffect("entity.horse.armor", GetPosition(), 1.0f, 0.8f);
	}
	else if (!a_Armor.IsEmpty())
	{
		return;  // Invalid item
	}

	m_Armor = std::move(a_Armor);
	m_World->BroadcastEntityMetadata(*this);
}

int cHorse::GetHorseArmour(void) const
{
	switch (m_Armor.m_ItemType)
	{
		case E_ITEM_EMPTY:               return 0;
		case E_ITEM_IRON_HORSE_ARMOR:    return 1;
		case E_ITEM_GOLD_HORSE_ARMOR:    return 2;
		case E_ITEM_DIAMOND_HORSE_ARMOR: return 3;

		default:
		{
			LOGWARN("cHorse::GetHorseArmour: Invalid armour item (%d)", m_Armor.m_ItemType);
			return 0;
		}
	}
}

void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
	if (IsBaby())
	{
		return;  // Babies don't drop items
	}

	unsigned int LootingLevel = 0;
	if (a_Killer != nullptr)
	{
		LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
	}
	AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_LEATHER);
	if (IsSaddled())
	{
		a_Drops.push_back(m_Saddle);
	}
	if (!m_Armor.IsEmpty())
	{
		a_Drops.push_back(m_Armor);
	}
}

void cHorse::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
	// If horse is tame and someone is sitting on it, don't walk around
	if ((!m_bIsTame) || (m_Attachee == nullptr))
	{
		Super::InStateIdle(a_Dt, a_Chunk);
	}
}

void cHorse::HandleSpeedFromAttachee(float a_Forward, float a_Sideways)
{
	if ((m_bIsTame) && IsSaddled())
	{
		Super::HandleSpeedFromAttachee(a_Forward * m_MaxSpeed, a_Sideways * m_MaxSpeed);
	}
}

void cHorse::PlayerOpenWindow(cPlayer & a_Player)
{
	auto Window = GetWindow();
	if (Window == nullptr)
	{
		Window = new cHorseWindow(*this);
		OpenWindow(Window);
	}

	a_Player.OpenWindow(*Window);
}
<commit_msg>Horsies: don't always broadcast metadata<commit_after>#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "Horse.h"
#include "../World.h"
#include "../EffectID.h"
#include "../Entities/Player.h"
#include "../UI/HorseWindow.h"

cHorse::cHorse(int Type, int Color, int Style, int TameTimes) :
	Super("Horse", mtHorse, "entity.horse.hurt", "entity.horse.death", "entity.horse.ambient", 1.4, 1.6),
	cEntityWindowOwner(this),
	m_bHasChest(false),
	m_bIsEating(false),
	m_bIsRearing(false),
	m_bIsMouthOpen(false),
	m_bIsTame(false),
	m_Type(Type),
	m_Color(Color),
	m_Style(Style),
	m_TimesToTame(TameTimes),
	m_TameAttemptTimes(0),
	m_RearTickCount(0),
	m_MaxSpeed(14.0)
{
}

cHorse::~cHorse()
{
	auto Window = GetWindow();
	if (Window != nullptr)
	{
		Window->OwnerDestroyed();
	}
}

void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
	Super::Tick(a_Dt, a_Chunk);
	if (!IsTicking())
	{
		// The base class tick destroyed us
		return;
	}

	bool MetadataDirty = false;
	auto & Random = GetRandomProvider();

	if (!m_bIsMouthOpen)
	{
		if (Random.RandBool(0.02))
		{
			MetadataDirty = true;
			m_bIsMouthOpen = true;
		}
	}
	else
	{
		if (Random.RandBool(0.10))
		{
			MetadataDirty = true;
			m_bIsMouthOpen = false;
		}
	}

	if ((m_Attachee != nullptr) && (!m_bIsTame))
	{
		if (m_TameAttemptTimes < m_TimesToTame)
		{
			if (Random.RandBool(0.02))
			{
				m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, GetPosition().Floor(), int(SmokeDirection::SOUTH_EAST));
				m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, GetPosition().Floor(), int(SmokeDirection::SOUTH_WEST));
				m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, GetPosition().Floor(), int(SmokeDirection::NORTH_EAST));
				m_World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, GetPosition().Floor(), int(SmokeDirection::NORTH_WEST));

				m_World->BroadcastSoundEffect("entity.horse.angry", GetPosition(), 1.0f, 1.0f);
				m_Attachee->Detach();
				MetadataDirty = true;
				m_bIsRearing = true;
			}
		}
		else
		{
			m_World->BroadcastParticleEffect("heart", static_cast<Vector3f>(GetPosition()), Vector3f{}, 0, 5);
			MetadataDirty = true;
			m_bIsTame = true;
		}
	}

	if (m_bIsRearing)
	{
		if (m_RearTickCount == 20)
		{
			MetadataDirty = true;
			m_bIsRearing = false;
			m_RearTickCount = 0;
		}
		else
		{
			m_RearTickCount++;
		}
	}

	if (MetadataDirty)
	{
		m_World->BroadcastEntityMetadata(*this);
	}
}

void cHorse::OnRightClicked(cPlayer & a_Player)
{
	Super::OnRightClicked(a_Player);

	if (m_bIsTame)
	{
		if (a_Player.IsCrouched())
		{
			PlayerOpenWindow(a_Player);
			return;
		}

		auto EquipedItemType = a_Player.GetEquippedItem().m_ItemType;

		if (
			!IsSaddled() &&
			(
				(EquipedItemType == E_ITEM_SADDLE) ||
				ItemCategory::IsHorseArmor(EquipedItemType)
			)
		)
		{
			// Player is holding a horse inventory item, open the window:
			PlayerOpenWindow(a_Player);
		}
		else
		{
			a_Player.AttachTo(this);
		}
	}
	else if (a_Player.GetEquippedItem().IsEmpty())
	{
		// Check if leashed / unleashed to player before try to ride
		if (!m_IsLeashActionJustDone)
		{
			if (m_Attachee != nullptr)
			{
				if (m_Attachee->GetUniqueID() == a_Player.GetUniqueID())
				{
					a_Player.Detach();
					return;
				}

				if (m_Attachee->IsPlayer())
				{
					return;
				}

				m_Attachee->Detach();
			}

			m_TameAttemptTimes++;
			a_Player.AttachTo(this);
		}
	}
	else
	{
		m_bIsRearing = true;
		m_RearTickCount = 0;
		m_World->BroadcastSoundEffect("entity.horse.angry", GetPosition(), 1.0f, 0.8f);
	}
}

void cHorse::SetHorseSaddle(cItem a_Saddle)
{
	if (a_Saddle.m_ItemType == E_ITEM_SADDLE)
	{
		m_World->BroadcastSoundEffect("entity.horse.saddle", GetPosition(), 1.0f, 0.8f);
	}
	else if (!a_Saddle.IsEmpty())
	{
		return;  // Invalid item
	}

	m_Saddle = std::move(a_Saddle);
	m_World->BroadcastEntityMetadata(*this);
}

void cHorse::SetHorseArmor(cItem a_Armor)
{
	if (ItemCategory::IsHorseArmor(a_Armor.m_ItemType))
	{
		m_World->BroadcastSoundEffect("entity.horse.armor", GetPosition(), 1.0f, 0.8f);
	}
	else if (!a_Armor.IsEmpty())
	{
		return;  // Invalid item
	}

	m_Armor = std::move(a_Armor);
	m_World->BroadcastEntityMetadata(*this);
}

int cHorse::GetHorseArmour(void) const
{
	switch (m_Armor.m_ItemType)
	{
		case E_ITEM_EMPTY:               return 0;
		case E_ITEM_IRON_HORSE_ARMOR:    return 1;
		case E_ITEM_GOLD_HORSE_ARMOR:    return 2;
		case E_ITEM_DIAMOND_HORSE_ARMOR: return 3;

		default:
		{
			LOGWARN("cHorse::GetHorseArmour: Invalid armour item (%d)", m_Armor.m_ItemType);
			return 0;
		}
	}
}

void cHorse::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
	if (IsBaby())
	{
		return;  // Babies don't drop items
	}

	unsigned int LootingLevel = 0;
	if (a_Killer != nullptr)
	{
		LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
	}
	AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_LEATHER);
	if (IsSaddled())
	{
		a_Drops.push_back(m_Saddle);
	}
	if (!m_Armor.IsEmpty())
	{
		a_Drops.push_back(m_Armor);
	}
}

void cHorse::InStateIdle(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
	// If horse is tame and someone is sitting on it, don't walk around
	if ((!m_bIsTame) || (m_Attachee == nullptr))
	{
		Super::InStateIdle(a_Dt, a_Chunk);
	}
}

void cHorse::HandleSpeedFromAttachee(float a_Forward, float a_Sideways)
{
	if ((m_bIsTame) && IsSaddled())
	{
		Super::HandleSpeedFromAttachee(a_Forward * m_MaxSpeed, a_Sideways * m_MaxSpeed);
	}
}

void cHorse::PlayerOpenWindow(cPlayer & a_Player)
{
	auto Window = GetWindow();
	if (Window == nullptr)
	{
		Window = new cHorseWindow(*this);
		OpenWindow(Window);
	}

	a_Player.OpenWindow(*Window);
}
<|endoftext|>