<commit_before>#define NOMINMAX
#include "dsb/bus/execution_state.hpp"

#include <algorithm>
#include <limits>
#include <stdexcept>
#include <string>

#include "dsb/bus/execution_manager_private.hpp"
#include "dsb/bus/slave_control_messenger.hpp"
#include "dsb/bus/slave_controller.hpp"
#include "dsb/util.hpp"

namespace dsb
{
namespace bus
{

// =============================================================================

void ConfigExecutionState::Terminate(ExecutionManagerPrivate& self)
{
    self.DoTerminate();
}

void ConfigExecutionState::BeginConfig(
    ExecutionManagerPrivate& /*self*/,
    ExecutionManager::BeginConfigHandler onComplete)
{
    // Do nothing, we're already here.
    onComplete(std::error_code());
}

void ConfigExecutionState::EndConfig(
    ExecutionManagerPrivate& self,
    ExecutionManager::BeginConfigHandler onComplete)
{
    self.SwapState(
        std::make_unique<PrimingExecutionState>(std::move(onComplete)));
}

void ConfigExecutionState::SetSimulationTime(
    ExecutionManagerPrivate& self,
    dsb::model::TimePoint startTime,
    dsb::model::TimePoint stopTime)
{
    DSB_PRECONDITION_CHECK(self.slaves.empty());
    DSB_INPUT_CHECK(startTime <= stopTime);
    self.slaveSetup.startTime = startTime;
    self.slaveSetup.stopTime = stopTime;
}

// NOTE:
// None of the per-slave operation completion handlers in the CONFIG state
// should capture the 'this' pointer of the ConfigExecutionState object.
// The reason is that if the user calls EndConfig() while operations are still
// pending, the operations will not complete in this state, but in the PRIMING
// state.  Hence, the object will have been deleted and replaced with a
// PrimingExecutionState object.

dsb::model::SlaveID ConfigExecutionState::AddSlave(
    ExecutionManagerPrivate& self,
    const dsb::net::SlaveLocator& slaveLocator,
    dsb::comm::Reactor& reactor,
    boost::chrono::milliseconds timeout,
    ExecutionManager::AddSlaveHandler onComplete)
{
    DSB_INPUT_CHECK(!!onComplete);
    if (self.lastSlaveID == std::numeric_limits<dsb::model::SlaveID>::max()) {
        throw std::length_error("Maximum number of slaves reached");
    }
    const auto id = ++self.lastSlaveID;
    const auto selfPtr = &self;
    self.slaves.insert(std::make_pair(id, std::make_unique<dsb::bus::SlaveController>(
        reactor,
        slaveLocator,
        id,
        self.slaveSetup,
        timeout,
        [id, onComplete, selfPtr] (const std::error_code& ec) {
            if (!ec) {
                onComplete(std::error_code(), id);
            } else {
                selfPtr->slaves[id]->Close();
                selfPtr->slaves.erase(id);
                onComplete(ec, dsb::model::INVALID_SLAVE_ID);
            }
            selfPtr->SlaveOpComplete();
        })));
    selfPtr->SlaveOpStarted();
    return id;
}

void ConfigExecutionState::SetVariables(
    ExecutionManagerPrivate& self,
    dsb::model::SlaveID slave,
    const std::vector<dsb::model::VariableSetting>& settings,
    boost::chrono::milliseconds timeout,
    ExecutionManager::SetVariablesHandler onComplete)
{
    const auto selfPtr = &self;
    self.slaves.at(slave)->SetVariables(
        settings,
        timeout,
        [onComplete, selfPtr](const std::error_code& ec) {
            const auto onExit = dsb::util::OnScopeExit([=]() {
                selfPtr->SlaveOpComplete();
            });
            onComplete(ec);
        });
    self.SlaveOpStarted();
}

// =============================================================================

PrimingExecutionState::PrimingExecutionState(
    ExecutionManager::EndConfigHandler onComplete)
    : m_onComplete(std::move(onComplete))
{
}

void PrimingExecutionState::StateEntered(ExecutionManagerPrivate& self)
{
    const auto selfPtr = &self;
    self.WhenAllSlaveOpsComplete([selfPtr, this] (const std::error_code& ec) {
        assert(!ec);
        const auto keepMeAlive = selfPtr->SwapState(std::make_unique<ReadyExecutionState>());
        assert(keepMeAlive.get() == this);
        dsb::util::MoveAndCall(m_onComplete, std::error_code());
   });
}

// =============================================================================

void ReadyExecutionState::Terminate(ExecutionManagerPrivate& self)
{
    self.DoTerminate();
}

void ReadyExecutionState::BeginConfig(
    ExecutionManagerPrivate& self,
    ExecutionManager::BeginConfigHandler onComplete)
{
    self.SwapState(std::make_unique<ConfigExecutionState>());
    onComplete(std::error_code());
}

void ReadyExecutionState::Step(
    ExecutionManagerPrivate& self,
    dsb::model::TimeDuration stepSize,
    boost::chrono::milliseconds timeout,
    ExecutionManager::StepHandler onComplete,
    ExecutionManager::SlaveStepHandler onSlaveStepComplete)
{
    self.SwapState(std::make_unique<SteppingExecutionState>(
        stepSize, timeout, std::move(onComplete), std::move(onSlaveStepComplete)));
}

// =============================================================================

SteppingExecutionState::SteppingExecutionState(
    dsb::model::TimeDuration stepSize,
    boost::chrono::milliseconds timeout,
    ExecutionManager::StepHandler onComplete,
    ExecutionManager::SlaveStepHandler onSlaveStepComplete)
    : m_stepSize(stepSize),
      m_timeout(timeout),
      m_onComplete(std::move(onComplete)),
      m_onSlaveStepComplete(std::move(onSlaveStepComplete))
{
}

void SteppingExecutionState::StateEntered(ExecutionManagerPrivate& self)
{
    const auto stepID = self.NextStepID();
    const auto selfPtr = &self; // Because we can't capture references in lambdas
    for (auto it = begin(self.slaves); it != end(self.slaves); ++it) {
        const auto slaveID = it->first;
        it->second->Step(
            stepID,
            self.CurrentSimTime(),
            m_stepSize,
            m_timeout,
            [selfPtr, slaveID, this] (const std::error_code& ec) {
                const auto onExit = dsb::util::OnScopeExit([=]() {
                    selfPtr->SlaveOpComplete();
                });
                if (m_onSlaveStepComplete) m_onSlaveStepComplete(ec, slaveID);
            });
        self.SlaveOpStarted();
    }
    self.WhenAllSlaveOpsComplete([selfPtr, this] (const std::error_code& ec) {
        assert(!ec);
        bool stepFailed = false;
        bool fatalError = false;
        for (auto it = begin(selfPtr->slaves); it != end(selfPtr->slaves); ++it) {
            if (it->second->State() == SLAVE_STEP_OK) {
                // do nothing
            } else if (it->second->State() == SLAVE_STEP_FAILED) {
                stepFailed = true;
            } else {
                assert(it->second->State() == SLAVE_NOT_CONNECTED);
                fatalError = true;
                break; // because there's no point in continuing
            }
        }
        if (fatalError) {
            const auto keepMeAlive = selfPtr->SwapState(
                std::make_unique<FatalErrorExecutionState>());
            assert(keepMeAlive.get() == this);
            m_onComplete(make_error_code(dsb::error::generic_error::operation_failed));
        } else if (stepFailed) {
            const auto keepMeAlive = selfPtr->SwapState(
                std::make_unique<StepFailedExecutionState>());
            assert(keepMeAlive.get() == this);
            m_onComplete(dsb::error::sim_error::cannot_perform_timestep);
        } else {
            const auto keepMeAlive = selfPtr->SwapState(
                std::make_unique<StepOkExecutionState>(m_stepSize));
            assert(keepMeAlive.get() == this);
            m_onComplete(std::error_code());
        }
    });
}

// =============================================================================

StepOkExecutionState::StepOkExecutionState(dsb::model::TimeDuration stepSize)
    : m_stepSize(stepSize)
{
}

void StepOkExecutionState::Terminate(ExecutionManagerPrivate& self)
{
    self.DoTerminate();
}

void StepOkExecutionState::AcceptStep(
    ExecutionManagerPrivate& self,
    boost::chrono::milliseconds timeout,
    ExecutionManager::AcceptStepHandler onComplete,
    ExecutionManager::SlaveAcceptStepHandler onSlaveAcceptStepComplete)
{
    self.AdvanceSimTime(m_stepSize);
    self.SwapState(std::make_unique<AcceptingExecutionState>(
        timeout, std::move(onComplete), std::move(onSlaveAcceptStepComplete)));
}

// =============================================================================

AcceptingExecutionState::AcceptingExecutionState(
    boost::chrono::milliseconds timeout,
    ExecutionManager::AcceptStepHandler onComplete,
    ExecutionManager::SlaveAcceptStepHandler onSlaveAcceptStepComplete)
    : m_timeout(timeout),
      m_onComplete(std::move(onComplete)),
      m_onSlaveAcceptStepComplete(std::move(onSlaveAcceptStepComplete))
{
}

void AcceptingExecutionState::StateEntered(ExecutionManagerPrivate& self)
{
    const auto selfPtr = &self; // Because we can't capture references in lambdas
    for (auto it = begin(self.slaves); it != end(self.slaves); ++it) {
        const auto slaveID = it->first;
        it->second->AcceptStep(
            m_timeout,
            [selfPtr, slaveID, this] (const std::error_code& ec) {
                const auto onExit = dsb::util::OnScopeExit([=]() {
                    selfPtr->SlaveOpComplete();
                });
                if (m_onSlaveAcceptStepComplete) {
                    m_onSlaveAcceptStepComplete(ec, slaveID);
                }
            });
        self.SlaveOpStarted();
    }
    self.WhenAllSlaveOpsComplete([selfPtr, this] (const std::error_code& ec) {
        assert(!ec);
        bool error = false;
        for (auto it = begin(selfPtr->slaves); it != end(selfPtr->slaves); ++it) {
            if (it->second->State() != SLAVE_READY) {
                assert(it->second->State() == SLAVE_NOT_CONNECTED);
                error = true;
                break;
            }
        }
        if (error) {
            const auto keepMeAlive = selfPtr->SwapState(
                std::make_unique<FatalErrorExecutionState>());
            assert(keepMeAlive.get() == this);
            m_onComplete(make_error_code(dsb::error::generic_error::operation_failed));
            return;
        } else {
            const auto keepMeAlive = selfPtr->SwapState(
                std::make_unique<ReadyExecutionState>());
            assert(keepMeAlive.get() == this);
            m_onComplete(std::error_code());
        }
    });
}

// =============================================================================

void StepFailedExecutionState::Terminate(ExecutionManagerPrivate& self)
{
    self.DoTerminate();
}

// =============================================================================

void FatalErrorExecutionState::Terminate(ExecutionManagerPrivate& self)
{
    self.DoTerminate();
}

// =============================================================================

void TerminatedExecutionState::Terminate(ExecutionManagerPrivate& self)
{
    // Do nothing, we're already here.
}

}} // namespace
<commit_msg>Postpone slave list cleanup until EndConfig()<commit_after>#define NOMINMAX
#include "dsb/bus/execution_state.hpp"

#include <algorithm>
#include <limits>
#include <stdexcept>
#include <string>

#include "dsb/bus/execution_manager_private.hpp"
#include "dsb/bus/slave_control_messenger.hpp"
#include "dsb/bus/slave_controller.hpp"
#include "dsb/util.hpp"

namespace dsb
{
namespace bus
{

// =============================================================================

void ConfigExecutionState::Terminate(ExecutionManagerPrivate& self)
{
    self.DoTerminate();
}

void ConfigExecutionState::BeginConfig(
    ExecutionManagerPrivate& /*self*/,
    ExecutionManager::BeginConfigHandler onComplete)
{
    // Do nothing, we're already here.
    onComplete(std::error_code());
}

void ConfigExecutionState::EndConfig(
    ExecutionManagerPrivate& self,
    ExecutionManager::BeginConfigHandler onComplete)
{
    self.SwapState(
        std::make_unique<PrimingExecutionState>(std::move(onComplete)));
}

void ConfigExecutionState::SetSimulationTime(
    ExecutionManagerPrivate& self,
    dsb::model::TimePoint startTime,
    dsb::model::TimePoint stopTime)
{
    DSB_PRECONDITION_CHECK(self.slaves.empty());
    DSB_INPUT_CHECK(startTime <= stopTime);
    self.slaveSetup.startTime = startTime;
    self.slaveSetup.stopTime = stopTime;
}

// NOTE:
// None of the per-slave operation completion handlers in the CONFIG state
// should capture the 'this' pointer of the ConfigExecutionState object.
// The reason is that if the user calls EndConfig() while operations are still
// pending, the operations will not complete in this state, but in the PRIMING
// state.  Hence, the object will have been deleted and replaced with a
// PrimingExecutionState object.

dsb::model::SlaveID ConfigExecutionState::AddSlave(
    ExecutionManagerPrivate& self,
    const dsb::net::SlaveLocator& slaveLocator,
    dsb::comm::Reactor& reactor,
    boost::chrono::milliseconds timeout,
    ExecutionManager::AddSlaveHandler onComplete)
{
    DSB_INPUT_CHECK(!!onComplete);
    if (self.lastSlaveID == std::numeric_limits<dsb::model::SlaveID>::max()) {
        throw std::length_error("Maximum number of slaves reached");
    }
    const auto id = ++self.lastSlaveID;
    const auto selfPtr = &self;
    self.slaves.insert(std::make_pair(id, std::make_unique<dsb::bus::SlaveController>(
        reactor,
        slaveLocator,
        id,
        self.slaveSetup,
        timeout,
        [id, onComplete, selfPtr] (const std::error_code& ec) {
            if (!ec) {
                onComplete(std::error_code(), id);
            } else {
                onComplete(ec, dsb::model::INVALID_SLAVE_ID);
            }
            selfPtr->SlaveOpComplete();
        })));
    selfPtr->SlaveOpStarted();
    return id;
}

void ConfigExecutionState::SetVariables(
    ExecutionManagerPrivate& self,
    dsb::model::SlaveID slave,
    const std::vector<dsb::model::VariableSetting>& settings,
    boost::chrono::milliseconds timeout,
    ExecutionManager::SetVariablesHandler onComplete)
{
    const auto selfPtr = &self;
    self.slaves.at(slave)->SetVariables(
        settings,
        timeout,
        [onComplete, selfPtr](const std::error_code& ec) {
            const auto onExit = dsb::util::OnScopeExit([=]() {
                selfPtr->SlaveOpComplete();
            });
            onComplete(ec);
        });
    self.SlaveOpStarted();
}

// =============================================================================

PrimingExecutionState::PrimingExecutionState(
    ExecutionManager::EndConfigHandler onComplete)
    : m_onComplete(std::move(onComplete))
{
}

void PrimingExecutionState::StateEntered(ExecutionManagerPrivate& self)
{
    const auto selfPtr = &self;
    self.WhenAllSlaveOpsComplete([selfPtr, this] (const std::error_code& ec) {
        assert(!ec);

        // Garbage collection: Remove all slave controllers whose connection
        // has failed or been canceled.
        for (auto it = begin(selfPtr->slaves); it != end(selfPtr->slaves); ) {
            if (it->second->State() == SLAVE_NOT_CONNECTED) {
                it = selfPtr->slaves.erase(it);
            } else {
                ++it;
            }
        }

        const auto keepMeAlive = selfPtr->SwapState(std::make_unique<ReadyExecutionState>());
        assert(keepMeAlive.get() == this);
        dsb::util::MoveAndCall(m_onComplete, std::error_code());
   });
}

// =============================================================================

void ReadyExecutionState::Terminate(ExecutionManagerPrivate& self)
{
    self.DoTerminate();
}

void ReadyExecutionState::BeginConfig(
    ExecutionManagerPrivate& self,
    ExecutionManager::BeginConfigHandler onComplete)
{
    self.SwapState(std::make_unique<ConfigExecutionState>());
    onComplete(std::error_code());
}

void ReadyExecutionState::Step(
    ExecutionManagerPrivate& self,
    dsb::model::TimeDuration stepSize,
    boost::chrono::milliseconds timeout,
    ExecutionManager::StepHandler onComplete,
    ExecutionManager::SlaveStepHandler onSlaveStepComplete)
{
    self.SwapState(std::make_unique<SteppingExecutionState>(
        stepSize, timeout, std::move(onComplete), std::move(onSlaveStepComplete)));
}

// =============================================================================

SteppingExecutionState::SteppingExecutionState(
    dsb::model::TimeDuration stepSize,
    boost::chrono::milliseconds timeout,
    ExecutionManager::StepHandler onComplete,
    ExecutionManager::SlaveStepHandler onSlaveStepComplete)
    : m_stepSize(stepSize),
      m_timeout(timeout),
      m_onComplete(std::move(onComplete)),
      m_onSlaveStepComplete(std::move(onSlaveStepComplete))
{
}

void SteppingExecutionState::StateEntered(ExecutionManagerPrivate& self)
{
    const auto stepID = self.NextStepID();
    const auto selfPtr = &self; // Because we can't capture references in lambdas
    for (auto it = begin(self.slaves); it != end(self.slaves); ++it) {
        const auto slaveID = it->first;
        it->second->Step(
            stepID,
            self.CurrentSimTime(),
            m_stepSize,
            m_timeout,
            [selfPtr, slaveID, this] (const std::error_code& ec) {
                const auto onExit = dsb::util::OnScopeExit([=]() {
                    selfPtr->SlaveOpComplete();
                });
                if (m_onSlaveStepComplete) m_onSlaveStepComplete(ec, slaveID);
            });
        self.SlaveOpStarted();
    }
    self.WhenAllSlaveOpsComplete([selfPtr, this] (const std::error_code& ec) {
        assert(!ec);
        bool stepFailed = false;
        bool fatalError = false;
        for (auto it = begin(selfPtr->slaves); it != end(selfPtr->slaves); ++it) {
            if (it->second->State() == SLAVE_STEP_OK) {
                // do nothing
            } else if (it->second->State() == SLAVE_STEP_FAILED) {
                stepFailed = true;
            } else {
                assert(it->second->State() == SLAVE_NOT_CONNECTED);
                fatalError = true;
                break; // because there's no point in continuing
            }
        }
        if (fatalError) {
            const auto keepMeAlive = selfPtr->SwapState(
                std::make_unique<FatalErrorExecutionState>());
            assert(keepMeAlive.get() == this);
            m_onComplete(make_error_code(dsb::error::generic_error::operation_failed));
        } else if (stepFailed) {
            const auto keepMeAlive = selfPtr->SwapState(
                std::make_unique<StepFailedExecutionState>());
            assert(keepMeAlive.get() == this);
            m_onComplete(dsb::error::sim_error::cannot_perform_timestep);
        } else {
            const auto keepMeAlive = selfPtr->SwapState(
                std::make_unique<StepOkExecutionState>(m_stepSize));
            assert(keepMeAlive.get() == this);
            m_onComplete(std::error_code());
        }
    });
}

// =============================================================================

StepOkExecutionState::StepOkExecutionState(dsb::model::TimeDuration stepSize)
    : m_stepSize(stepSize)
{
}

void StepOkExecutionState::Terminate(ExecutionManagerPrivate& self)
{
    self.DoTerminate();
}

void StepOkExecutionState::AcceptStep(
    ExecutionManagerPrivate& self,
    boost::chrono::milliseconds timeout,
    ExecutionManager::AcceptStepHandler onComplete,
    ExecutionManager::SlaveAcceptStepHandler onSlaveAcceptStepComplete)
{
    self.AdvanceSimTime(m_stepSize);
    self.SwapState(std::make_unique<AcceptingExecutionState>(
        timeout, std::move(onComplete), std::move(onSlaveAcceptStepComplete)));
}

// =============================================================================

AcceptingExecutionState::AcceptingExecutionState(
    boost::chrono::milliseconds timeout,
    ExecutionManager::AcceptStepHandler onComplete,
    ExecutionManager::SlaveAcceptStepHandler onSlaveAcceptStepComplete)
    : m_timeout(timeout),
      m_onComplete(std::move(onComplete)),
      m_onSlaveAcceptStepComplete(std::move(onSlaveAcceptStepComplete))
{
}

void AcceptingExecutionState::StateEntered(ExecutionManagerPrivate& self)
{
    const auto selfPtr = &self; // Because we can't capture references in lambdas
    for (auto it = begin(self.slaves); it != end(self.slaves); ++it) {
        const auto slaveID = it->first;
        it->second->AcceptStep(
            m_timeout,
            [selfPtr, slaveID, this] (const std::error_code& ec) {
                const auto onExit = dsb::util::OnScopeExit([=]() {
                    selfPtr->SlaveOpComplete();
                });
                if (m_onSlaveAcceptStepComplete) {
                    m_onSlaveAcceptStepComplete(ec, slaveID);
                }
            });
        self.SlaveOpStarted();
    }
    self.WhenAllSlaveOpsComplete([selfPtr, this] (const std::error_code& ec) {
        assert(!ec);
        bool error = false;
        for (auto it = begin(selfPtr->slaves); it != end(selfPtr->slaves); ++it) {
            if (it->second->State() != SLAVE_READY) {
                assert(it->second->State() == SLAVE_NOT_CONNECTED);
                error = true;
                break;
            }
        }
        if (error) {
            const auto keepMeAlive = selfPtr->SwapState(
                std::make_unique<FatalErrorExecutionState>());
            assert(keepMeAlive.get() == this);
            m_onComplete(make_error_code(dsb::error::generic_error::operation_failed));
            return;
        } else {
            const auto keepMeAlive = selfPtr->SwapState(
                std::make_unique<ReadyExecutionState>());
            assert(keepMeAlive.get() == this);
            m_onComplete(std::error_code());
        }
    });
}

// =============================================================================

void StepFailedExecutionState::Terminate(ExecutionManagerPrivate& self)
{
    self.DoTerminate();
}

// =============================================================================

void FatalErrorExecutionState::Terminate(ExecutionManagerPrivate& self)
{
    self.DoTerminate();
}

// =============================================================================

void TerminatedExecutionState::Terminate(ExecutionManagerPrivate& self)
{
    // Do nothing, we're already here.
}

}} // namespace
<|endoftext|>


<commit_before>/*!
 * \file int_path.hpp
 * \brief file int_path.hpp
 *
 * Copyright 2017 by Intel.
 *
 * Contact: kevin.rogovin@intel.com
 *
 * This Source Code Form is subject to the
 * terms of the Mozilla Public License, v. 2.0.
 * If a copy of the MPL was not distributed with
 * this file, You can obtain one at
 * http://mozilla.org/MPL/2.0/.
 *
 * \author Kevin Rogovin <kevin.rogovin@intel.com>
 *
 */

#pragma once

#include <fastuidraw/util/util.hpp>
#include <fastuidraw/util/c_array.hpp>
#include <fastuidraw/util/vecN.hpp>
#include <fastuidraw/util/reference_counted.hpp>
#include <fastuidraw/util/fastuidraw_memory.hpp>
#include <fastuidraw/path.hpp>
#include <fastuidraw/text/glyph_render_data_curve_pair.hpp>
#include <fastuidraw/text/glyph_render_data_distance_field.hpp>

#include "array2d.hpp"
#include "bounding_box.hpp"
#include "util_private.hpp"

namespace fastuidraw
{
  namespace detail
  {
    class IntBezierCurve
    {
    public:

      template<typename T>
      class transformation
      {
      public:
        transformation(T sc = T(1), vecN<T, 2> tr = vecN<T, 2>(T(0))):
          m_scale(sc),
          m_translate(tr)
        {}

        T
        scale(void) const
        {
          return m_scale;
        }

        vecN<T, 2>
        translate(void) const
        {
          return m_translate;
        }

        template<typename U>
        transformation<U>
        cast(void) const
        {
          return transformation<U>(U(m_scale), vecN<U, 2>(m_translate));
        }

        vecN<T, 2>
        operator()(vecN<T, 2> p) const
        {
          return m_translate + m_scale * p;
        }

      private:
        T m_scale;
        vecN<T, 2> m_translate;
      };

      class ID_t
      {
      public:
        ID_t(void):
          m_contourID(-1),
          m_curveID(-1)
        {}

        unsigned int m_contourID;
        unsigned int m_curveID;
      };

      IntBezierCurve(const ID_t &pID, const IntBezierCurve &curve):
        m_ID(pID),
        m_control_pts(curve.m_control_pts),
        m_num_control_pts(curve.m_num_control_pts),
        m_as_polynomial_fcn(curve.m_as_polynomial_fcn),
        m_derivatives_cancel(curve.m_derivatives_cancel),
        m_bb(curve.m_bb)
      {
      }

      IntBezierCurve(const ID_t &pID, const ivec2 &pt0, const ivec2 &pt1):
        m_ID(pID),
        m_control_pts(pt0, pt1, ivec2(), ivec2()),
        m_num_control_pts(2)
      {
        process_control_pts();
      }

      IntBezierCurve(const ID_t &pID, const ivec2 &pt0, const ivec2 &ct,
                     const ivec2 &pt1):
        m_ID(pID),
        m_control_pts(pt0, ct, pt1, ivec2()),
        m_num_control_pts(3)
      {
        process_control_pts();
      }

      IntBezierCurve(const ID_t &pID, const ivec2 &pt0, const ivec2 &ct0,
                     const ivec2 &ct1, const ivec2 &pt1):
        m_ID(pID),
        m_control_pts(pt0, ct0, ct1, pt1),
        m_num_control_pts(4)
      {
        process_control_pts();
      }

      ~IntBezierCurve()
      {}

      const ID_t&
      ID(void) const
      {
        return m_ID;
      }

      const_c_array<ivec2>
      control_pts(void) const
      {
        return const_c_array<ivec2>(m_control_pts.c_ptr(), m_num_control_pts);
      }

      const BoundingBox<int>&
      bounding_box(void) const
      {
        return m_bb;
      }

      BoundingBox<int>
      bounding_box(const transformation<int> &tr) const
      {
        BoundingBox<int> R;
        R.union_point(tr(m_bb.min_point()));
        R.union_point(tr(m_bb.max_point()));
        return R;
      }

      static
      bool
      are_ordered_neighbors(const IntBezierCurve &curve0,
                            const IntBezierCurve &curve1)
      {
        return curve0.control_pts().back() == curve1.control_pts().front();
      }

      int
      degree(void) const
      {
        FASTUIDRAWassert(m_num_control_pts > 0);
        return m_num_control_pts - 1;
      }

      const_c_array<vec2>
      derivatives_cancel(void) const
      {
        return const_c_array<vec2>(m_derivatives_cancel.c_ptr(),
                                   m_num_derivatives_cancel);
      }

      const_c_array<int>
      as_polynomial(int coord) const
      {
        return const_c_array<int>(m_as_polynomial_fcn[coord].c_ptr(), m_num_control_pts);
      }

      vecN<const_c_array<int>, 2>
      as_polynomial(void) const
      {
        return vecN<const_c_array<int>, 2>(as_polynomial(0), as_polynomial(1));
      }

      vec2
      eval(float t) const;

    private:
      void
      process_control_pts(void);

      void
      compute_derivatives_cancel_pts(void);

      c_array<int>
      as_polynomial(int coord)
      {
        return c_array<int>(m_as_polynomial_fcn[coord].c_ptr(), m_num_control_pts);
      }

      ID_t m_ID;
      vecN<ivec2, 4> m_control_pts;
      unsigned int m_num_control_pts;
      vecN<ivec4, 2> m_as_polynomial_fcn;

      // where dx/dt +- dy/dt = 0
      vecN<vec2, 6> m_derivatives_cancel;
      unsigned int m_num_derivatives_cancel;
      BoundingBox<int> m_bb;
    };

    class IntContour
    {
    public:
      IntContour(void)
      {}

      void
      add_curve(const IntBezierCurve &curve)
      {
        FASTUIDRAWassert(m_curves.empty() || IntBezierCurve::are_ordered_neighbors(m_curves.back(), curve));
        m_curves.push_back(curve);
      }

      bool
      closed(void)
      {
        return !m_curves.empty()
          && IntBezierCurve::are_ordered_neighbors(m_curves.back(), m_curves.front());
      }

      const std::vector<IntBezierCurve>&
      curves(void) const
      {
        return m_curves;
      }

      const IntBezierCurve&
      curve(unsigned int curveID) const
      {
        FASTUIDRAWassert(curveID < m_curves.size());
        return m_curves[curveID];
      }

      /* Filter the Contour as follows:
          1. Collapse any curves that are within a texel
          2. Curves of tiny curvature are realized as a line
          3. Cubics are broken into quadratics
         The transformation tr is -NOT- applied to the contour,
         it is used as the transformation from IntContour
         coordinates to texel coordinates. The value of texel_size
         gives the size of a texel with the texel at (0, 0)
         starting at (0, 0) [in texel coordinates].
       */
      void
      filter(float curvature_collapse,
             const IntBezierCurve::transformation<int> &tr, ivec2 texel_size);

      void
      add_to_path(const IntBezierCurve::transformation<float> &tr, Path *dst) const;

    private:
      std::vector<IntBezierCurve> m_curves;
    };

    class IntPath
    {
    public:
      IntPath(void)
      {}

      void
      move_to(const ivec2 &pt);

      void
      line_to(const ivec2 &pt);

      void
      conic_to(const ivec2 &control_pt, const ivec2 &pt);

      void
      cubic_to(const ivec2 &control_pt0, const ivec2 &control_pt1, const ivec2 &pt);

      bool
      empty(void) const
      {
        return m_contours.empty();
      }

      void
      add_to_path(const IntBezierCurve::transformation<float> &tr, Path *dst) const;

      /* Filter the Path as follows:
          1. Collapse any curves that are within a texel
          2. Curves of tiny curvature are realized as a line
          3. Cubics are broken into quadratics
         The transformation tr is -NOT- applied to the contour,
         it is used as the transformation from IntContour
         coordinates to texel coordinates. The value of texel_size
         gives the size of a texel with the texel at (0, 0)
         starting at (0, 0) [in texel coordinates].
       */
      void
      filter(float curvature_collapse,
             const IntBezierCurve::transformation<int> &tr, ivec2 texel_size);

      /* extract render data the location of pixel(i, j) is at
         (step.x() * i, step.y() * j). The resolution is (count.x(), count.y())
         and the values extracted are those values from the IntPath
         transformed by tr. The max-distance values is in units
         of the IntPath with the transformation tr applied
       */
      void
      extract_render_data(const ivec2 &step, const ivec2 &count,
                          float max_distance,
                          IntBezierCurve::transformation<int> tr,
                          GlyphRenderDataDistanceField *dst) const;

      /* extract render data the location of pixel(i, j) is at
         (step.x() * i, step.y() * j). The resolution is (count.x(), count.y())
         and the values extracted are those values from the IntPath
         transformed by tr.

         The path should have filter() applied to it so that there
         are no cubic paths and that small paths are dropped
       */
      void
      extract_render_data(const ivec2 &step, const ivec2 &count,
                          IntBezierCurve::transformation<int> tr,
                          GlyphRenderDataCurvePair *dst) const;

    private:
      IntBezierCurve::ID_t
      computeID(void);

      fastuidraw::ivec2 m_last_pt;
      std::vector<IntContour> m_contours;
    };

  } //namespace fastuidraw
} //namespace detail
<commit_msg>fastuidraw/private/int_path: better descriptions for extract_render_data() methods<commit_after>/*!
 * \file int_path.hpp
 * \brief file int_path.hpp
 *
 * Copyright 2017 by Intel.
 *
 * Contact: kevin.rogovin@intel.com
 *
 * This Source Code Form is subject to the
 * terms of the Mozilla Public License, v. 2.0.
 * If a copy of the MPL was not distributed with
 * this file, You can obtain one at
 * http://mozilla.org/MPL/2.0/.
 *
 * \author Kevin Rogovin <kevin.rogovin@intel.com>
 *
 */

#pragma once

#include <fastuidraw/util/util.hpp>
#include <fastuidraw/util/c_array.hpp>
#include <fastuidraw/util/vecN.hpp>
#include <fastuidraw/util/reference_counted.hpp>
#include <fastuidraw/util/fastuidraw_memory.hpp>
#include <fastuidraw/path.hpp>
#include <fastuidraw/text/glyph_render_data_curve_pair.hpp>
#include <fastuidraw/text/glyph_render_data_distance_field.hpp>

#include "array2d.hpp"
#include "bounding_box.hpp"
#include "util_private.hpp"

namespace fastuidraw
{
  namespace detail
  {
    class IntBezierCurve
    {
    public:

      template<typename T>
      class transformation
      {
      public:
        transformation(T sc = T(1), vecN<T, 2> tr = vecN<T, 2>(T(0))):
          m_scale(sc),
          m_translate(tr)
        {}

        T
        scale(void) const
        {
          return m_scale;
        }

        vecN<T, 2>
        translate(void) const
        {
          return m_translate;
        }

        template<typename U>
        transformation<U>
        cast(void) const
        {
          return transformation<U>(U(m_scale), vecN<U, 2>(m_translate));
        }

        vecN<T, 2>
        operator()(vecN<T, 2> p) const
        {
          return m_translate + m_scale * p;
        }

      private:
        T m_scale;
        vecN<T, 2> m_translate;
      };

      class ID_t
      {
      public:
        ID_t(void):
          m_contourID(-1),
          m_curveID(-1)
        {}

        unsigned int m_contourID;
        unsigned int m_curveID;
      };

      IntBezierCurve(const ID_t &pID, const IntBezierCurve &curve):
        m_ID(pID),
        m_control_pts(curve.m_control_pts),
        m_num_control_pts(curve.m_num_control_pts),
        m_as_polynomial_fcn(curve.m_as_polynomial_fcn),
        m_derivatives_cancel(curve.m_derivatives_cancel),
        m_bb(curve.m_bb)
      {
      }

      IntBezierCurve(const ID_t &pID, const ivec2 &pt0, const ivec2 &pt1):
        m_ID(pID),
        m_control_pts(pt0, pt1, ivec2(), ivec2()),
        m_num_control_pts(2)
      {
        process_control_pts();
      }

      IntBezierCurve(const ID_t &pID, const ivec2 &pt0, const ivec2 &ct,
                     const ivec2 &pt1):
        m_ID(pID),
        m_control_pts(pt0, ct, pt1, ivec2()),
        m_num_control_pts(3)
      {
        process_control_pts();
      }

      IntBezierCurve(const ID_t &pID, const ivec2 &pt0, const ivec2 &ct0,
                     const ivec2 &ct1, const ivec2 &pt1):
        m_ID(pID),
        m_control_pts(pt0, ct0, ct1, pt1),
        m_num_control_pts(4)
      {
        process_control_pts();
      }

      ~IntBezierCurve()
      {}

      const ID_t&
      ID(void) const
      {
        return m_ID;
      }

      const_c_array<ivec2>
      control_pts(void) const
      {
        return const_c_array<ivec2>(m_control_pts.c_ptr(), m_num_control_pts);
      }

      const BoundingBox<int>&
      bounding_box(void) const
      {
        return m_bb;
      }

      BoundingBox<int>
      bounding_box(const transformation<int> &tr) const
      {
        BoundingBox<int> R;
        R.union_point(tr(m_bb.min_point()));
        R.union_point(tr(m_bb.max_point()));
        return R;
      }

      static
      bool
      are_ordered_neighbors(const IntBezierCurve &curve0,
                            const IntBezierCurve &curve1)
      {
        return curve0.control_pts().back() == curve1.control_pts().front();
      }

      int
      degree(void) const
      {
        FASTUIDRAWassert(m_num_control_pts > 0);
        return m_num_control_pts - 1;
      }

      const_c_array<vec2>
      derivatives_cancel(void) const
      {
        return const_c_array<vec2>(m_derivatives_cancel.c_ptr(),
                                   m_num_derivatives_cancel);
      }

      const_c_array<int>
      as_polynomial(int coord) const
      {
        return const_c_array<int>(m_as_polynomial_fcn[coord].c_ptr(), m_num_control_pts);
      }

      vecN<const_c_array<int>, 2>
      as_polynomial(void) const
      {
        return vecN<const_c_array<int>, 2>(as_polynomial(0), as_polynomial(1));
      }

      vec2
      eval(float t) const;

    private:
      void
      process_control_pts(void);

      void
      compute_derivatives_cancel_pts(void);

      c_array<int>
      as_polynomial(int coord)
      {
        return c_array<int>(m_as_polynomial_fcn[coord].c_ptr(), m_num_control_pts);
      }

      ID_t m_ID;
      vecN<ivec2, 4> m_control_pts;
      unsigned int m_num_control_pts;
      vecN<ivec4, 2> m_as_polynomial_fcn;

      // where dx/dt +- dy/dt = 0
      vecN<vec2, 6> m_derivatives_cancel;
      unsigned int m_num_derivatives_cancel;
      BoundingBox<int> m_bb;
    };

    class IntContour
    {
    public:
      IntContour(void)
      {}

      void
      add_curve(const IntBezierCurve &curve)
      {
        FASTUIDRAWassert(m_curves.empty() || IntBezierCurve::are_ordered_neighbors(m_curves.back(), curve));
        m_curves.push_back(curve);
      }

      bool
      closed(void)
      {
        return !m_curves.empty()
          && IntBezierCurve::are_ordered_neighbors(m_curves.back(), m_curves.front());
      }

      const std::vector<IntBezierCurve>&
      curves(void) const
      {
        return m_curves;
      }

      const IntBezierCurve&
      curve(unsigned int curveID) const
      {
        FASTUIDRAWassert(curveID < m_curves.size());
        return m_curves[curveID];
      }

      /* Filter the Contour as follows:
          1. Collapse any curves that are within a texel
          2. Curves of tiny curvature are realized as a line
          3. Cubics are broken into quadratics
         The transformation tr is -NOT- applied to the contour,
         it is used as the transformation from IntContour
         coordinates to texel coordinates. The value of texel_size
         gives the size of a texel with the texel at (0, 0)
         starting at (0, 0) [in texel coordinates].
       */
      void
      filter(float curvature_collapse,
             const IntBezierCurve::transformation<int> &tr, ivec2 texel_size);

      void
      add_to_path(const IntBezierCurve::transformation<float> &tr, Path *dst) const;

    private:
      std::vector<IntBezierCurve> m_curves;
    };

    class IntPath
    {
    public:
      IntPath(void)
      {}

      void
      move_to(const ivec2 &pt);

      void
      line_to(const ivec2 &pt);

      void
      conic_to(const ivec2 &control_pt, const ivec2 &pt);

      void
      cubic_to(const ivec2 &control_pt0, const ivec2 &control_pt1, const ivec2 &pt);

      bool
      empty(void) const
      {
        return m_contours.empty();
      }

      /* Add this IntPath with a transforamtion tr applied to it to a
         pre-exising (and possibly empty) Path.
         \param tr transformation to apply to data of path
       */
      void
      add_to_path(const IntBezierCurve::transformation<float> &tr, Path *dst) const;

      /* Filter the Path as follows:
          1. Collapse any curves that are within a texel
          2. Curves of tiny curvature are realized as a line
          3. Cubics are broken into quadratics
         The transformation tr is -NOT- applied to the path,
         it is used as the transformation from IntContour
         coordinates to texel coordinates. The value of texel_size
         gives the size of a texel with the texel at (0, 0)
         starting at (0, 0) [in texel coordinates].
       */
      void
      filter(float curvature_collapse,
             const IntBezierCurve::transformation<int> &tr,
             ivec2 texel_size);

      /* Compute distance field data, where distance values are
         sampled at the center of each texel; the caller needs to
         make sure that the path with the transformation tr applied
         is entirely within the region [0, W] x [0, H] where
         (W, H) = texel_size * image_sz.
         \param texel_size the size of each texel in coordinates
                           AFTER tr is applied
         \param image_sz size of the distance field to make
         \param tr transformation to apply to data of path
       */
      void
      extract_render_data(const ivec2 &texel_size, const ivec2 &image_sz,
                          float max_distance,
                          IntBezierCurve::transformation<int> tr,
                          GlyphRenderDataDistanceField *dst) const;

      /* Compute curve-pair render data. The caller should have applied
         filter() before calling to reduce cubics and collapse tiny
         curves; also, the caller needs to make sure that the path
         with the transformation tr applied is entirely within the
         region [0, W] x [0, H] where (W, H) = texel_size * image_sz.
         \param texel_size the size of each texel in coordinates
                           AFTER tr is applied
         \param image_sz size of the distance field to make
         \param tr transformation to apply to data of path
       */
      void
      extract_render_data(const ivec2 &texel_size, const ivec2 &image_sz,
                          IntBezierCurve::transformation<int> tr,
                          GlyphRenderDataCurvePair *dst) const;

    private:
      IntBezierCurve::ID_t
      computeID(void);

      fastuidraw::ivec2 m_last_pt;
      std::vector<IntContour> m_contours;
    };

  } //namespace fastuidraw
} //namespace detail
<|endoftext|>


<commit_before>#include "gui/mrview/colourmap_button.h"
#include "gui/mrview/colourmap.h"
#include "math/rng.h"

namespace MR
{
namespace GUI
{
namespace MRView
{

using Entry = ColourMap::Entry;
const std::vector<Entry> ColourMapButton::core_colourmaps_entries{{
    Entry ("Gray",
        "color.rgb = vec3 (amplitude);\n"),

    Entry ("Hot",
        "color.rgb = vec3 (2.7213 * amplitude, 2.7213 * amplitude - 1.0, 3.7727 * amplitude - 2.7727);\n"),

    Entry ("Cool",
        "color.rgb = 1.0 - (vec3 (2.7213 * (1.0 - amplitude), 2.7213 * (1.0 - amplitude) - 1.0, 3.7727 * (1.0 - amplitude) - 2.7727));\n"),

    Entry ("Jet",
        "color.rgb = 1.5 - 4.0 * abs (1.0 - amplitude - vec3(0.25, 0.5, 0.75));\n")
}};

const std::vector<Entry> ColourMapButton::special_colourmaps_entries{{
    Entry ("RGB",
           "color.rgb = scale * (abs(color.rgb) - offset);\n",
           "length (color.rgb)", true),

    Entry ("Complex",
           "float phase = atan (color.r, color.g) * 0.954929658551372;\n"
           "color.rgb = phase + vec3 (-2.0, 0.0, 2.0);\n"
           "if (phase > 2.0) color.b -= 6.0;\n"
           "if (phase < -2.0) color.r += 6.0;\n"
           "color.rgb = clamp (scale * (amplitude - offset), 0.0, 1.0) * (2.0 - abs (color.rgb));\n",
           "length (color.rg)", true)
}};

ColourMapButton::ColourMapButton(QWidget* parent, ColourMapButtonObserver& obs,
                                 bool use_shortcuts,
                                 bool use_special_colourmaps,
                                 bool use_customise_state_items) :
    QToolButton(parent),
    colourmap_actions(ColourMapButton::core_colourmaps_entries.size()),
    observer(obs),
    core_colourmaps_actions(new QActionGroup(parent))
{
    setToolTip(tr("Colourmap menu"));
    setIcon(QIcon(":/colourmap.svg"));
    setPopupMode(QToolButton::InstantPopup);

    init_menu(use_shortcuts, use_special_colourmaps, use_customise_state_items);
}

void ColourMapButton::init_core_menu_items(bool create_shortcuts)
{
    core_colourmaps_actions->setExclusive(true);

    size_t n = 0;
    for(const auto& colourmap_entry : ColourMapButton::core_colourmaps_entries)
    {
      QAction* action = new QAction(colourmap_entry.name, this);
      action->setCheckable(true);
      core_colourmaps_actions->addAction(action);

      colourmap_menu->addAction(action);
      addAction(action);

      if(create_shortcuts)
        action->setShortcut(QObject::tr(std::string ("Ctrl+" + str (n+1)).c_str()));

      colourmap_actions[n] = action;
      n++;
    }

    connect(core_colourmaps_actions, SIGNAL(triggered (QAction*)), this, SLOT(select_colourmap_slot(QAction*)));
    colourmap_actions[1]->setChecked(true);
}

void ColourMapButton::init_custom_colour_menu_items()
{
    custom_colour_action = new QAction(tr("Custom colour..."), nullptr);
    custom_colour_action->setCheckable(true);
    connect(custom_colour_action, SIGNAL(triggered ()), this, SLOT(select_colour_slot()));

    core_colourmaps_actions->addAction(custom_colour_action);
    colourmap_menu->addAction(custom_colour_action);
    addAction(custom_colour_action);
    colourmap_actions.push_back(custom_colour_action);

    auto choose_random_colour = new QAction(tr("Random colour"), nullptr);
    connect(choose_random_colour, SIGNAL(triggered ()), this, SLOT(select_random_colour_slot()));

    colourmap_menu->addAction(choose_random_colour);
    addAction(choose_random_colour);
}

void ColourMapButton::init_special_colour_menu_items(bool create_shortcuts)
{
    size_t n = colourmap_actions.size();
    for(const auto& colourmap_entry : ColourMapButton::special_colourmaps_entries)
    {
      QAction* action = new QAction(colourmap_entry.name, this);
      action->setCheckable(true);
      core_colourmaps_actions->addAction(action);

      colourmap_menu->addAction(action);
      addAction(action);

      if(create_shortcuts)
        action->setShortcut(QObject::tr(std::string ("Ctrl+" + str (n+1)).c_str()));

      colourmap_actions.push_back(action);
      n++;
    }
}

void ColourMapButton::init_customise_sate_menu_items()
{
    auto show_colour_bar = colourmap_menu->addAction(tr("Show colour bar"), this, SLOT(show_colour_bar_slot(bool)));
    show_colour_bar->setCheckable(true);
    show_colour_bar->setChecked(true);
    addAction(show_colour_bar);

    auto invert_scale = colourmap_menu->addAction(tr("Invert"), this, SLOT(invert_colourmap_slot(bool)));
    invert_scale->setCheckable(true);
    addAction(invert_scale);

    QAction* reset_intensity = colourmap_menu->addAction(tr("Reset intensity"), this, SLOT(reset_intensity_slot()));
    addAction(reset_intensity);
}

void ColourMapButton::init_menu(bool create_shortcuts, bool use_special, bool customise_state)
{
    colourmap_menu = new QMenu(tr("Colourmap menu"), this);

    init_core_menu_items(create_shortcuts);
    init_custom_colour_menu_items();

    colourmap_menu->addSeparator();

    if(use_special)
    {
        init_special_colour_menu_items(create_shortcuts);
        colourmap_menu->addSeparator();
    }

    if(customise_state)
        init_customise_sate_menu_items();

    setMenu(colourmap_menu);
}

void ColourMapButton::set_colourmap_index(size_t index)
{
    if(index < colourmap_actions.size())
    {
        QAction* action = colourmap_actions[index];
        action->setChecked(true);
        select_colourmap_slot(action);
    }
}

void ColourMapButton::select_colourmap_slot(QAction* action)
{
    auto begin = colourmap_actions.cbegin();
    auto end = colourmap_actions.cend();
    auto it = std::find(begin, end, action);

    if(it != end)
        observer.selected_colourmap(std::distance(begin, it), *this);
}

void ColourMapButton::select_colour_slot()
{
    QColor colour = QColorDialog::getColor(Qt::red, this, "Select Color", QColorDialog::DontUseNativeDialog);

    if (colour.isValid())
        observer.selected_custom_colour(colour, *this);
}

void ColourMapButton::select_random_colour_slot()
{
    size_t colour[3];
    Math::RNG rng;
    std::uniform_int_distribution<unsigned char> uniform_int;
    constexpr size_t max_half = std::numeric_limits<unsigned char>::max()/2;
    do {
      colour[0] = uniform_int(rng);
      colour[1] = uniform_int(rng);
      colour[2] = uniform_int(rng);
    } while (colour[0] < max_half && colour[1] < max_half && colour[2] < max_half);

    custom_colour_action->setChecked(true);

    select_colourmap_slot(custom_colour_action);
    observer.selected_custom_colour(QColor(colour[0],colour[1],colour[2]), *this);
}

void ColourMapButton::show_colour_bar_slot(bool visible)
{
    observer.toggle_show_colour_bar(visible, *this);
}

void ColourMapButton::invert_colourmap_slot(bool inverted)
{
    observer.toggle_invert_colourmap(inverted, *this);
}

void ColourMapButton::reset_intensity_slot()
{
    observer.reset_colourmap(*this);
}

}
}
}
<commit_msg>Fix minor memory leak<commit_after>#include "gui/mrview/colourmap_button.h"
#include "gui/mrview/colourmap.h"
#include "math/rng.h"

namespace MR
{
namespace GUI
{
namespace MRView
{

using Entry = ColourMap::Entry;
const std::vector<Entry> ColourMapButton::core_colourmaps_entries{{
    Entry ("Gray",
        "color.rgb = vec3 (amplitude);\n"),

    Entry ("Hot",
        "color.rgb = vec3 (2.7213 * amplitude, 2.7213 * amplitude - 1.0, 3.7727 * amplitude - 2.7727);\n"),

    Entry ("Cool",
        "color.rgb = 1.0 - (vec3 (2.7213 * (1.0 - amplitude), 2.7213 * (1.0 - amplitude) - 1.0, 3.7727 * (1.0 - amplitude) - 2.7727));\n"),

    Entry ("Jet",
        "color.rgb = 1.5 - 4.0 * abs (1.0 - amplitude - vec3(0.25, 0.5, 0.75));\n")
}};

const std::vector<Entry> ColourMapButton::special_colourmaps_entries{{
    Entry ("RGB",
           "color.rgb = scale * (abs(color.rgb) - offset);\n",
           "length (color.rgb)", true),

    Entry ("Complex",
           "float phase = atan (color.r, color.g) * 0.954929658551372;\n"
           "color.rgb = phase + vec3 (-2.0, 0.0, 2.0);\n"
           "if (phase > 2.0) color.b -= 6.0;\n"
           "if (phase < -2.0) color.r += 6.0;\n"
           "color.rgb = clamp (scale * (amplitude - offset), 0.0, 1.0) * (2.0 - abs (color.rgb));\n",
           "length (color.rg)", true)
}};

ColourMapButton::ColourMapButton(QWidget* parent, ColourMapButtonObserver& obs,
                                 bool use_shortcuts,
                                 bool use_special_colourmaps,
                                 bool use_customise_state_items) :
    QToolButton(parent),
    colourmap_actions(ColourMapButton::core_colourmaps_entries.size()),
    observer(obs),
    core_colourmaps_actions(new QActionGroup(parent))
{
    setToolTip(tr("Colourmap menu"));
    setIcon(QIcon(":/colourmap.svg"));
    setPopupMode(QToolButton::InstantPopup);

    init_menu(use_shortcuts, use_special_colourmaps, use_customise_state_items);
}

void ColourMapButton::init_core_menu_items(bool create_shortcuts)
{
    core_colourmaps_actions->setExclusive(true);

    size_t n = 0;
    for(const auto& colourmap_entry : ColourMapButton::core_colourmaps_entries)
    {
      QAction* action = new QAction(colourmap_entry.name, this);
      action->setCheckable(true);
      core_colourmaps_actions->addAction(action);

      colourmap_menu->addAction(action);
      addAction(action);

      if(create_shortcuts)
        action->setShortcut(QObject::tr(std::string ("Ctrl+" + str (n+1)).c_str()));

      colourmap_actions[n] = action;
      n++;
    }

    connect(core_colourmaps_actions, SIGNAL(triggered (QAction*)), this, SLOT(select_colourmap_slot(QAction*)));
    colourmap_actions[1]->setChecked(true);
}

void ColourMapButton::init_custom_colour_menu_items()
{
    custom_colour_action = new QAction(tr("Custom colour..."), this);
    custom_colour_action->setCheckable(true);
    connect(custom_colour_action, SIGNAL(triggered ()), this, SLOT(select_colour_slot()));

    core_colourmaps_actions->addAction(custom_colour_action);
    colourmap_menu->addAction(custom_colour_action);
    addAction(custom_colour_action);
    colourmap_actions.push_back(custom_colour_action);

    auto choose_random_colour = new QAction(tr("Random colour"), this);
    connect(choose_random_colour, SIGNAL(triggered ()), this, SLOT(select_random_colour_slot()));

    colourmap_menu->addAction(choose_random_colour);
    addAction(choose_random_colour);
}

void ColourMapButton::init_special_colour_menu_items(bool create_shortcuts)
{
    size_t n = colourmap_actions.size();
    for(const auto& colourmap_entry : ColourMapButton::special_colourmaps_entries)
    {
      QAction* action = new QAction(colourmap_entry.name, this);
      action->setCheckable(true);
      core_colourmaps_actions->addAction(action);

      colourmap_menu->addAction(action);
      addAction(action);

      if(create_shortcuts)
        action->setShortcut(QObject::tr(std::string ("Ctrl+" + str (n+1)).c_str()));

      colourmap_actions.push_back(action);
      n++;
    }
}

void ColourMapButton::init_customise_sate_menu_items()
{
    auto show_colour_bar = colourmap_menu->addAction(tr("Show colour bar"), this, SLOT(show_colour_bar_slot(bool)));
    show_colour_bar->setCheckable(true);
    show_colour_bar->setChecked(true);
    addAction(show_colour_bar);

    auto invert_scale = colourmap_menu->addAction(tr("Invert"), this, SLOT(invert_colourmap_slot(bool)));
    invert_scale->setCheckable(true);
    addAction(invert_scale);

    QAction* reset_intensity = colourmap_menu->addAction(tr("Reset intensity"), this, SLOT(reset_intensity_slot()));
    addAction(reset_intensity);
}

void ColourMapButton::init_menu(bool create_shortcuts, bool use_special, bool customise_state)
{
    colourmap_menu = new QMenu(tr("Colourmap menu"), this);

    init_core_menu_items(create_shortcuts);
    init_custom_colour_menu_items();

    colourmap_menu->addSeparator();

    if(use_special)
    {
        init_special_colour_menu_items(create_shortcuts);
        colourmap_menu->addSeparator();
    }

    if(customise_state)
        init_customise_sate_menu_items();

    setMenu(colourmap_menu);
}

void ColourMapButton::set_colourmap_index(size_t index)
{
    if(index < colourmap_actions.size())
    {
        QAction* action = colourmap_actions[index];
        action->setChecked(true);
        select_colourmap_slot(action);
    }
}

void ColourMapButton::select_colourmap_slot(QAction* action)
{
    auto begin = colourmap_actions.cbegin();
    auto end = colourmap_actions.cend();
    auto it = std::find(begin, end, action);

    if(it != end)
        observer.selected_colourmap(std::distance(begin, it), *this);
}

void ColourMapButton::select_colour_slot()
{
    QColor colour = QColorDialog::getColor(Qt::red, this, "Select Color", QColorDialog::DontUseNativeDialog);

    if (colour.isValid())
        observer.selected_custom_colour(colour, *this);
}

void ColourMapButton::select_random_colour_slot()
{
    size_t colour[3];
    Math::RNG rng;
    std::uniform_int_distribution<unsigned char> uniform_int;
    constexpr size_t max_half = std::numeric_limits<unsigned char>::max()/2;
    do {
      colour[0] = uniform_int(rng);
      colour[1] = uniform_int(rng);
      colour[2] = uniform_int(rng);
    } while (colour[0] < max_half && colour[1] < max_half && colour[2] < max_half);

    custom_colour_action->setChecked(true);

    select_colourmap_slot(custom_colour_action);
    observer.selected_custom_colour(QColor(colour[0],colour[1],colour[2]), *this);
}

void ColourMapButton::show_colour_bar_slot(bool visible)
{
    observer.toggle_show_colour_bar(visible, *this);
}

void ColourMapButton::invert_colourmap_slot(bool inverted)
{
    observer.toggle_invert_colourmap(inverted, *this);
}

void ColourMapButton::reset_intensity_slot()
{
    observer.reset_colourmap(*this);
}

}
}
}
<|endoftext|>


<commit_before>/****************************************************************************
 **
 ** Copyright (C) Qxt Foundation. Some rights reserved.
 **
 ** This file is part of the QxtGui module of the Qxt library.
 **
 ** This library is free software; you can redistribute it and/or modify it
 ** under the terms of the Common Public License, version 1.0, as published
 ** by IBM, and/or under the terms of the GNU Lesser General Public License,
 ** version 2.1, as published by the Free Software Foundation.
 **
 ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
 ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
 ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
 ** FITNESS FOR A PARTICULAR PURPOSE.
 **
 ** You should have received a copy of the CPL and the LGPL along with this
 ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
 ** included with the source distribution for more information.
 ** If you did not receive a copy of the licenses, contact the Qxt Foundation.
 **
 ** <http://libqxt.org>  <foundation@libqxt.org>
 **
 ****************************************************************************/

#include "qxtsortfilterproxymodel.h"
#include <QRegExp>

/*!
 *@class QxtSortFilterProxyModel QxtSortFilterProxyModel is a multi column filter model
 *The QxtSortFilterProxyModel makes it possible to filter over multiple columns.
 *@code
 *QxtSortFilterProxyModel * filterModel = new QxtSortFilterProxyModel(parent);
 *filterModel->setSourceModel(sourceModel);
 *filterModel->beginDeclareFilter();
 *filterModel->setFilter(1,QVariant("SomeStringValue"),Qt::DisplayRole,Qt::MatchExactly);
 * //remove some old filter
 *filterModel->removeFilter(2);
 *filterModel->setFilter(5,QVariant(1234),Qt::DisplayRole,Qt::MatchExactly);
 *filterModel->endDeclateFilter();
 *@endcode
 *Now the model will filter column 1 and 5, to be accepted by the filtermodel a row needs to pass all filters
 */

class QxtModelFilterPrivate
{
    public:
        QxtModelFilterPrivate   (const QVariant &value = QVariant(), const int role = Qt::DisplayRole, const Qt::MatchFlags flags = Qt::MatchContains);
        bool            acceptsValue ( const QVariant & value );
        QVariant        m_value;
        int             m_role;
        Qt::MatchFlags  m_flags;
};

class QxtSortFilterProxyModelPrivate : public QxtPrivate<QxtSortFilterProxyModel>
{
    public:
        QMap<int,QxtModelFilterPrivate> filters;
        bool                            m_declaringFilter;
};

QxtModelFilterPrivate::QxtModelFilterPrivate(const QVariant &value, const int role, const Qt::MatchFlags flags)
    :m_value(value),m_role(role),m_flags(flags)
{

}

bool QxtModelFilterPrivate::acceptsValue ( const QVariant & value )
{
    uint matchType = m_flags & 0x0F;
    //if we have no value we accept everything
    if(!m_value.isValid() || !value.isValid())
        return true;

    // QVariant based matching
    if (matchType == Qt::MatchExactly ){
        if(m_value == value)
            return true;
    }
    // QString based matching
    else {
        Qt::CaseSensitivity cs = m_flags & Qt::MatchCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
        QString filterText = m_value.toString();
        QString modelText  = value.toString();
        switch (matchType){
            case Qt::MatchRegExp:
                if (QRegExp(filterText, cs).exactMatch(modelText))
                    return true;
                break;
            case Qt::MatchWildcard:
                if (QRegExp(filterText, cs, QRegExp::Wildcard).exactMatch(modelText))
                    return true;
                break;
            case Qt::MatchStartsWith:
                if (modelText.startsWith(filterText, cs))
                    return true;
                break;
            case Qt::MatchEndsWith:
                if (modelText.endsWith(filterText, cs))
                    return true;
                break;
            case Qt::MatchFixedString:
                if (modelText.compare(filterText, cs) == 0)
                    return true;
                break;
            default:
            case Qt::MatchContains:
                if (modelText.contains(filterText, cs))
                    return true;
        }
    }
    return false;
}

QxtSortFilterProxyModel::QxtSortFilterProxyModel ( QObject *parent) : QSortFilterProxyModel(parent)
{
    QXT_INIT_PRIVATE(QxtSortFilterProxyModel);
    qxt_d().m_declaringFilter = false;
}

/*!
 *@desc tells the model you want to declare a new filter
 *If you have a lot of data in your model it can be slow to declare more than one filter,
 *because the model will always rebuild itself. If you call this member before setting the
 *new filters the model will invalidate its contents not before you call \sa endDeclareFilter()
 */
void QxtSortFilterProxyModel::beginDeclareFilter ( )
{
    qxt_d().m_declaringFilter = true;
}

/*!
 *@desc stops the filter declaration and invalidates the filter
 *\sa beginDeclareFilter()
 */
void QxtSortFilterProxyModel::endDeclareFilter ( )
{
    if(qxt_d().m_declaringFilter){
        qxt_d().m_declaringFilter = false;
        invalidateFilter();
    }
}

bool QxtSortFilterProxyModel::filterAcceptsRow ( int source_row, const QModelIndex &source_parent ) const
{
    QList<int> filterColumns = qxt_d().filters.keys();
    foreach(int currCol,filterColumns){
        QxtModelFilterPrivate filter = qxt_d().filters[currCol];
        // if the column specified by the user is -1
        // that means all columns need to pass the filter to get into the result
        if (currCol == -1) {
            int column_count = sourceModel()->columnCount(source_parent);
            for (int column = 0; column < column_count; ++column) {
                QModelIndex source_index = sourceModel()->index(source_row, column, source_parent);
                QVariant key = sourceModel()->data(source_index, filter.m_role);
                if (!filter.acceptsValue(key))
                   return false;
            }
            continue;
        }
        QModelIndex source_index = sourceModel()->index(source_row, currCol , source_parent);
        if (!source_index.isValid()) // the column may not exist
            continue;
        QVariant key = sourceModel()->data(source_index, filter.m_role);
        if(!filter.acceptsValue(key))
            return false;
    }
    return true;
}

void QxtSortFilterProxyModel::setFilter ( const int column, const QVariant &value, const int role, Qt::MatchFlags flags )
{
    if(qxt_d().filters.contains(column))
        qxt_d().filters[column] = QxtModelFilterPrivate(value,role,flags);
    else
        qxt_d().filters.insert(column,QxtModelFilterPrivate(value,role,flags));

    if(!qxt_d().m_declaringFilter)
        invalidateFilter();
}

void QxtSortFilterProxyModel::removeFilter ( const int column )
{
    if(qxt_d().filters.contains(column)){
        qxt_d().filters.remove(column);

        if(!qxt_d().m_declaringFilter)
            invalidateFilter();
    }
}

void QxtSortFilterProxyModel::setFilterValue ( const int column , const QVariant &value )
{
    if(qxt_d().filters.contains(column))
        qxt_d().filters[column].m_value = value;
    else
        qxt_d().filters.insert(column,QxtModelFilterPrivate(value));

    if(!qxt_d().m_declaringFilter)
        invalidateFilter();
}

void QxtSortFilterProxyModel::setFilterRole ( const int column , const int role )
{
    if(qxt_d().filters.contains(column))
        qxt_d().filters[column].m_role=role;
    else
        qxt_d().filters.insert(column,QxtModelFilterPrivate(QVariant(),role));

    if(!qxt_d().m_declaringFilter)
        invalidateFilter();
}

void QxtSortFilterProxyModel::setFilterFlags ( const int column , const Qt::MatchFlags flags )
{
    if(qxt_d().filters.contains(column))
        qxt_d().filters[column].m_flags = flags;
    else
        qxt_d().filters.insert(column,QxtModelFilterPrivate(QVariant(),Qt::DisplayRole,flags));

    if(!qxt_d().m_declaringFilter)
        invalidateFilter();
}

QVariant QxtSortFilterProxyModel::filterValue ( const int column ) const
{
    if(qxt_d().filters.contains(column))
        return qxt_d().filters[column].m_value;
    return QVariant();
}

int QxtSortFilterProxyModel::filterRole ( const int column ) const
{
    if(qxt_d().filters.contains(column))
        return qxt_d().filters[column].m_role;
    return -1;
}

/*!
 * @desc returns the filter flags for the given column
 * @note if the column is not filtered it will return the default value
 */
Qt::MatchFlags QxtSortFilterProxyModel::filterFlags ( const int column ) const
{
    if(qxt_d().filters.contains(column))
        return qxt_d().filters[column].m_flags;
    return Qt::MatchContains;
}

/*!
 *Returns true if the column is filtered
 */
bool QxtSortFilterProxyModel::isFiltered ( const int column )
{
    return qxt_d().filters.contains(column);
}

<commit_msg>Fixed QxtSortFilterProxyModel docs.<commit_after>/****************************************************************************
 **
 ** Copyright (C) Qxt Foundation. Some rights reserved.
 **
 ** This file is part of the QxtGui module of the Qxt library.
 **
 ** This library is free software; you can redistribute it and/or modify it
 ** under the terms of the Common Public License, version 1.0, as published
 ** by IBM, and/or under the terms of the GNU Lesser General Public License,
 ** version 2.1, as published by the Free Software Foundation.
 **
 ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY
 ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY
 ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR
 ** FITNESS FOR A PARTICULAR PURPOSE.
 **
 ** You should have received a copy of the CPL and the LGPL along with this
 ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files
 ** included with the source distribution for more information.
 ** If you did not receive a copy of the licenses, contact the Qxt Foundation.
 **
 ** <http://libqxt.org>  <foundation@libqxt.org>
 **
 ****************************************************************************/

#include "qxtsortfilterproxymodel.h"
#include <QRegExp>

/*!
 *\class QxtSortFilterProxyModel QxtSortFilterProxyModel is a multi column filter model
 *The QxtSortFilterProxyModel makes it possible to filter over multiple columns.
 *\code
 *QxtSortFilterProxyModel * filterModel = new QxtSortFilterProxyModel(parent);
 *filterModel->setSourceModel(sourceModel);
 *filterModel->beginDeclareFilter();
 *filterModel->setFilter(1,QVariant("SomeStringValue"),Qt::DisplayRole,Qt::MatchExactly);
 * //remove some old filter
 *filterModel->removeFilter(2);
 *filterModel->setFilter(5,QVariant(1234),Qt::DisplayRole,Qt::MatchExactly);
 *filterModel->endDeclateFilter();
 *\endcode
 *Now the model will filter column 1 and 5, to be accepted by the filtermodel a row needs to pass all filters
 */

class QxtModelFilterPrivate
{
    public:
        QxtModelFilterPrivate   (const QVariant &value = QVariant(), const int role = Qt::DisplayRole, const Qt::MatchFlags flags = Qt::MatchContains);
        bool            acceptsValue ( const QVariant & value );
        QVariant        m_value;
        int             m_role;
        Qt::MatchFlags  m_flags;
};

class QxtSortFilterProxyModelPrivate : public QxtPrivate<QxtSortFilterProxyModel>
{
    public:
        QMap<int,QxtModelFilterPrivate> filters;
        bool                            m_declaringFilter;
};

QxtModelFilterPrivate::QxtModelFilterPrivate(const QVariant &value, const int role, const Qt::MatchFlags flags)
    :m_value(value),m_role(role),m_flags(flags)
{

}

bool QxtModelFilterPrivate::acceptsValue ( const QVariant & value )
{
    uint matchType = m_flags & 0x0F;
    //if we have no value we accept everything
    if(!m_value.isValid() || !value.isValid())
        return true;

    // QVariant based matching
    if (matchType == Qt::MatchExactly ){
        if(m_value == value)
            return true;
    }
    // QString based matching
    else {
        Qt::CaseSensitivity cs = m_flags & Qt::MatchCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
        QString filterText = m_value.toString();
        QString modelText  = value.toString();
        switch (matchType){
            case Qt::MatchRegExp:
                if (QRegExp(filterText, cs).exactMatch(modelText))
                    return true;
                break;
            case Qt::MatchWildcard:
                if (QRegExp(filterText, cs, QRegExp::Wildcard).exactMatch(modelText))
                    return true;
                break;
            case Qt::MatchStartsWith:
                if (modelText.startsWith(filterText, cs))
                    return true;
                break;
            case Qt::MatchEndsWith:
                if (modelText.endsWith(filterText, cs))
                    return true;
                break;
            case Qt::MatchFixedString:
                if (modelText.compare(filterText, cs) == 0)
                    return true;
                break;
            default:
            case Qt::MatchContains:
                if (modelText.contains(filterText, cs))
                    return true;
        }
    }
    return false;
}

QxtSortFilterProxyModel::QxtSortFilterProxyModel ( QObject *parent) : QSortFilterProxyModel(parent)
{
    QXT_INIT_PRIVATE(QxtSortFilterProxyModel);
    qxt_d().m_declaringFilter = false;
}

/*!
 *\brief tells the model you want to declare a new filter
 *If you have a lot of data in your model it can be slow to declare more than one filter,
 *because the model will always rebuild itself. If you call this member before setting the
 *new filters the model will invalidate its contents not before you call \sa endDeclareFilter()
 */
void QxtSortFilterProxyModel::beginDeclareFilter ( )
{
    qxt_d().m_declaringFilter = true;
}

/*!
 *\brief stops the filter declaration and invalidates the filter
 *\sa beginDeclareFilter()
 */
void QxtSortFilterProxyModel::endDeclareFilter ( )
{
    if(qxt_d().m_declaringFilter){
        qxt_d().m_declaringFilter = false;
        invalidateFilter();
    }
}

bool QxtSortFilterProxyModel::filterAcceptsRow ( int source_row, const QModelIndex &source_parent ) const
{
    QList<int> filterColumns = qxt_d().filters.keys();
    foreach(int currCol,filterColumns){
        QxtModelFilterPrivate filter = qxt_d().filters[currCol];
        // if the column specified by the user is -1
        // that means all columns need to pass the filter to get into the result
        if (currCol == -1) {
            int column_count = sourceModel()->columnCount(source_parent);
            for (int column = 0; column < column_count; ++column) {
                QModelIndex source_index = sourceModel()->index(source_row, column, source_parent);
                QVariant key = sourceModel()->data(source_index, filter.m_role);
                if (!filter.acceptsValue(key))
                   return false;
            }
            continue;
        }
        QModelIndex source_index = sourceModel()->index(source_row, currCol , source_parent);
        if (!source_index.isValid()) // the column may not exist
            continue;
        QVariant key = sourceModel()->data(source_index, filter.m_role);
        if(!filter.acceptsValue(key))
            return false;
    }
    return true;
}

void QxtSortFilterProxyModel::setFilter ( const int column, const QVariant &value, const int role, Qt::MatchFlags flags )
{
    if(qxt_d().filters.contains(column))
        qxt_d().filters[column] = QxtModelFilterPrivate(value,role,flags);
    else
        qxt_d().filters.insert(column,QxtModelFilterPrivate(value,role,flags));

    if(!qxt_d().m_declaringFilter)
        invalidateFilter();
}

void QxtSortFilterProxyModel::removeFilter ( const int column )
{
    if(qxt_d().filters.contains(column)){
        qxt_d().filters.remove(column);

        if(!qxt_d().m_declaringFilter)
            invalidateFilter();
    }
}

void QxtSortFilterProxyModel::setFilterValue ( const int column , const QVariant &value )
{
    if(qxt_d().filters.contains(column))
        qxt_d().filters[column].m_value = value;
    else
        qxt_d().filters.insert(column,QxtModelFilterPrivate(value));

    if(!qxt_d().m_declaringFilter)
        invalidateFilter();
}

void QxtSortFilterProxyModel::setFilterRole ( const int column , const int role )
{
    if(qxt_d().filters.contains(column))
        qxt_d().filters[column].m_role=role;
    else
        qxt_d().filters.insert(column,QxtModelFilterPrivate(QVariant(),role));

    if(!qxt_d().m_declaringFilter)
        invalidateFilter();
}

void QxtSortFilterProxyModel::setFilterFlags ( const int column , const Qt::MatchFlags flags )
{
    if(qxt_d().filters.contains(column))
        qxt_d().filters[column].m_flags = flags;
    else
        qxt_d().filters.insert(column,QxtModelFilterPrivate(QVariant(),Qt::DisplayRole,flags));

    if(!qxt_d().m_declaringFilter)
        invalidateFilter();
}

QVariant QxtSortFilterProxyModel::filterValue ( const int column ) const
{
    if(qxt_d().filters.contains(column))
        return qxt_d().filters[column].m_value;
    return QVariant();
}

int QxtSortFilterProxyModel::filterRole ( const int column ) const
{
    if(qxt_d().filters.contains(column))
        return qxt_d().filters[column].m_role;
    return -1;
}

/*!
 * \brief returns the filter flags for the given column
 * \bold {Note:} if the column is not filtered it will return the default value
 */
Qt::MatchFlags QxtSortFilterProxyModel::filterFlags ( const int column ) const
{
    if(qxt_d().filters.contains(column))
        return qxt_d().filters[column].m_flags;
    return Qt::MatchContains;
}

/*!
 *Returns true if the column is filtered
 */
bool QxtSortFilterProxyModel::isFiltered ( const int column )
{
    return qxt_d().filters.contains(column);
}

<|endoftext|>


<commit_before>// -*- Mode:C++ -*-

/**************************************************************************************************/
/*                                                                                */
/* Copyright (C) 2015 University of Hull                                                          */
/*                                                                                */
/**************************************************************************************************/
/*                                                                                */
/*  module     :  celestial/orbital/test/orbitals.cpp                                             */
/*  project    :                                                                                */
/*  description:                                                                                */
/*                                                                                */
/**************************************************************************************************/

// includes, system

#include <boost/date_time/posix_time/posix_time.hpp> // boost::posix_time::ptime
#include <boost/io/ios_state.hpp>                    // boost::io::ios_flags_saver
#include <glm/gtx/io.hpp>                            // glm::operator<<

// includes, project

#include <celestial/orbitals.hpp>

#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

// internal unnamed namespace

namespace {

  // types, internal (class, enum, struct, union, typedef)

  // variables, internal

  boost::posix_time::ptime const dtime(// boost::posix_time::second_clock::local_time().date(),
                                       boost::gregorian::date(2000, 1, 1),
                                       boost::posix_time::hours(0));

  // functions, internal

  void
  print_orbital(celestial::orbital::base const& o)
  {
    boost::io::ios_flags_saver const ifs(std::cout);

    BOOST_MESSAGE(o << " @"
                  << glm::io::precision(2) << glm::io::width(1+2+1+2)
                  << o.distance_and_anomaly() << ':'
                  << glm::io::precision(2) << glm::io::width(1+1+1+2)
                  << o.coord_ecliptic_rect_geo() << ':'
                  << o.coord_equatorial_rect_geo());
  }

} // namespace {

// variables, exported

// functions, exported

BOOST_AUTO_TEST_CASE(test_celestial_orbital_moon)
{
  celestial::orbital::moon const m(dtime, 0.0);

  BOOST_CHECK(60.2666 == m.a);
  BOOST_CHECK(0.0549  == m.e);
  BOOST_CHECK(5.1454  == m.i);

  print_orbital(m);
}

BOOST_AUTO_TEST_CASE(test_celestial_orbital_sun)
{
  celestial::orbital::sun const s(dtime, 0.0);

  BOOST_CHECK(0.0 == s.N);
  BOOST_CHECK(1.0 == s.a);
  BOOST_CHECK(0.0 == s.i);

  print_orbital(s);
}

BOOST_AUTO_TEST_CASE(test_celestial_orbital_mercury)
{
  celestial::orbital::mercury const m(dtime, 0.0);

  BOOST_CHECK(0.387098 == m.a);

  print_orbital(m);
}

BOOST_AUTO_TEST_CASE(test_celestial_orbital_venus)
{
  celestial::orbital::venus const v(dtime, 0.0);

  BOOST_CHECK(0.723330 == v.a);

  print_orbital(v);
}

BOOST_AUTO_TEST_CASE(test_celestial_orbital_mars)
{
  celestial::orbital::mars const m(dtime, 0.0);

  BOOST_CHECK(1.523688 == m.a);

  print_orbital(m);
}

BOOST_AUTO_TEST_CASE(test_celestial_orbital_jupiter)
{
  celestial::orbital::jupiter const j(dtime, 0.0);

  BOOST_CHECK(5.20256 == j.a);

  print_orbital(j);
}

BOOST_AUTO_TEST_CASE(test_celestial_orbital_saturn)
{
  celestial::orbital::saturn const s(dtime, 0.0);

  BOOST_CHECK(9.55475 == s.a);

  print_orbital(s);
}

BOOST_AUTO_TEST_CASE(test_celestial_orbital_uranus)
{
  celestial::orbital::uranus const u(dtime, 0.0);

  BOOST_CHECK(true);

  print_orbital(u);
}

BOOST_AUTO_TEST_CASE(test_celestial_orbital_neptune)
{
  celestial::orbital::neptune const n(dtime, 0.0);

  BOOST_CHECK(true);

  print_orbital(n);
}
<commit_msg>formatting<commit_after>// -*- Mode:C++ -*-

/**************************************************************************************************/
/*                                                                                */
/* Copyright (C) 2015 University of Hull                                                          */
/*                                                                                */
/**************************************************************************************************/
/*                                                                                */
/*  module     :  celestial/orbital/test/orbitals.cpp                                             */
/*  project    :                                                                                */
/*  description:                                                                                */
/*                                                                                */
/**************************************************************************************************/

// includes, system

#include <boost/date_time/posix_time/posix_time.hpp> // boost::posix_time::ptime
#include <boost/io/ios_state.hpp>                    // boost::io::ios_flags_saver
#include <glm/gtx/io.hpp>                            // glm::operator<<

// includes, project

#include <celestial/orbitals.hpp>

#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

// internal unnamed namespace

namespace {

  // types, internal (class, enum, struct, union, typedef)

  // variables, internal

  boost::posix_time::ptime const dtime(// boost::posix_time::second_clock::local_time().date(),
                                       boost::gregorian::date(2000, 1, 1),
                                       boost::posix_time::hours(0));

  // functions, internal

  void
  print_orbital(celestial::orbital::base const& o)
  {
    boost::io::ios_flags_saver const ifs(std::cout);

    BOOST_MESSAGE(o << " @"
                  << glm::io::precision(2) << glm::io::width(1+2+1+2)
                  << o.distance_and_anomaly() << ':'
                  << glm::io::precision(2) << glm::io::width(1+1+1+2)
                  << o.coord_ecliptic_rect_geo() << ':'
                  << o.coord_equatorial_rect_geo());
  }

} // namespace {

// variables, exported

// functions, exported

BOOST_AUTO_TEST_CASE(test_celestial_orbital_moon)
{
  celestial::orbital::moon const m(dtime, 0.0);

  BOOST_CHECK(60.2666 == m.a);
  BOOST_CHECK( 0.0549  == m.e);
  BOOST_CHECK( 5.1454  == m.i);

  print_orbital(m);
}

BOOST_AUTO_TEST_CASE(test_celestial_orbital_sun)
{
  celestial::orbital::sun const s(dtime, 0.0);

  BOOST_CHECK(0.0 == s.N);
  BOOST_CHECK(1.0 == s.a);
  BOOST_CHECK(0.0 == s.i);

  print_orbital(s);
}

BOOST_AUTO_TEST_CASE(test_celestial_orbital_mercury)
{
  celestial::orbital::mercury const m(dtime, 0.0);

  BOOST_CHECK(0.387098 == m.a);

  print_orbital(m);
}

BOOST_AUTO_TEST_CASE(test_celestial_orbital_venus)
{
  celestial::orbital::venus const v(dtime, 0.0);

  BOOST_CHECK(0.723330 == v.a);

  print_orbital(v);
}

BOOST_AUTO_TEST_CASE(test_celestial_orbital_mars)
{
  celestial::orbital::mars const m(dtime, 0.0);

  BOOST_CHECK(1.523688 == m.a);

  print_orbital(m);
}

BOOST_AUTO_TEST_CASE(test_celestial_orbital_jupiter)
{
  celestial::orbital::jupiter const j(dtime, 0.0);

  BOOST_CHECK(5.20256 == j.a);

  print_orbital(j);
}

BOOST_AUTO_TEST_CASE(test_celestial_orbital_saturn)
{
  celestial::orbital::saturn const s(dtime, 0.0);

  BOOST_CHECK(9.55475 == s.a);

  print_orbital(s);
}

BOOST_AUTO_TEST_CASE(test_celestial_orbital_uranus)
{
  celestial::orbital::uranus const u(dtime, 0.0);

  BOOST_CHECK(true);

  print_orbital(u);
}

BOOST_AUTO_TEST_CASE(test_celestial_orbital_neptune)
{
  celestial::orbital::neptune const n(dtime, 0.0);

  BOOST_CHECK(true);

  print_orbital(n);
}
<|endoftext|>


<commit_before>/*
 *
 *    Copyright (c) 2020 Project CHIP Authors
 *    Copyright (c) 2013-2017 Nest Labs, Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
#include "lib/support/CHIPMemString.h"
#include "lib/support/ScopedBuffer.h"
#include <lib/core/DataModelTypes.h>
#include <lib/support/Base64.h>
#include <lib/support/jsontlv/TlvJson.h>

namespace {
/*
 * Encapsulates the different types of keys permissible.
 *
 * Root Key = Key with a name of 'value'. This is the top-most key in a given JSON object generated from TLV.
 * Struct Field = Key containing the 32-bit field ID of an item in a struct.
 * Array Item = Key containing the 16-bit list index of an item in a list.
 *
 * In the latter two modes, the actual field ID/list index is encapsulated within the 'key' member.
 *
 */
struct KeyContext
{
    enum KeyType
    {
        kRoot,
        kStructField,
        kArrayItem
    };

    KeyContext() = default;

    KeyContext(chip::FieldId fieldId)
    {
        keyType = kStructField;
        key     = fieldId;
    }

    KeyContext(chip::ListIndex listIndex)
    {
        keyType = kArrayItem;
        key     = listIndex;
    }

    KeyType keyType = kRoot;
    uint32_t key    = 0;
};
} // namespace

//
// For now, let's put a bound of the maximum length of a byte/char string to be the size of an IPv6
// MTU. While this is smaller than that of the limit defined in the data model specification,
// strings by virtue of not being chunked are intrinsically limited in size to the size of the encompassing packet.
//
static constexpr uint16_t kMaxStringLen = 1280;

namespace chip {

/*
 * This templated function inserts a key/value pair into the Json value object.
 * The value is templated to be of type T and accepts any of the following primitive
 * types:
 *      bool, uint*_t, int*_t, char *, float, double.
 *
 * This method uses the provided key context to deduce the type of element being added.
 *
 */
template <typename T>
void InsertKeyValue(Json::Value & json, const KeyContext & keyContext, T val)
{
    //
    // This needs to accomodate either the string 'value', or a 32-bit integer.
    // The size of the largest 32-bit integer key represented as a string is 11 characters long.
    // Tack on 1 byte for the null character.
    //
    char keyBuf[12];

    if (keyContext.keyType == KeyContext::kRoot)
    {
        Platform::CopyString(keyBuf, sizeof(keyBuf), "value");
        json[keyBuf] = val;
    }
    else if (keyContext.keyType == KeyContext::kStructField)
    {
        snprintf(keyBuf, sizeof(keyBuf), "%" PRIu32, keyContext.key);
        json[keyBuf] = val;
    }
    else
    {
        json[keyContext.key] = val;
    }
}

std::string JsonToString(Json::Value & json)
{
    Json::StyledWriter writer;
    return writer.write(json);
}

CHIP_ERROR TlvToJson(TLV::TLVReader & reader, KeyContext context, Json::Value & parent)
{
    bool isStruct = false;

    switch (reader.GetType())
    {
    case TLV::kTLVType_UnsignedInteger: {
        uint64_t v;
        ReturnErrorOnFailure(reader.Get(v));
        InsertKeyValue(parent, context, v);
        break;
    }

    case TLV::kTLVType_SignedInteger: {
        int64_t v;
        ReturnErrorOnFailure(reader.Get(v));
        InsertKeyValue(parent, context, v);
        break;
    }

    case TLV::kTLVType_Boolean: {
        bool v;
        ReturnErrorOnFailure(reader.Get(v));
        InsertKeyValue(parent, context, v);
        break;
    }

    case TLV::kTLVType_FloatingPointNumber: {
        double v;
        ReturnErrorOnFailure(reader.Get(v));
        InsertKeyValue(parent, context, v);
        break;
    }

    case TLV::kTLVType_ByteString: {
        ByteSpan span;

        ReturnErrorOnFailure(reader.Get(span));
        VerifyOrReturnError(span.size() < kMaxStringLen, CHIP_ERROR_INVALID_TLV_ELEMENT);

        Platform::ScopedMemoryBuffer<char> byteString;
        byteString.Alloc(BASE64_ENCODED_LEN(span.size()) + 1);
        VerifyOrReturnError(byteString.Get() != nullptr, CHIP_ERROR_NO_MEMORY);

        auto encodedLen              = Base64Encode(span.data(), span.size(), byteString.Get());
        byteString.Get()[encodedLen] = '\0';

        InsertKeyValue(parent, context, byteString.Get());
        break;
    }

    case TLV::kTLVType_UTF8String: {
        CharSpan span;

        ReturnErrorOnFailure(reader.Get(span));
        VerifyOrReturnError(span.size() < kMaxStringLen, CHIP_ERROR_INVALID_TLV_ELEMENT);

        Platform::ScopedMemoryString charString(span.data(), span.size());
        InsertKeyValue(parent, context, charString.Get());
        break;
    }

    case TLV::kTLVType_Null: {
        InsertKeyValue(parent, context, Json::Value());
        break;
    }

    case TLV::kTLVType_Structure:
        isStruct = true;

        //
        // Fall-through to the case below since
        // arrays and structs are handled similarly with
        // just a small difference in terms of handling of field IDs vs.
        // list indices of the elements in the respective collections.
        //

    case TLV::kTLVType_Array: {
        TLV::TLVType containerType;

        ReturnErrorOnFailure(reader.EnterContainer(containerType));

        CHIP_ERROR err;
        Json::Value value;
        size_t listIndex = 0;

        while ((err = reader.Next()) == CHIP_NO_ERROR)
        {
            if (isStruct)
            {
                VerifyOrReturnError(TLV::IsContextTag(reader.GetTag()), CHIP_ERROR_INVALID_TLV_TAG);
                KeyContext context2(static_cast<chip::FieldId>(TLV::TagNumFromTag(reader.GetTag())));

                //
                // Recursively convert to JSON the encompassing item within the struct.
                //
                ReturnErrorOnFailure(TlvToJson(reader, context2, value));
            }
            else
            {
                KeyContext context2(static_cast<chip::ListIndex>(listIndex++));

                //
                // Recursively convert to JSON the encompassing item within the array.
                //
                ReturnErrorOnFailure(TlvToJson(reader, context2, value));
            }
        }

        VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
        ReturnErrorOnFailure(reader.ExitContainer(containerType));
        InsertKeyValue(parent, context, value);
        break;
    }

    default:
        return CHIP_ERROR_INVALID_TLV_ELEMENT;
        break;
    }

    return CHIP_NO_ERROR;
}

CHIP_ERROR TlvToJson(TLV::TLVReader & reader, Json::Value & root)
{
    KeyContext context;
    return TlvToJson(reader, context, root);
}

} // namespace chip
<commit_msg>Revert "Fix sprintf type issue in TlvJson (#15772)" (#15779)<commit_after>/*
 *
 *    Copyright (c) 2020 Project CHIP Authors
 *    Copyright (c) 2013-2017 Nest Labs, Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
#include "lib/support/CHIPMemString.h"
#include "lib/support/ScopedBuffer.h"
#include <lib/core/DataModelTypes.h>
#include <lib/support/Base64.h>
#include <lib/support/jsontlv/TlvJson.h>

namespace {
/*
 * Encapsulates the different types of keys permissible.
 *
 * Root Key = Key with a name of 'value'. This is the top-most key in a given JSON object generated from TLV.
 * Struct Field = Key containing the 32-bit field ID of an item in a struct.
 * Array Item = Key containing the 16-bit list index of an item in a list.
 *
 * In the latter two modes, the actual field ID/list index is encapsulated within the 'key' member.
 *
 */
struct KeyContext
{
    enum KeyType
    {
        kRoot,
        kStructField,
        kArrayItem
    };

    KeyContext() = default;

    KeyContext(chip::FieldId fieldId)
    {
        keyType = kStructField;
        key     = fieldId;
    }

    KeyContext(chip::ListIndex listIndex)
    {
        keyType = kArrayItem;
        key     = listIndex;
    }

    KeyType keyType  = kRoot;
    unsigned int key = 0;
};
} // namespace

//
// For now, let's put a bound of the maximum length of a byte/char string to be the size of an IPv6
// MTU. While this is smaller than that of the limit defined in the data model specification,
// strings by virtue of not being chunked are intrinsically limited in size to the size of the encompassing packet.
//
static constexpr uint16_t kMaxStringLen = 1280;

namespace chip {

/*
 * This templated function inserts a key/value pair into the Json value object.
 * The value is templated to be of type T and accepts any of the following primitive
 * types:
 *      bool, uint*_t, int*_t, char *, float, double.
 *
 * This method uses the provided key context to deduce the type of element being added.
 *
 */
template <typename T>
void InsertKeyValue(Json::Value & json, const KeyContext & keyContext, T val)
{
    //
    // This needs to accomodate either the string 'value', or a 32-bit integer.
    // The size of the largest 32-bit integer key represented as a string is 11 characters long.
    // Tack on 1 byte for the null character.
    //
    char keyBuf[12];

    if (keyContext.keyType == KeyContext::kRoot)
    {
        Platform::CopyString(keyBuf, sizeof(keyBuf), "value");
        json[keyBuf] = val;
    }
    else if (keyContext.keyType == KeyContext::kStructField)
    {
        snprintf(keyBuf, sizeof(keyBuf), "%" PRIu32, keyContext.key);
        json[keyBuf] = val;
    }
    else
    {
        json[keyContext.key] = val;
    }
}

std::string JsonToString(Json::Value & json)
{
    Json::StyledWriter writer;
    return writer.write(json);
}

CHIP_ERROR TlvToJson(TLV::TLVReader & reader, KeyContext context, Json::Value & parent)
{
    bool isStruct = false;

    switch (reader.GetType())
    {
    case TLV::kTLVType_UnsignedInteger: {
        uint64_t v;
        ReturnErrorOnFailure(reader.Get(v));
        InsertKeyValue(parent, context, v);
        break;
    }

    case TLV::kTLVType_SignedInteger: {
        int64_t v;
        ReturnErrorOnFailure(reader.Get(v));
        InsertKeyValue(parent, context, v);
        break;
    }

    case TLV::kTLVType_Boolean: {
        bool v;
        ReturnErrorOnFailure(reader.Get(v));
        InsertKeyValue(parent, context, v);
        break;
    }

    case TLV::kTLVType_FloatingPointNumber: {
        double v;
        ReturnErrorOnFailure(reader.Get(v));
        InsertKeyValue(parent, context, v);
        break;
    }

    case TLV::kTLVType_ByteString: {
        ByteSpan span;

        ReturnErrorOnFailure(reader.Get(span));
        VerifyOrReturnError(span.size() < kMaxStringLen, CHIP_ERROR_INVALID_TLV_ELEMENT);

        Platform::ScopedMemoryBuffer<char> byteString;
        byteString.Alloc(BASE64_ENCODED_LEN(span.size()) + 1);
        VerifyOrReturnError(byteString.Get() != nullptr, CHIP_ERROR_NO_MEMORY);

        auto encodedLen              = Base64Encode(span.data(), span.size(), byteString.Get());
        byteString.Get()[encodedLen] = '\0';

        InsertKeyValue(parent, context, byteString.Get());
        break;
    }

    case TLV::kTLVType_UTF8String: {
        CharSpan span;

        ReturnErrorOnFailure(reader.Get(span));
        VerifyOrReturnError(span.size() < kMaxStringLen, CHIP_ERROR_INVALID_TLV_ELEMENT);

        Platform::ScopedMemoryString charString(span.data(), span.size());
        InsertKeyValue(parent, context, charString.Get());
        break;
    }

    case TLV::kTLVType_Null: {
        InsertKeyValue(parent, context, Json::Value());
        break;
    }

    case TLV::kTLVType_Structure:
        isStruct = true;

        //
        // Fall-through to the case below since
        // arrays and structs are handled similarly with
        // just a small difference in terms of handling of field IDs vs.
        // list indices of the elements in the respective collections.
        //

    case TLV::kTLVType_Array: {
        TLV::TLVType containerType;

        ReturnErrorOnFailure(reader.EnterContainer(containerType));

        CHIP_ERROR err;
        Json::Value value;
        size_t listIndex = 0;

        while ((err = reader.Next()) == CHIP_NO_ERROR)
        {
            if (isStruct)
            {
                VerifyOrReturnError(TLV::IsContextTag(reader.GetTag()), CHIP_ERROR_INVALID_TLV_TAG);
                KeyContext context2(static_cast<chip::FieldId>(TLV::TagNumFromTag(reader.GetTag())));

                //
                // Recursively convert to JSON the encompassing item within the struct.
                //
                ReturnErrorOnFailure(TlvToJson(reader, context2, value));
            }
            else
            {
                KeyContext context2(static_cast<chip::ListIndex>(listIndex++));

                //
                // Recursively convert to JSON the encompassing item within the array.
                //
                ReturnErrorOnFailure(TlvToJson(reader, context2, value));
            }
        }

        VerifyOrReturnError(err == CHIP_END_OF_TLV, err);
        ReturnErrorOnFailure(reader.ExitContainer(containerType));
        InsertKeyValue(parent, context, value);
        break;
    }

    default:
        return CHIP_ERROR_INVALID_TLV_ELEMENT;
        break;
    }

    return CHIP_NO_ERROR;
}

CHIP_ERROR TlvToJson(TLV::TLVReader & reader, Json::Value & root)
{
    KeyContext context;
    return TlvToJson(reader, context, root);
}

} // namespace chip
<|endoftext|>