<commit_before>/*
    Copyright (c) 2005-2021 Intel Corporation

    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.
*/

// Message based key matching is a preview feature
#define TBB_PREVIEW_FLOW_GRAPH_FEATURES 1

#define MAX_TUPLE_TEST_SIZE 10

#include "common/config.h"

#include "test_join_node.h"

//! \file test_join_node_msg_key_matching.cpp
//! \brief Test for [preview] functionality

#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
#include <array>
#include <vector>
void test_follows_and_precedes_api() {
    using msg_t = MyMessageKeyWithoutKey<int, int>;
    using JoinOutputType = std::tuple<msg_t, msg_t, msg_t>;

    std::array<msg_t, 3> messages_for_follows = { {msg_t(), msg_t(), msg_t()} };
    std::vector<msg_t> messages_for_precedes = { msg_t(), msg_t(), msg_t() };

    follows_and_precedes_testing::test_follows
        <msg_t, tbb::flow::join_node<JoinOutputType, tbb::flow::key_matching<std::size_t>>, tbb::flow::buffer_node<msg_t>>
        (messages_for_follows);
    follows_and_precedes_testing::test_precedes
        <msg_t, tbb::flow::join_node<JoinOutputType, tbb::flow::key_matching<std::size_t>>>
        (messages_for_precedes);
}
#endif

#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
struct message_key {
    int my_key;
    double my_value;

    int key() const { return my_key; }

    operator size_t() const { return my_key; }

    bool operator==(const message_key& rhs) { return my_value == rhs.my_value; }
};

void test_deduction_guides() {
    using namespace tbb::flow;
    using tuple_type = std::tuple<message_key, message_key>;

    graph g;
    broadcast_node<message_key> bm1(g), bm2(g);
    broadcast_node<tuple_type> bm3(g);
    join_node<tuple_type, key_matching<int> > j0(g);
    join_node j3(j0);
    static_assert(std::is_same_v<decltype(j3), join_node<tuple_type, key_matching<int>>>);
}
#endif

//! Serial test with matching policies
//! \brief \ref error_guessing
TEST_CASE("Serial test") {
    generate_test<serial_test, std::tuple<MyMessageKeyWithBrokenKey<int, double>, MyMessageKeyWithoutKey<int, float> >, message_based_key_matching<int> >::do_test();
    generate_test<serial_test, std::tuple<MyMessageKeyWithoutKeyMethod<std::string, double>, MyMessageKeyWithBrokenKey<std::string, float> >, message_based_key_matching<std::string> >::do_test();
}

template <typename T1, typename T2>
using make_tuple = decltype(std::tuple_cat(T1(), std::tuple<T2>()));
using T1 = std::tuple<MyMessageKeyWithoutKeyMethod<std::string, double>>;
using T2 = make_tuple<T1, MyMessageKeyWithBrokenKey<std::string, int>>;
using T3 = make_tuple < T2, MyMessageKeyWithoutKey<std::string, int>>;
using T4 = make_tuple < T3, MyMessageKeyWithoutKeyMethod<std::string, size_t>>;
using T5 = make_tuple < T4, MyMessageKeyWithBrokenKey<std::string, int>>;
using T6 = make_tuple < T5, MyMessageKeyWithoutKeyMethod<std::string, short>>;
using T7 = make_tuple < T6, MyMessageKeyWithoutKeyMethod<std::string, threebyte>>;
using T8 = make_tuple < T7, MyMessageKeyWithBrokenKey<std::string, int>>;
using T9 = make_tuple < T8, MyMessageKeyWithoutKeyMethod<std::string, threebyte>>;
using T10 = make_tuple < T9, MyMessageKeyWithBrokenKey<std::string, size_t>>;

#if TBB_TEST_LOW_WORKLOAD && TBB_USE_DEBUG
// the compiler might generate huge object file in debug (>64M)
#define TEST_CASE_TEMPLATE_N_ARGS(dec) TEST_CASE_TEMPLATE(dec, T, T2, T3, T4, T5, T6, T7, T8, T9, T10)
#else
#define TEST_CASE_TEMPLATE_N_ARGS(dec) TEST_CASE_TEMPLATE(dec, T, T2, T5, T8, T10)
#endif

//! Serial test with different tuple sizes
//! \brief \ref error_guessing
TEST_CASE_TEMPLATE_N_ARGS("Serial N tests") {
    generate_test<serial_test, T, message_based_key_matching<std::string&> >::do_test();
}

//! Parallel test with different tuple sizes
//! \brief \ref error_guessing
TEST_CASE_TEMPLATE_N_ARGS("Parallel N tests") {
    generate_test<parallel_test, T, message_based_key_matching<std::string&> >::do_test();
}

//! Parallel test with special key types
//! \brief \ref error_guessing
TEST_CASE("Parallel test"){
    generate_test<parallel_test, std::tuple<MyMessageKeyWithBrokenKey<int, double>, MyMessageKeyWithoutKey<int, float> >, message_based_key_matching<int> >::do_test();
    generate_test<parallel_test, std::tuple<MyMessageKeyWithoutKeyMethod<int, double>, MyMessageKeyWithBrokenKey<int, float> >, message_based_key_matching<int&> >::do_test();
    generate_test<parallel_test, std::tuple<MyMessageKeyWithoutKey<std::string, double>, MyMessageKeyWithoutKeyMethod<std::string, float> >, message_based_key_matching<std::string&> >::do_test();
}

#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
//! Test deduction guides
//! \brief \ref requirement
TEST_CASE("Deduction guides test"){
    test_deduction_guides();
}
#endif

//! Serial test with special key types
//! \brief \ref error_guessing
TEST_CASE("Serial test"){
    generate_test<serial_test, std::tuple<MyMessageKeyWithBrokenKey<int, double>, MyMessageKeyWithoutKey<int, float> >, message_based_key_matching<int> >::do_test();
    generate_test<serial_test, std::tuple<MyMessageKeyWithoutKeyMethod<std::string, double>, MyMessageKeyWithBrokenKey<std::string, float> >, message_based_key_matching<std::string> >::do_test();
}
<commit_msg>Remove the same test cases to fix MIPS compilation (#412)<commit_after>/*
    Copyright (c) 2005-2021 Intel Corporation

    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.
*/

// Message based key matching is a preview feature
#define TBB_PREVIEW_FLOW_GRAPH_FEATURES 1

#define MAX_TUPLE_TEST_SIZE 10

#include "common/config.h"

#include "test_join_node.h"

//! \file test_join_node_msg_key_matching.cpp
//! \brief Test for [preview] functionality

#if __TBB_PREVIEW_FLOW_GRAPH_NODE_SET
#include <array>
#include <vector>
void test_follows_and_precedes_api() {
    using msg_t = MyMessageKeyWithoutKey<int, int>;
    using JoinOutputType = std::tuple<msg_t, msg_t, msg_t>;

    std::array<msg_t, 3> messages_for_follows = { {msg_t(), msg_t(), msg_t()} };
    std::vector<msg_t> messages_for_precedes = { msg_t(), msg_t(), msg_t() };

    follows_and_precedes_testing::test_follows
        <msg_t, tbb::flow::join_node<JoinOutputType, tbb::flow::key_matching<std::size_t>>, tbb::flow::buffer_node<msg_t>>
        (messages_for_follows);
    follows_and_precedes_testing::test_precedes
        <msg_t, tbb::flow::join_node<JoinOutputType, tbb::flow::key_matching<std::size_t>>>
        (messages_for_precedes);
}
#endif

#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
struct message_key {
    int my_key;
    double my_value;

    int key() const { return my_key; }

    operator size_t() const { return my_key; }

    bool operator==(const message_key& rhs) { return my_value == rhs.my_value; }
};

void test_deduction_guides() {
    using namespace tbb::flow;
    using tuple_type = std::tuple<message_key, message_key>;

    graph g;
    broadcast_node<message_key> bm1(g), bm2(g);
    broadcast_node<tuple_type> bm3(g);
    join_node<tuple_type, key_matching<int> > j0(g);
    join_node j3(j0);
    static_assert(std::is_same_v<decltype(j3), join_node<tuple_type, key_matching<int>>>);
}
#endif

template <typename T1, typename T2>
using make_tuple = decltype(std::tuple_cat(T1(), std::tuple<T2>()));
using T1 = std::tuple<MyMessageKeyWithoutKeyMethod<std::string, double>>;
using T2 = make_tuple<T1, MyMessageKeyWithBrokenKey<std::string, int>>;
using T3 = make_tuple < T2, MyMessageKeyWithoutKey<std::string, int>>;
using T4 = make_tuple < T3, MyMessageKeyWithoutKeyMethod<std::string, size_t>>;
using T5 = make_tuple < T4, MyMessageKeyWithBrokenKey<std::string, int>>;
using T6 = make_tuple < T5, MyMessageKeyWithoutKeyMethod<std::string, short>>;
using T7 = make_tuple < T6, MyMessageKeyWithoutKeyMethod<std::string, threebyte>>;
using T8 = make_tuple < T7, MyMessageKeyWithBrokenKey<std::string, int>>;
using T9 = make_tuple < T8, MyMessageKeyWithoutKeyMethod<std::string, threebyte>>;
using T10 = make_tuple < T9, MyMessageKeyWithBrokenKey<std::string, size_t>>;

#if TBB_TEST_LOW_WORKLOAD && TBB_USE_DEBUG
// the compiler might generate huge object file in debug (>64M)
#define TEST_CASE_TEMPLATE_N_ARGS(dec) TEST_CASE_TEMPLATE(dec, T, T2, T3, T4, T5, T6, T7, T8, T9, T10)
#else
#define TEST_CASE_TEMPLATE_N_ARGS(dec) TEST_CASE_TEMPLATE(dec, T, T2, T10)
#endif

//! Serial test with different tuple sizes
//! \brief \ref error_guessing
TEST_CASE_TEMPLATE_N_ARGS("Serial N tests") {
    generate_test<serial_test, T, message_based_key_matching<std::string&> >::do_test();
}

//! Parallel test with different tuple sizes
//! \brief \ref error_guessing
TEST_CASE_TEMPLATE_N_ARGS("Parallel N tests") {
    generate_test<parallel_test, T, message_based_key_matching<std::string&> >::do_test();
}

//! Serial test with matching policies
//! \brief \ref error_guessing
TEST_CASE("Serial test") {
    generate_test<serial_test, std::tuple<MyMessageKeyWithBrokenKey<int, double>, MyMessageKeyWithoutKey<int, float> >, message_based_key_matching<int> >::do_test();
    generate_test<serial_test, std::tuple<MyMessageKeyWithoutKeyMethod<std::string, double>, MyMessageKeyWithBrokenKey<std::string, float> >, message_based_key_matching<std::string> >::do_test();
}

//! Parallel test with special key types
//! \brief \ref error_guessing
TEST_CASE("Parallel test"){
    generate_test<parallel_test, std::tuple<MyMessageKeyWithBrokenKey<int, double>, MyMessageKeyWithoutKey<int, float> >, message_based_key_matching<int> >::do_test();
    generate_test<parallel_test, std::tuple<MyMessageKeyWithoutKeyMethod<int, double>, MyMessageKeyWithBrokenKey<int, float> >, message_based_key_matching<int&> >::do_test();
    generate_test<parallel_test, std::tuple<MyMessageKeyWithoutKey<std::string, double>, MyMessageKeyWithoutKeyMethod<std::string, float> >, message_based_key_matching<std::string&> >::do_test();
}

#if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
//! Test deduction guides
//! \brief \ref requirement
TEST_CASE("Deduction guides test"){
    test_deduction_guides();
}
#endif
<|endoftext|>


<commit_before>/******************************************************************************\
 *           ___        __                                                    *
 *          /\_ \    __/\ \                                                   *
 *          \//\ \  /\_\ \ \____    ___   _____   _____      __               *
 *            \ \ \ \/\ \ \ '__`\  /'___\/\ '__`\/\ '__`\  /'__`\             *
 *             \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_           *
 *             /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\          *
 *             \/____/ \/_/\/___/  \/____/ \ \ \/  \ \ \/  \/__/\/_/          *
 *                                          \ \_\   \ \_\                     *
 *                                           \/_/    \/_/                     *
 *                                                                            *
 * Copyright (C) 2011, 2012                                                   *
 * Dominik Charousset <dominik.charousset@haw-hamburg.de>                     *
 *                                                                            *
 * This file is part of libcppa.                                              *
 * libcppa is free software: you can redistribute it and/or modify it under   *
 * the terms of the GNU Lesser General Public License as published by the     *
 * Free Software Foundation, either version 3 of the License                  *
 * or (at your option) any later version.                                     *
 *                                                                            *
 * libcppa is distributed in the hope that it will be useful,                 *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                       *
 * See the GNU Lesser General Public License for more details.                *
 *                                                                            *
 * You should have received a copy of the GNU Lesser General Public License   *
 * along with libcppa. If not, see <http://www.gnu.org/licenses/>.            *
\******************************************************************************/

#ifndef LIBCPPA_PATTERN_HPP
#define LIBCPPA_PATTERN_HPP

#include <vector>
#include <cstddef>
#include <type_traits>

#include "cppa/anything.hpp"
#include "cppa/any_tuple.hpp"
#include "cppa/uniform_type_info.hpp"

#include "cppa/util/tbind.hpp"
#include "cppa/util/type_list.hpp"

#include "cppa/detail/boxed.hpp"
#include "cppa/detail/tdata.hpp"
#include "cppa/detail/types_array.hpp"

namespace cppa {

template<typename... Types>
class pattern
{

    static_assert(sizeof...(Types) > 0, "empty pattern");

    pattern(pattern const&) = delete;
    pattern& operator=(pattern const&) = delete;

 public:

    static constexpr size_t size = sizeof...(Types);

    typedef util::type_list<Types...> types;

    typedef typename util::lt_filter_not<types, util::tbind<std::is_same, anything>::type>::type
            filtered_types;

    typedef typename tuple_view_type_from_type_list<filtered_types>::type
            tuple_view_type;

    typedef typename tuple_view_type::mapping_vector mapping_vector;

    class const_iterator
    {

        pattern const* m_ptr;
        size_t m_pos;

     public:

        const_iterator(pattern const* ptr) : m_ptr(ptr), m_pos(0) { }

        const_iterator(const_iterator const&) = default;

        const_iterator& operator=(const_iterator const&) = default;

        const_iterator& next()
        {
            ++m_pos;
            return *this;
        }

        inline bool at_end()
        {
            return m_pos == sizeof...(Types);
        }

        inline uniform_type_info const* type() const
        {
            return m_ptr->m_utis[m_pos];
        }

        inline bool has_value() const
        {
            return m_ptr->m_data_ptr[m_pos] != nullptr;
        }

        inline void const* value() const
        {
            return m_ptr->m_data_ptr[m_pos];
        }

    };

    const_iterator begin() const
    {
        return {this};
    }

    pattern()
    {
        for (size_t i = 0; i < size; ++i)
        {
            m_data_ptr[i] = nullptr;
        }
    }

    template<typename Arg0, typename... Args>
    pattern(Arg0 const& arg0, Args const&... args) : m_data(arg0, args...)
    {
        bool invalid_args[] = { detail::is_boxed<Arg0>::value,
                                detail::is_boxed<Args>::value... };
        for (size_t i = 0; i < sizeof...(Args) + 1; ++i)
        {
            m_data_ptr[i] = invalid_args[i] ? nullptr : m_data.at(i);
        }
        for (size_t i = sizeof...(Args) + 1; i < size; ++i)
        {
            m_data_ptr[i] = nullptr;
        }
    }

    detail::tdata<Types...> m_data;
    static detail::types_array<Types...> m_utis;
    void const* m_data_ptr[size];

};

template<typename... Types>
detail::types_array<Types...> pattern<Types...>::m_utis;

template<class ExtendedType, class BasicType>
ExtendedType* extend_pattern(BasicType const* p)
{
    ExtendedType* et = new ExtendedType;
    detail::tdata_set(et->m_data, p->m_data);
    for (size_t i = 0; i < BasicType::size; ++i)
    {
        if (p->m_data_ptr[i] != nullptr)
        {
            et->m_data_ptr[i] = et->m_data.at(i);
        }
    }
    return et;
}

template<typename TypeList>
struct pattern_from_type_list;

template<typename... Types>
struct pattern_from_type_list<util::type_list<Types...>>
{
    typedef pattern<Types...> type;
};

} // namespace cppa

#endif // PATTERN_HPP
<commit_msg>fixed range check<commit_after>/******************************************************************************\
 *           ___        __                                                    *
 *          /\_ \    __/\ \                                                   *
 *          \//\ \  /\_\ \ \____    ___   _____   _____      __               *
 *            \ \ \ \/\ \ \ '__`\  /'___\/\ '__`\/\ '__`\  /'__`\             *
 *             \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_           *
 *             /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\          *
 *             \/____/ \/_/\/___/  \/____/ \ \ \/  \ \ \/  \/__/\/_/          *
 *                                          \ \_\   \ \_\                     *
 *                                           \/_/    \/_/                     *
 *                                                                            *
 * Copyright (C) 2011, 2012                                                   *
 * Dominik Charousset <dominik.charousset@haw-hamburg.de>                     *
 *                                                                            *
 * This file is part of libcppa.                                              *
 * libcppa is free software: you can redistribute it and/or modify it under   *
 * the terms of the GNU Lesser General Public License as published by the     *
 * Free Software Foundation, either version 3 of the License                  *
 * or (at your option) any later version.                                     *
 *                                                                            *
 * libcppa is distributed in the hope that it will be useful,                 *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                       *
 * See the GNU Lesser General Public License for more details.                *
 *                                                                            *
 * You should have received a copy of the GNU Lesser General Public License   *
 * along with libcppa. If not, see <http://www.gnu.org/licenses/>.            *
\******************************************************************************/

#ifndef LIBCPPA_PATTERN_HPP
#define LIBCPPA_PATTERN_HPP

#include <vector>
#include <cstddef>
#include <type_traits>

#include "cppa/anything.hpp"
#include "cppa/any_tuple.hpp"
#include "cppa/uniform_type_info.hpp"

#include "cppa/util/tbind.hpp"
#include "cppa/util/type_list.hpp"
#include "cppa/util/arg_match_t.hpp"

#include "cppa/detail/boxed.hpp"
#include "cppa/detail/tdata.hpp"
#include "cppa/detail/types_array.hpp"

namespace cppa {

template<typename... Types>
class pattern
{

    static_assert(sizeof...(Types) > 0, "empty pattern");

    pattern(pattern const&) = delete;
    pattern& operator=(pattern const&) = delete;

 public:

    static constexpr size_t size = sizeof...(Types);

    typedef util::type_list<Types...> types;

    typedef typename util::lt_filter_not<types, util::tbind<std::is_same, anything>::type>::type
            filtered_types;

    typedef typename tuple_view_type_from_type_list<filtered_types>::type
            tuple_view_type;

    typedef typename tuple_view_type::mapping_vector mapping_vector;

    class const_iterator
    {

        pattern const* m_ptr;
        size_t m_pos;

     public:

        const_iterator(pattern const* ptr) : m_ptr(ptr), m_pos(0) { }

        const_iterator(const_iterator const&) = default;

        const_iterator& operator=(const_iterator const&) = default;

        const_iterator& next()
        {
            ++m_pos;
            return *this;
        }

        inline bool at_end()
        {
            return m_pos == sizeof...(Types);
        }

        inline uniform_type_info const* type() const
        {
            return m_ptr->m_utis[m_pos];
        }

        inline bool has_value() const
        {
            return m_ptr->m_data_ptr[m_pos] != nullptr;
        }

        inline void const* value() const
        {
            return m_ptr->m_data_ptr[m_pos];
        }

    };

    const_iterator begin() const
    {
        return {this};
    }

    pattern()
    {
        for (size_t i = 0; i < size; ++i)
        {
            m_data_ptr[i] = nullptr;
        }
    }

    template<typename Arg0, typename... Args>
    pattern(Arg0 const& arg0, Args const&... args) : m_data(arg0, args...)
    {
        using std::is_same;
        using namespace util;
        typedef typename type_list<Arg0, Args...>::back arg_n;
        static constexpr bool ignore_arg_n = is_same<arg_n, arg_match_t>::value;
        bool invalid_args[] = { detail::is_boxed<Arg0>::value,
                                detail::is_boxed<Args>::value... };
        // ignore extra arg_match_t argument
        static constexpr size_t args_size = sizeof...(Args)
                                          + (ignore_arg_n ? 0 : 1);
        static_assert(args_size <= size, "too many arguments");
        for (size_t i = 0; i < args_size; ++i)
        {
            m_data_ptr[i] = invalid_args[i] ? nullptr : m_data.at(i);
        }
        for (size_t i = args_size; i < size; ++i)
        {
            m_data_ptr[i] = nullptr;
        }
    }

    detail::tdata<Types...> m_data;
    static detail::types_array<Types...> m_utis;
    void const* m_data_ptr[size];

};

template<typename... Types>
detail::types_array<Types...> pattern<Types...>::m_utis;

template<class ExtendedType, class BasicType>
ExtendedType* extend_pattern(BasicType const* p)
{
    ExtendedType* et = new ExtendedType;
    detail::tdata_set(et->m_data, p->m_data);
    for (size_t i = 0; i < BasicType::size; ++i)
    {
        if (p->m_data_ptr[i] != nullptr)
        {
            et->m_data_ptr[i] = et->m_data.at(i);
        }
    }
    return et;
}

template<typename TypeList>
struct pattern_from_type_list;

template<typename... Types>
struct pattern_from_type_list<util::type_list<Types...>>
{
    typedef pattern<Types...> type;
};

} // namespace cppa

#endif // PATTERN_HPP
<|endoftext|>


<commit_before>#pragma once
#include <iostream>
#include <utility>
#include <vector>

namespace std {

template<typename T>
std::istream& operator>>(std::istream& in, std::vector<T>& vec) {
	for (auto& it : vec) {
		in >> it;
	}
	return in;
}

template<typename T, typename U>
std::istream& operator>>(std::istream& in, std::pair<T, U>& rhs) {
	in >> rhs.first >> rhs.second;
	return in;
}

void assign_files_input_txt() {
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
}

}
<commit_msg>Minor refactoring in cpplib/io.<commit_after>#pragma once
#include <iostream>
#include <utility>
#include <vector>

namespace std {

template<typename T>
std::istream& operator>>(std::istream& in, std::vector<T>& vec) {
	for (auto& it : vec) {
		in >> it;
	}
	return in;
}

template<typename T, typename U>
std::istream& operator>>(std::istream& in, std::pair<T, U>& rhs) {
	in >> rhs.first >> rhs.second;
	return in;
}

}  // namespace std

void assign_files_input_txt() {
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
}
<|endoftext|>


<commit_before>/*
	Flexisip, a flexible SIP proxy server with media capabilities.
    Copyright (C) 2010  Belledonne Communications SARL.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef configmanager_hh
#define configmanager_hh
#if defined(HAVE_CONFIG_H) && !defined(FLEXISIP_INCLUDED)
#undef PACKAGE_BUGREPORT
#undef PACKAGE_NAME
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#undef PACKAGE_VERSION
#include "flexisip-config.h"
#define FLEXISIP_INCLUDED
#endif
#include <string>
#include <sstream>
#include <list>
#include <cstdlib>
#include <vector>
#include <unordered_set>

#include <algorithm>

#include "common.hh"
#include <typeinfo>

#ifdef ENABLE_SNMP
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#else
typedef unsigned long oid;
#endif

enum GenericValueType{
	Boolean,
	Integer,
	Counter64,
	String,
	StringList,
	Struct
};

struct ConfigItemDescriptor {
	GenericValueType type;
	const char *name;
	const char *help;
	const char *default_value;
};
static const ConfigItemDescriptor config_item_end={Boolean,NULL,NULL,NULL};

struct StatItemDescriptor {
	GenericValueType type;
	const char *name;
	const char *help;
};
static const StatItemDescriptor stat_item_end={Boolean,NULL,NULL};

class Oid {
	friend class GenericEntry;
	friend class StatCounter64;
	friend class ConfigValue;
	friend class GenericStruct;
	friend class RootConfigStruct;
protected:
	Oid(Oid &parent, oid leaf);
	Oid(std::vector<oid> path);
	Oid(std::vector<oid> path, oid leaf);
	std::vector<oid> &getValue() {return mOidPath;}
	virtual ~Oid();
private:
	std::vector<oid> mOidPath;
public:
	std::string getValueAsString(){
		std::ostringstream oss (std::ostringstream::out);
		for (oid i=0; i < mOidPath.size(); ++i) {
			if (i != 0) oss << ".";
			oss << mOidPath[i];
		}
		return oss.str();
	}
	oid getLeaf() { return mOidPath[mOidPath.size()-1]; }
	static oid oidFromHashedString(const std::string &str);
};

class GenericEntry;
class GenericEntriesGetter {
	static GenericEntriesGetter *sInstance;
	std::map<std::string, GenericEntry *> mEntries;
	std::unordered_set<std::string> mKeys;
public:
	static GenericEntriesGetter &get() {
		if (!sInstance) sInstance = new GenericEntriesGetter();
		return *sInstance;
	};
	void registerWithKey(const std::string &key, GenericEntry *stat) {
		if (mKeys.find(key) != mKeys.end()) {
			LOGA("Duplicate entry key %s", key.c_str());
		}
//		LOGD("Register with key %s", key.c_str());
		mEntries.insert(make_pair(key, stat));
	}
	template <typename _retType>
	_retType &find(const std::string &key)const;
};

class GenericEntry {
public:
	static std::string sanitize(const std::string &str);

	const std::string & getName()const{
		return mName;
	}
	GenericValueType getType()const{
		return mType;
	}
	const std::string &getHelp()const{
		return mHelp;
	}
	GenericEntry *getParent()const{
		return mParent;
	}
	virtual ~GenericEntry(){
	}
	virtual void setParent(GenericEntry *parent);
	/*
	 * @returns entry oid built from parent & object oid index
	 */
	Oid& getOid() {return *mOid;};
#ifdef ENABLE_SNMP
	static int sHandleSnmpRequest(netsnmp_mib_handler *handler,
			netsnmp_handler_registration *reginfo,
			netsnmp_agent_request_info   *reqinfo,
			netsnmp_request_info         *requests);
	virtual int handleSnmpRequest(netsnmp_mib_handler *,
			netsnmp_handler_registration *,netsnmp_agent_request_info*,netsnmp_request_info*){return -1;};
#endif
	virtual void mibFragment(std::ostream &ostr, std::string spacing)const=0;
	void registerWithKey(const std::string &key) {
		GenericEntriesGetter::get().registerWithKey(key, this);
	}
protected:
	void doMibFragment(std::ostream &ostr, std::string &syntax, std::string spacing) const;
	GenericEntry(const std::string &name, GenericValueType type, const std::string &help,oid oid_index=0);
	Oid *mOid;
	const std::string mName;
private:
	const std::string mHelp;
	GenericValueType mType;
	GenericEntry *mParent;

	oid mOidLeaf;
};

template <typename _retType>
_retType &GenericEntriesGetter::find(const std::string &key)const{
	auto it=mEntries.find(key);
	if (it == mEntries.end()) {
		LOGA("Entry not found %s", key.c_str());
	}

	GenericEntry *ge=(*it).second;
	_retType *ret=dynamic_cast<_retType *>(ge);
	if (!ret) {
		LOGA("%s - bad entry type %s", key.c_str(), typeid(*ge).name());
	}

	return *ret;
};

class ConfigValue;

class GenericStruct : public GenericEntry{
public:
	GenericStruct(const std::string &name, const std::string &help,oid oid_index);
	GenericEntry * addChild(GenericEntry *c);
	void addChildrenValues(ConfigItemDescriptor *items);
	void addChildrenValues(StatItemDescriptor *items);
	std::list<GenericEntry*> &getChildren();
	template <typename _retType>
	_retType *get(const char *name)const;
	~GenericStruct();
	GenericEntry *find(const char *name)const;
	GenericEntry *findApproximate(const char *name)const;
	void mibFragment(std::ostream & ost, std::string spacing) const;
	virtual void setParent(GenericEntry *parent);
private:
	std::list<GenericEntry*> mEntries;
};

class RootConfigStruct : public GenericStruct {
public:
	RootConfigStruct(const std::string &name, const std::string &help, std::vector<oid> oid_root_prefix);
};

class StatCounter64 : public GenericEntry{
public:
	StatCounter64(const std::string &name, const std::string &help, oid oid_index);
#ifdef ENABLE_SNMP
	virtual int handleSnmpRequest(netsnmp_mib_handler *,
			netsnmp_handler_registration *,netsnmp_agent_request_info*,netsnmp_request_info*);
#endif
	void mibFragment(std::ostream & ost, std::string spacing) const;
	static StatCounter64 &find(const std::string &key) {
		return GenericEntriesGetter::get().find<StatCounter64>(key);
	}
	void setParent(GenericEntry *parent);
	uint64_t read() { return mValue; }
	void set(uint64_t val) { mValue=val; }
	void operator++() {++mValue;};
	void operator++(int count) {mValue+=count;};
	void operator--() {--mValue;};
	void operator--(int count) {mValue-=count;};
private:
	uint64_t mValue;
};

class StatFinishListener {
	std::unordered_set<StatCounter64*> mStatList;
public:
	void addStatCounter(StatCounter64 &stat) {
		mStatList.insert(&stat);
	}
	~StatFinishListener(){
		for(auto it=mStatList.begin(); it != mStatList.end(); ++it) {
			StatCounter64 &s = **it;
			++s;
		}
	}
};

class ConfigValue : public GenericEntry{
public:
	ConfigValue(const std::string &name, GenericValueType  vt, const std::string &help, const std::string &default_value,oid oid_index);
	void set(const std::string &value);
	const std::string &get()const;
	const std::string &getDefault()const;
	void setDefault(const std::string &value);
#ifdef ENABLE_SNMP
	virtual int handleSnmpRequest(netsnmp_mib_handler *,
			netsnmp_handler_registration *,netsnmp_agent_request_info*,netsnmp_request_info*);
#endif
	virtual void setParent(GenericEntry *parent);
private:
	std::string mValue;
	std::string mDefaultValue;

};

class ConfigBoolean : public ConfigValue{
public:
	ConfigBoolean(const std::string &name, const std::string &help, const std::string &default_value,oid oid_index);
	bool read()const;
#ifdef ENABLE_SNMP
	virtual int handleSnmpRequest(netsnmp_mib_handler *,
			netsnmp_handler_registration *,netsnmp_agent_request_info*,netsnmp_request_info*);
#endif
	void mibFragment(std::ostream & ost, std::string spacing)const;
};

class ConfigInt : public ConfigValue{
public:
#ifdef ENABLE_SNMP
	virtual int handleSnmpRequest(netsnmp_mib_handler *,
			netsnmp_handler_registration *,netsnmp_agent_request_info*,netsnmp_request_info*);
#endif
	ConfigInt(const std::string &name, const std::string &help, const std::string &default_value,oid oid_index);
	void mibFragment(std::ostream & ost, std::string spacing) const;
	int read()const;
};

class ConfigString : public ConfigValue{
public:
	ConfigString(const std::string &name, const std::string &help, const std::string &default_value,oid oid_index);
	const std::string & read()const;
	void mibFragment(std::ostream & ost, std::string spacing) const;
};

class ConfigStringList : public ConfigValue{
public:
	ConfigStringList(const std::string &name, const std::string &help, const std::string &default_value,oid oid_index);
	std::list<std::string> read()const;
	void mibFragment(std::ostream & ost, std::string spacing) const;
private:
};

template <typename _retType>
_retType *GenericStruct::get(const char *name)const{
	GenericEntry *e=find(name);
	if (e==NULL) {
		LOGA("No ConfigEntry with name %s in struct %s",name,getName().c_str());
		return NULL;
	}
	_retType *ret=dynamic_cast<_retType *>(e);
	if (ret==NULL){
		LOGA("Config entry %s in struct %s does not have the expected type",name,e->getParent()->getName().c_str());
		return NULL;
	}
	return ret;
};

class FileConfigReader{
public:
	FileConfigReader(GenericStruct *root) : mRoot(root),mCfg(NULL),mHaveUnreads(false){
	}
	int read(const char *filename);
	int reload();
	void checkUnread();
	~FileConfigReader();
private:
	int read2(GenericEntry *entry, int level);
	static void onUnreadItem(void *p, const char *secname, const char *key, int lineno);
	void onUnreadItem(const char *secname, const char *key, int lineno);
	GenericStruct *mRoot;
	struct _LpConfig *mCfg;
	bool mHaveUnreads;
};

class GenericManager{
	friend class ConfigArea;
public:
	static GenericManager *get();
	void declareArea(const char *area_name, const char *help, ConfigItemDescriptor *items);
	int load(const char* configFile);
	GenericStruct *getRoot();
	const GenericStruct *getGlobal();
	void loadStrict();
	StatCounter64 &findStat(const std::string &key);
	void addStat(const std::string &key, StatCounter64 &stat);
private:
	GenericManager();
	static void atexit(); // Don't call directly!
	RootConfigStruct mConfigRoot;
	FileConfigReader mReader;
	static GenericManager *sInstance;
	std::map<std::string,StatCounter64*> mStatMap;
	std::unordered_set<std::string> mStatOids;
};

class FileConfigDumper{
public:
	FileConfigDumper(GenericStruct *root){
		mRoot=root;
	}
	std::ostream &dump(std::ostream & ostr)const;
private:
	std::ostream & printHelp(std::ostream &os, const std::string &help, const std::string &comment_prefix)const;
	std::ostream &dump2(std::ostream & ostr, GenericEntry *entry, int level)const;
	GenericStruct *mRoot;
};

inline std::ostream & operator<<(std::ostream &ostr, const FileConfigDumper &dumper){
	return dumper.dump(ostr);
}

class MibDumper{
public:
	MibDumper(GenericStruct *root){
		mRoot=root;
	}
	std::ostream &dump(std::ostream & ostr)const;
private:
	std::ostream &dump2(std::ostream & ostr, GenericEntry *entry, int level)const;
	GenericStruct *mRoot;
};

inline std::ostream & operator<<(std::ostream &ostr, const MibDumper &dumper){
	return dumper.dump(ostr);
}

#endif
<commit_msg>Correctly delete oid in GenericEntry.<commit_after>/*
	Flexisip, a flexible SIP proxy server with media capabilities.
    Copyright (C) 2010  Belledonne Communications SARL.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef configmanager_hh
#define configmanager_hh
#if defined(HAVE_CONFIG_H) && !defined(FLEXISIP_INCLUDED)
#undef PACKAGE_BUGREPORT
#undef PACKAGE_NAME
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#undef PACKAGE_VERSION
#include "flexisip-config.h"
#define FLEXISIP_INCLUDED
#endif
#include <string>
#include <sstream>
#include <list>
#include <cstdlib>
#include <vector>
#include <unordered_set>

#include <algorithm>

#include "common.hh"
#include <typeinfo>

#ifdef ENABLE_SNMP
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>
#else
typedef unsigned long oid;
#endif

enum GenericValueType{
	Boolean,
	Integer,
	Counter64,
	String,
	StringList,
	Struct
};

struct ConfigItemDescriptor {
	GenericValueType type;
	const char *name;
	const char *help;
	const char *default_value;
};
static const ConfigItemDescriptor config_item_end={Boolean,NULL,NULL,NULL};

struct StatItemDescriptor {
	GenericValueType type;
	const char *name;
	const char *help;
};
static const StatItemDescriptor stat_item_end={Boolean,NULL,NULL};

class Oid {
	friend class GenericEntry;
	friend class StatCounter64;
	friend class ConfigValue;
	friend class GenericStruct;
	friend class RootConfigStruct;
protected:
	Oid(Oid &parent, oid leaf);
	Oid(std::vector<oid> path);
	Oid(std::vector<oid> path, oid leaf);
	std::vector<oid> &getValue() {return mOidPath;}
	virtual ~Oid();
private:
	std::vector<oid> mOidPath;
public:
	std::string getValueAsString(){
		std::ostringstream oss (std::ostringstream::out);
		for (oid i=0; i < mOidPath.size(); ++i) {
			if (i != 0) oss << ".";
			oss << mOidPath[i];
		}
		return oss.str();
	}
	oid getLeaf() { return mOidPath[mOidPath.size()-1]; }
	static oid oidFromHashedString(const std::string &str);
};

class GenericEntry;
class GenericEntriesGetter {
	static GenericEntriesGetter *sInstance;
	std::map<std::string, GenericEntry *> mEntries;
	std::unordered_set<std::string> mKeys;
public:
	static GenericEntriesGetter &get() {
		if (!sInstance) sInstance = new GenericEntriesGetter();
		return *sInstance;
	};
	void registerWithKey(const std::string &key, GenericEntry *stat) {
		if (mKeys.find(key) != mKeys.end()) {
			LOGA("Duplicate entry key %s", key.c_str());
		}
//		LOGD("Register with key %s", key.c_str());
		mEntries.insert(make_pair(key, stat));
	}
	template <typename _retType>
	_retType &find(const std::string &key)const;
};

class GenericEntry {
public:
	static std::string sanitize(const std::string &str);

	const std::string & getName()const{
		return mName;
	}
	GenericValueType getType()const{
		return mType;
	}
	const std::string &getHelp()const{
		return mHelp;
	}
	GenericEntry *getParent()const{
		return mParent;
	}
	virtual ~GenericEntry(){
		if (mOid) delete mOid;
	}
	virtual void setParent(GenericEntry *parent);
	/*
	 * @returns entry oid built from parent & object oid index
	 */
	Oid& getOid() {return *mOid;};
#ifdef ENABLE_SNMP
	static int sHandleSnmpRequest(netsnmp_mib_handler *handler,
			netsnmp_handler_registration *reginfo,
			netsnmp_agent_request_info   *reqinfo,
			netsnmp_request_info         *requests);
	virtual int handleSnmpRequest(netsnmp_mib_handler *,
			netsnmp_handler_registration *,netsnmp_agent_request_info*,netsnmp_request_info*){return -1;};
#endif
	virtual void mibFragment(std::ostream &ostr, std::string spacing)const=0;
	void registerWithKey(const std::string &key) {
		GenericEntriesGetter::get().registerWithKey(key, this);
	}
protected:
	void doMibFragment(std::ostream &ostr, std::string &syntax, std::string spacing) const;
	GenericEntry(const std::string &name, GenericValueType type, const std::string &help,oid oid_index=0);
	Oid *mOid;
	const std::string mName;
private:
	const std::string mHelp;
	GenericValueType mType;
	GenericEntry *mParent;

	oid mOidLeaf;
};

template <typename _retType>
_retType &GenericEntriesGetter::find(const std::string &key)const{
	auto it=mEntries.find(key);
	if (it == mEntries.end()) {
		LOGA("Entry not found %s", key.c_str());
	}

	GenericEntry *ge=(*it).second;
	_retType *ret=dynamic_cast<_retType *>(ge);
	if (!ret) {
		LOGA("%s - bad entry type %s", key.c_str(), typeid(*ge).name());
	}

	return *ret;
};

class ConfigValue;

class GenericStruct : public GenericEntry{
public:
	GenericStruct(const std::string &name, const std::string &help,oid oid_index);
	GenericEntry * addChild(GenericEntry *c);
	void addChildrenValues(ConfigItemDescriptor *items);
	void addChildrenValues(StatItemDescriptor *items);
	std::list<GenericEntry*> &getChildren();
	template <typename _retType>
	_retType *get(const char *name)const;
	~GenericStruct();
	GenericEntry *find(const char *name)const;
	GenericEntry *findApproximate(const char *name)const;
	void mibFragment(std::ostream & ost, std::string spacing) const;
	virtual void setParent(GenericEntry *parent);
private:
	std::list<GenericEntry*> mEntries;
};

class RootConfigStruct : public GenericStruct {
public:
	RootConfigStruct(const std::string &name, const std::string &help, std::vector<oid> oid_root_prefix);
};

class StatCounter64 : public GenericEntry{
public:
	StatCounter64(const std::string &name, const std::string &help, oid oid_index);
#ifdef ENABLE_SNMP
	virtual int handleSnmpRequest(netsnmp_mib_handler *,
			netsnmp_handler_registration *,netsnmp_agent_request_info*,netsnmp_request_info*);
#endif
	void mibFragment(std::ostream & ost, std::string spacing) const;
	static StatCounter64 &find(const std::string &key) {
		return GenericEntriesGetter::get().find<StatCounter64>(key);
	}
	void setParent(GenericEntry *parent);
	uint64_t read() { return mValue; }
	void set(uint64_t val) { mValue=val; }
	void operator++() {++mValue;};
	void operator++(int count) {mValue+=count;};
	void operator--() {--mValue;};
	void operator--(int count) {mValue-=count;};
private:
	uint64_t mValue;
};

class StatFinishListener {
	std::unordered_set<StatCounter64*> mStatList;
public:
	void addStatCounter(StatCounter64 &stat) {
		mStatList.insert(&stat);
	}
	~StatFinishListener(){
		for(auto it=mStatList.begin(); it != mStatList.end(); ++it) {
			StatCounter64 &s = **it;
			++s;
		}
	}
};

class ConfigValue : public GenericEntry{
public:
	ConfigValue(const std::string &name, GenericValueType  vt, const std::string &help, const std::string &default_value,oid oid_index);
	void set(const std::string &value);
	const std::string &get()const;
	const std::string &getDefault()const;
	void setDefault(const std::string &value);
#ifdef ENABLE_SNMP
	virtual int handleSnmpRequest(netsnmp_mib_handler *,
			netsnmp_handler_registration *,netsnmp_agent_request_info*,netsnmp_request_info*);
#endif
	virtual void setParent(GenericEntry *parent);
private:
	std::string mValue;
	std::string mDefaultValue;

};

class ConfigBoolean : public ConfigValue{
public:
	ConfigBoolean(const std::string &name, const std::string &help, const std::string &default_value,oid oid_index);
	bool read()const;
#ifdef ENABLE_SNMP
	virtual int handleSnmpRequest(netsnmp_mib_handler *,
			netsnmp_handler_registration *,netsnmp_agent_request_info*,netsnmp_request_info*);
#endif
	void mibFragment(std::ostream & ost, std::string spacing)const;
};

class ConfigInt : public ConfigValue{
public:
#ifdef ENABLE_SNMP
	virtual int handleSnmpRequest(netsnmp_mib_handler *,
			netsnmp_handler_registration *,netsnmp_agent_request_info*,netsnmp_request_info*);
#endif
	ConfigInt(const std::string &name, const std::string &help, const std::string &default_value,oid oid_index);
	void mibFragment(std::ostream & ost, std::string spacing) const;
	int read()const;
};

class ConfigString : public ConfigValue{
public:
	ConfigString(const std::string &name, const std::string &help, const std::string &default_value,oid oid_index);
	const std::string & read()const;
	void mibFragment(std::ostream & ost, std::string spacing) const;
};

class ConfigStringList : public ConfigValue{
public:
	ConfigStringList(const std::string &name, const std::string &help, const std::string &default_value,oid oid_index);
	std::list<std::string> read()const;
	void mibFragment(std::ostream & ost, std::string spacing) const;
private:
};

template <typename _retType>
_retType *GenericStruct::get(const char *name)const{
	GenericEntry *e=find(name);
	if (e==NULL) {
		LOGA("No ConfigEntry with name %s in struct %s",name,getName().c_str());
		return NULL;
	}
	_retType *ret=dynamic_cast<_retType *>(e);
	if (ret==NULL){
		LOGA("Config entry %s in struct %s does not have the expected type",name,e->getParent()->getName().c_str());
		return NULL;
	}
	return ret;
};

class FileConfigReader{
public:
	FileConfigReader(GenericStruct *root) : mRoot(root),mCfg(NULL),mHaveUnreads(false){
	}
	int read(const char *filename);
	int reload();
	void checkUnread();
	~FileConfigReader();
private:
	int read2(GenericEntry *entry, int level);
	static void onUnreadItem(void *p, const char *secname, const char *key, int lineno);
	void onUnreadItem(const char *secname, const char *key, int lineno);
	GenericStruct *mRoot;
	struct _LpConfig *mCfg;
	bool mHaveUnreads;
};

class GenericManager{
	friend class ConfigArea;
public:
	static GenericManager *get();
	void declareArea(const char *area_name, const char *help, ConfigItemDescriptor *items);
	int load(const char* configFile);
	GenericStruct *getRoot();
	const GenericStruct *getGlobal();
	void loadStrict();
	StatCounter64 &findStat(const std::string &key);
	void addStat(const std::string &key, StatCounter64 &stat);
private:
	GenericManager();
	static void atexit(); // Don't call directly!
	RootConfigStruct mConfigRoot;
	FileConfigReader mReader;
	static GenericManager *sInstance;
	std::map<std::string,StatCounter64*> mStatMap;
	std::unordered_set<std::string> mStatOids;
};

class FileConfigDumper{
public:
	FileConfigDumper(GenericStruct *root){
		mRoot=root;
	}
	std::ostream &dump(std::ostream & ostr)const;
private:
	std::ostream & printHelp(std::ostream &os, const std::string &help, const std::string &comment_prefix)const;
	std::ostream &dump2(std::ostream & ostr, GenericEntry *entry, int level)const;
	GenericStruct *mRoot;
};

inline std::ostream & operator<<(std::ostream &ostr, const FileConfigDumper &dumper){
	return dumper.dump(ostr);
}

class MibDumper{
public:
	MibDumper(GenericStruct *root){
		mRoot=root;
	}
	std::ostream &dump(std::ostream & ostr)const;
private:
	std::ostream &dump2(std::ostream & ostr, GenericEntry *entry, int level)const;
	GenericStruct *mRoot;
};

inline std::ostream & operator<<(std::ostream &ostr, const MibDumper &dumper){
	return dumper.dump(ostr);
}

#endif
<|endoftext|>


<commit_before>// Copyright (c) 2017 Franka Emika GmbH
// Use of this source code is governed by the Apache-2.0 license, see LICENSE
#include "control_loop.h"

#include <pthread.h>

#include <cstring>
#include <exception>
#include <fstream>

#include <franka/exception.h>

#include "motion_generator_traits.h"

// `using std::string_literals::operator""s` produces a GCC warning that cannot be disabled, so we
// have to use `using namespace ...`.
// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65923#c0
using namespace std::string_literals;  // NOLINT (google-build-using-namespace)

namespace franka {

template class ControlLoop<JointPositions>;
template class ControlLoop<JointVelocities>;
template class ControlLoop<CartesianPose>;
template class ControlLoop<CartesianVelocities>;

template <typename T>
constexpr research_interface::robot::Move::Deviation ControlLoop<T>::kDefaultDeviation;

template <typename T>
ControlLoop<T>::ControlLoop(RobotControl& robot,
                            MotionGeneratorCallback motion_callback,
                            ControlCallback control_callback)
    : robot_(robot),
      motion_callback_(std::move(motion_callback)),
      control_callback_(std::move(control_callback)) {
  bool throw_on_error = robot_.realtimeConfig() == RealtimeConfig::kEnforce;
  if (throw_on_error && !hasRealtimeKernel()) {
    throw RealtimeException("libfranka: Running kernel does not have realtime capabilities.");
  }
  setCurrentThreadToRealtime(throw_on_error);
}

template <typename T>
ControlLoop<T>::ControlLoop(RobotControl& robot,
                            ControlCallback control_callback,
                            MotionGeneratorCallback motion_callback)
    : ControlLoop(robot, std::move(motion_callback), std::move(control_callback)) {
  if (!control_callback_) {
    throw std::invalid_argument("libfranka: Invalid control callback given.");
  }
  if (!motion_callback_) {
    throw std::invalid_argument("libfranka: Invalid motion callback given.");
  }

  motion_id_ = robot.startMotion(
      research_interface::robot::Move::ControllerMode::kExternalController,
      MotionGeneratorTraits<T>::kMotionGeneratorMode, kDefaultDeviation, kDefaultDeviation);
}

template <typename T>
ControlLoop<T>::ControlLoop(RobotControl& robot,
                            ControllerMode controller_mode,
                            MotionGeneratorCallback motion_callback)
    : ControlLoop(robot, std::move(motion_callback), {}) {
  if (!motion_callback_) {
    throw std::invalid_argument("libfranka: Invalid motion callback given.");
  }
  research_interface::robot::Move::ControllerMode mode;
  switch (controller_mode) {
    case ControllerMode::kJointImpedance:
      mode = decltype(mode)::kJointImpedance;
      break;
    case ControllerMode::kCartesianImpedance:
      mode = decltype(mode)::kCartesianImpedance;
      break;
    default:
      throw std::invalid_argument("libfranka: Invalid controller mode given.");
  }
  motion_id_ = robot.startMotion(mode, MotionGeneratorTraits<T>::kMotionGeneratorMode,
                                 kDefaultDeviation, kDefaultDeviation);
}

template <typename T>
void ControlLoop<T>::operator()() try {
  RobotState robot_state = robot_.update();
  robot_.throwOnMotionError(robot_state, motion_id_);

  Duration previous_time = robot_state.time;

  research_interface::robot::MotionGeneratorCommand motion_command{};
  if (control_callback_) {
    research_interface::robot::ControllerCommand control_command{};
    while (spinMotion(robot_state, robot_state.time - previous_time, &motion_command) &&
           spinControl(robot_state, robot_state.time - previous_time, &control_command)) {
      previous_time = robot_state.time;
      robot_state = robot_.update(&motion_command, &control_command);
      robot_.throwOnMotionError(robot_state, motion_id_);
    }
    robot_.finishMotion(motion_id_, &motion_command, &control_command);
  } else {
    while (spinMotion(robot_state, robot_state.time - previous_time, &motion_command)) {
      previous_time = robot_state.time;
      robot_state = robot_.update(&motion_command, nullptr);
      robot_.throwOnMotionError(robot_state, motion_id_);
    }
    robot_.finishMotion(motion_id_, &motion_command, nullptr);
  }
} catch (...) {
  try {
    robot_.cancelMotion(motion_id_);
  } catch (...) {
  }
  throw;
}

template <typename T>
bool ControlLoop<T>::spinControl(const RobotState& robot_state,
                                 franka::Duration time_step,
                                 research_interface::robot::ControllerCommand* command) {
  Torques control_output = control_callback_(robot_state, time_step);
  command->tau_J_d = control_output.tau_J;
  return !control_output.motion_finished;
}

template <typename T>
bool ControlLoop<T>::spinMotion(const RobotState& robot_state,
                                franka::Duration time_step,
                                research_interface::robot::MotionGeneratorCommand* command) {
  T motion_output = motion_callback_(robot_state, time_step);
  convertMotion(motion_output, command);
  return !motion_output.motion_finished;
}

template <>
void ControlLoop<JointPositions>::convertMotion(
    const JointPositions& motion,
    research_interface::robot::MotionGeneratorCommand* command) {
  command->q_d = motion.q;
}

template <>
void ControlLoop<JointVelocities>::convertMotion(
    const JointVelocities& motion,
    research_interface::robot::MotionGeneratorCommand* command) {
  command->dq_d = motion.dq;
}

template <>
void ControlLoop<CartesianPose>::convertMotion(
    const CartesianPose& motion,
    research_interface::robot::MotionGeneratorCommand* command) {
  command->O_T_EE_d = motion.O_T_EE;

  if (motion.hasValidElbow()) {
    command->valid_elbow = true;
    command->elbow_d = motion.elbow;
  } else {
    command->valid_elbow = false;
    command->elbow_d = {};
  }
}

template <>
void ControlLoop<CartesianVelocities>::convertMotion(
    const CartesianVelocities& motion,
    research_interface::robot::MotionGeneratorCommand* command) {
  command->O_dP_EE_d = motion.O_dP_EE;

  if (motion.hasValidElbow()) {
    command->valid_elbow = true;
    command->elbow_d = motion.elbow;
  } else {
    command->valid_elbow = false;
    command->elbow_d = {};
  }
}

void setCurrentThreadToRealtime(bool throw_on_error) {
  constexpr int kThreadPriority = 20;
  sched_param thread_param{};
  thread_param.sched_priority = kThreadPriority;
  if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &thread_param) != 0) {
    if (throw_on_error) {
      throw RealtimeException("libfranka: unable to set realtime scheduling: "s + strerror(errno));
    }
  }
}

bool hasRealtimeKernel() {
  std::ifstream realtime("/sys/kernel/realtime", std::ios_base::in);
  bool is_realtime;
  realtime >> is_realtime;
  return is_realtime;
}

}  // namespace franka
<commit_msg>Set thread priority to max.<commit_after>// Copyright (c) 2017 Franka Emika GmbH
// Use of this source code is governed by the Apache-2.0 license, see LICENSE
#include "control_loop.h"

#include <pthread.h>

#include <cstring>
#include <exception>
#include <fstream>

#include <franka/exception.h>

#include "motion_generator_traits.h"

// `using std::string_literals::operator""s` produces a GCC warning that cannot be disabled, so we
// have to use `using namespace ...`.
// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65923#c0
using namespace std::string_literals;  // NOLINT (google-build-using-namespace)

namespace franka {

template class ControlLoop<JointPositions>;
template class ControlLoop<JointVelocities>;
template class ControlLoop<CartesianPose>;
template class ControlLoop<CartesianVelocities>;

template <typename T>
constexpr research_interface::robot::Move::Deviation ControlLoop<T>::kDefaultDeviation;

template <typename T>
ControlLoop<T>::ControlLoop(RobotControl& robot,
                            MotionGeneratorCallback motion_callback,
                            ControlCallback control_callback)
    : robot_(robot),
      motion_callback_(std::move(motion_callback)),
      control_callback_(std::move(control_callback)) {
  bool throw_on_error = robot_.realtimeConfig() == RealtimeConfig::kEnforce;
  if (throw_on_error && !hasRealtimeKernel()) {
    throw RealtimeException("libfranka: Running kernel does not have realtime capabilities.");
  }
  setCurrentThreadToRealtime(throw_on_error);
}

template <typename T>
ControlLoop<T>::ControlLoop(RobotControl& robot,
                            ControlCallback control_callback,
                            MotionGeneratorCallback motion_callback)
    : ControlLoop(robot, std::move(motion_callback), std::move(control_callback)) {
  if (!control_callback_) {
    throw std::invalid_argument("libfranka: Invalid control callback given.");
  }
  if (!motion_callback_) {
    throw std::invalid_argument("libfranka: Invalid motion callback given.");
  }

  motion_id_ = robot.startMotion(
      research_interface::robot::Move::ControllerMode::kExternalController,
      MotionGeneratorTraits<T>::kMotionGeneratorMode, kDefaultDeviation, kDefaultDeviation);
}

template <typename T>
ControlLoop<T>::ControlLoop(RobotControl& robot,
                            ControllerMode controller_mode,
                            MotionGeneratorCallback motion_callback)
    : ControlLoop(robot, std::move(motion_callback), {}) {
  if (!motion_callback_) {
    throw std::invalid_argument("libfranka: Invalid motion callback given.");
  }
  research_interface::robot::Move::ControllerMode mode;
  switch (controller_mode) {
    case ControllerMode::kJointImpedance:
      mode = decltype(mode)::kJointImpedance;
      break;
    case ControllerMode::kCartesianImpedance:
      mode = decltype(mode)::kCartesianImpedance;
      break;
    default:
      throw std::invalid_argument("libfranka: Invalid controller mode given.");
  }
  motion_id_ = robot.startMotion(mode, MotionGeneratorTraits<T>::kMotionGeneratorMode,
                                 kDefaultDeviation, kDefaultDeviation);
}

template <typename T>
void ControlLoop<T>::operator()() try {
  RobotState robot_state = robot_.update();
  robot_.throwOnMotionError(robot_state, motion_id_);

  Duration previous_time = robot_state.time;

  research_interface::robot::MotionGeneratorCommand motion_command{};
  if (control_callback_) {
    research_interface::robot::ControllerCommand control_command{};
    while (spinMotion(robot_state, robot_state.time - previous_time, &motion_command) &&
           spinControl(robot_state, robot_state.time - previous_time, &control_command)) {
      previous_time = robot_state.time;
      robot_state = robot_.update(&motion_command, &control_command);
      robot_.throwOnMotionError(robot_state, motion_id_);
    }
    robot_.finishMotion(motion_id_, &motion_command, &control_command);
  } else {
    while (spinMotion(robot_state, robot_state.time - previous_time, &motion_command)) {
      previous_time = robot_state.time;
      robot_state = robot_.update(&motion_command, nullptr);
      robot_.throwOnMotionError(robot_state, motion_id_);
    }
    robot_.finishMotion(motion_id_, &motion_command, nullptr);
  }
} catch (...) {
  try {
    robot_.cancelMotion(motion_id_);
  } catch (...) {
  }
  throw;
}

template <typename T>
bool ControlLoop<T>::spinControl(const RobotState& robot_state,
                                 franka::Duration time_step,
                                 research_interface::robot::ControllerCommand* command) {
  Torques control_output = control_callback_(robot_state, time_step);
  command->tau_J_d = control_output.tau_J;
  return !control_output.motion_finished;
}

template <typename T>
bool ControlLoop<T>::spinMotion(const RobotState& robot_state,
                                franka::Duration time_step,
                                research_interface::robot::MotionGeneratorCommand* command) {
  T motion_output = motion_callback_(robot_state, time_step);
  convertMotion(motion_output, command);
  return !motion_output.motion_finished;
}

template <>
void ControlLoop<JointPositions>::convertMotion(
    const JointPositions& motion,
    research_interface::robot::MotionGeneratorCommand* command) {
  command->q_d = motion.q;
}

template <>
void ControlLoop<JointVelocities>::convertMotion(
    const JointVelocities& motion,
    research_interface::robot::MotionGeneratorCommand* command) {
  command->dq_d = motion.dq;
}

template <>
void ControlLoop<CartesianPose>::convertMotion(
    const CartesianPose& motion,
    research_interface::robot::MotionGeneratorCommand* command) {
  command->O_T_EE_d = motion.O_T_EE;

  if (motion.hasValidElbow()) {
    command->valid_elbow = true;
    command->elbow_d = motion.elbow;
  } else {
    command->valid_elbow = false;
    command->elbow_d = {};
  }
}

template <>
void ControlLoop<CartesianVelocities>::convertMotion(
    const CartesianVelocities& motion,
    research_interface::robot::MotionGeneratorCommand* command) {
  command->O_dP_EE_d = motion.O_dP_EE;

  if (motion.hasValidElbow()) {
    command->valid_elbow = true;
    command->elbow_d = motion.elbow;
  } else {
    command->valid_elbow = false;
    command->elbow_d = {};
  }
}

void setCurrentThreadToRealtime(bool throw_on_error) {
  const int kThreadPriority = sched_get_priority_max(SCHED_FIFO);
  sched_param thread_param{};
  thread_param.sched_priority = kThreadPriority;
  if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &thread_param) != 0) {
    if (throw_on_error) {
      throw RealtimeException("libfranka: unable to set realtime scheduling: "s + strerror(errno));
    }
  }
}

bool hasRealtimeKernel() {
  std::ifstream realtime("/sys/kernel/realtime", std::ios_base::in);
  bool is_realtime;
  realtime >> is_realtime;
  return is_realtime;
}

}  // namespace franka
<|endoftext|>


<commit_before><commit_msg>wasapi: increase initial silence on debug<commit_after><|endoftext|>


<commit_before>/**
 * @file   NumberOfArgumentsTest.cpp
 * @brief  NumberOfArguments class tester.
 * @author zer0
 * @date   2019-05-16
 */

#include <gtest/gtest.h>
#include <libtbag/tmp/NumberOfArguments.hpp>

#include <type_traits>
#include <sstream>

using namespace libtbag;
using namespace libtbag::tmp;

template <typename ... Args>
static int NumberOfArgumentsTest_Argument1(Args && ... args) TBAG_NOEXCEPT
{
    static_assert(NumberOfTemplateArguments<Args ...>::value == 1, "");
    return get_number_of_arguments(std::forward<Args>(args) ...);
}

template <typename ... Args>
static int NumberOfArgumentsTest_Argument2(Args && ... args) TBAG_NOEXCEPT
{
    static_assert(NumberOfTemplateArguments<Args ...>::value == 2, "");
    return get_number_of_arguments(std::forward<Args>(args) ...);
}

TEST(NumberOfArgumentsTest, Default)
{
    ASSERT_EQ(1, get_number_of_arguments(1));
    ASSERT_EQ(2, get_number_of_arguments("test", "string"));
    ASSERT_EQ(3, get_number_of_arguments("test", "string", 100));
}

TEST(NumberOfArgumentsTest, ParameterPack)
{
    std::stringstream ss;
    ss << "test" << 100 << std::endl;
    ASSERT_EQ(1, NumberOfArgumentsTest_Argument1(ss.str()));
    ASSERT_EQ(2, NumberOfArgumentsTest_Argument2(ss.str(), ss.str()));
}

<commit_msg>Create NumberOfArgumentsTest.SizeOf tester.<commit_after>/**
 * @file   NumberOfArgumentsTest.cpp
 * @brief  NumberOfArguments class tester.
 * @author zer0
 * @date   2019-05-16
 */

#include <gtest/gtest.h>
#include <libtbag/tmp/NumberOfArguments.hpp>

#include <type_traits>
#include <sstream>

using namespace libtbag;
using namespace libtbag::tmp;

template <typename ... Args>
static int NumberOfArgumentsTest_Argument1(Args && ... args) TBAG_NOEXCEPT
{
    static_assert(NumberOfTemplateArguments<Args ...>::value == 1, "");
    return get_number_of_arguments(std::forward<Args>(args) ...);
}

template <typename ... Args>
static int NumberOfArgumentsTest_Argument2(Args && ... args) TBAG_NOEXCEPT
{
    static_assert(NumberOfTemplateArguments<Args ...>::value == 2, "");
    return get_number_of_arguments(std::forward<Args>(args) ...);
}

TEST(NumberOfArgumentsTest, Default)
{
    ASSERT_EQ(1, get_number_of_arguments(1));
    ASSERT_EQ(2, get_number_of_arguments("test", "string"));
    ASSERT_EQ(3, get_number_of_arguments("test", "string", 100));
}

TEST(NumberOfArgumentsTest, ParameterPack)
{
    std::stringstream ss;
    ss << "test" << 100 << std::endl;
    ASSERT_EQ(1, NumberOfArgumentsTest_Argument1(ss.str()));
    ASSERT_EQ(2, NumberOfArgumentsTest_Argument2(ss.str(), ss.str()));
}

template <typename ... Args>
static int NumberOfArgumentsTest_SizeOf(Args && ... args) TBAG_NOEXCEPT
{
    return sizeof...(Args);
}

TEST(NumberOfArgumentsTest, SizeOf)
{
    ASSERT_EQ(1, NumberOfArgumentsTest_SizeOf(1));
    ASSERT_EQ(2, NumberOfArgumentsTest_SizeOf("test", "string"));
    ASSERT_EQ(3, NumberOfArgumentsTest_SizeOf("test", "string", 100));
}

<|endoftext|>


<commit_before>#include "include/planetsuniverse.h"
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QFile>
#include <QColor>
#include <QMessageBox>
#include <qmath.h>

#define ALPHAMASK 0xff000000

PlanetsUniverse::PlanetsUniverse() : selected(0), simspeed(1.0f), stepsPerFrame(100) {}

bool PlanetsUniverse::load(const QString &filename){
    if(!QFile::exists(filename)){
        QMessageBox::warning(NULL, tr("Error loading simulation!"), tr("file \"%1\" does not exist!").arg(filename));
        return false;
    }

    QFile file(filename);

    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
        QMessageBox::warning(NULL, tr("Error loading simulation!"), tr("Unable to open file \"%1\" for reading!").arg(filename));
        return false;
    }

    QXmlStreamReader xml(&file);

    if(!(xml.readNextStartElement() && xml.name() == "planets-3d-universe")){
        QMessageBox::warning(NULL, tr("Error loading simulation!"), tr("\"%1\" is not a valid universe file!").arg(filename));
        return false;
    }

    deleteAll();
    while(xml.readNextStartElement()) {
        if(xml.name() == "planet"){
            Planet planet;

            planet.setMass(xml.attributes().value("mass").toString().toFloat());
            QRgb color = QColor(xml.attributes().value("color").toString()).rgb();

            while(xml.readNextStartElement()){
                if(xml.name() == "position"){
                    planet.position.setX(xml.attributes().value("x").toString().toFloat());
                    planet.position.setY(xml.attributes().value("y").toString().toFloat());
                    planet.position.setZ(xml.attributes().value("z").toString().toFloat());
                    xml.readNext();
                }else if(xml.name() == "velocity"){
                    planet.velocity.setX(xml.attributes().value("x").toString().toFloat() * velocityfac);
                    planet.velocity.setY(xml.attributes().value("y").toString().toFloat() * velocityfac);
                    planet.velocity.setZ(xml.attributes().value("z").toString().toFloat() * velocityfac);
                    xml.readNext();
                }
            }
            addPlanet(planet, color);
            xml.readNext();
        }
    }

    if(xml.hasError()){
        deleteAll();
        QMessageBox::warning(NULL, tr("Error loading simulation!"), tr("Error in file \"%1\": %2").arg(filename).arg(xml.errorString()));
        return false;
    }

    return true;
}

bool PlanetsUniverse::save(const QString &filename){
    QFile file(filename);

    if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
        QMessageBox::warning(NULL, tr("Error saving simulation!"), tr("Unable to open file \"%1\" for writing!").arg(filename));
        return false;
    }

    QXmlStreamWriter xml(&file);

    xml.setAutoFormatting(true);

    xml.writeStartDocument();
    xml.writeStartElement("planets-3d-universe");

    for(QMapIterator<QRgb, Planet> i(planets); i.hasNext();) {
        const Planet &planet = i.next().value();

        xml.writeStartElement("planet");

        xml.writeAttribute("mass", QString::number(planet.mass()));

        xml.writeAttribute("color", QColor(i.key() ^ALPHAMASK).name());

        xml.writeStartElement("position"); {
            xml.writeAttribute("x", QString::number(planet.position.x()));
            xml.writeAttribute("y", QString::number(planet.position.y()));
            xml.writeAttribute("z", QString::number(planet.position.z()));
        } xml.writeEndElement();

        xml.writeStartElement("velocity"); {
            xml.writeAttribute("x", QString::number(planet.velocity.x() / velocityfac));
            xml.writeAttribute("y", QString::number(planet.velocity.y() / velocityfac));
            xml.writeAttribute("z", QString::number(planet.velocity.z() / velocityfac));
        } xml.writeEndElement();

        xml.writeEndElement();
    }
    xml.writeEndElement();

    xml.writeEndDocument();

    if(xml.hasError()){
        QMessageBox::warning(NULL, tr("Error saving simulation!"), tr("Error writing to file \"%1\"!").arg(filename));
        return false;
    }

    return true;
}

void PlanetsUniverse::advance(float time){
    time *= simspeed;
    time /= stepsPerFrame;

    for(int s = 0; s < stepsPerFrame; s++){
        for(QMutableMapIterator<QRgb, Planet> i(planets); i.hasNext();){
            Planet &planet = i.next().value();
            QRgb planetkey = i.key();

            while(i.hasNext()){
                Planet &other = i.next().value();

                QVector3D direction = other.position - planet.position;
                float distance = direction.lengthSquared();

                if(sqrt(distance) < (planet.radius() + other.radius()) * 0.8f){
                    planet.position = (other.position * other.mass() + planet.position * planet.mass()) / (other.mass() + planet.mass());
                    planet.velocity = (other.velocity * other.mass() + planet.velocity * planet.mass()) / (other.mass() + planet.mass());
                    planet.setMass(planet.mass() + other.mass());
                    if(i.key() == selected){
                        selected = planetkey;
                    }
                    i.remove();
                    planet.path.clear();
                }else{
                    float frc = gravityconst * ((other.mass() * planet.mass()) / distance);

                    planet.velocity += direction * frc * time / planet.mass();
                    other.velocity -= direction * frc * time / other.mass();
                }
            }

            i.toFront();
            while(i.hasNext() && i.next().key() != planetkey);

            planet.position += planet.velocity * time;
            planet.updatePath();

        }
    }
}

QRgb PlanetsUniverse::addPlanet(const Planet &planet, QRgb colorhint){
    if(colorhint != 0){
        colorhint = colorhint | ALPHAMASK;
    }else{
        colorhint = qrand() | ALPHAMASK;
    }

    while(planets.contains(colorhint)){
        colorhint = qrand() | ALPHAMASK;
    }

    planets[colorhint] = planet;
    return colorhint;
}

void PlanetsUniverse::deleteAll(){
    planets.clear();
    selected = 0;
}

void PlanetsUniverse::centerAll(){
    QMutableMapIterator<QRgb, Planet> i(planets);
    QVector3D averagePosition, averageVelocity;
    float totalmass = 0.0f;
    while(i.hasNext()) {
        Planet &planet = i.next().value();

        averagePosition += planet.position * planet.mass();
        averageVelocity += planet.velocity * planet.mass();
        totalmass += planet.mass();
    }
    averagePosition /= totalmass;
    averageVelocity /= totalmass;

    i.toFront();
    while(i.hasNext()) {
        Planet &planet = i.next().value();

        planet.position -= averagePosition;
        planet.velocity -= averageVelocity;
        planet.path.clear();
    }
}
<commit_msg>use foreach loop in centerAll() where const is fine.<commit_after>#include "include/planetsuniverse.h"
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QFile>
#include <QColor>
#include <QMessageBox>
#include <qmath.h>

#define ALPHAMASK 0xff000000

PlanetsUniverse::PlanetsUniverse() : selected(0), simspeed(1.0f), stepsPerFrame(100) {}

bool PlanetsUniverse::load(const QString &filename){
    if(!QFile::exists(filename)){
        QMessageBox::warning(NULL, tr("Error loading simulation!"), tr("file \"%1\" does not exist!").arg(filename));
        return false;
    }

    QFile file(filename);

    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
        QMessageBox::warning(NULL, tr("Error loading simulation!"), tr("Unable to open file \"%1\" for reading!").arg(filename));
        return false;
    }

    QXmlStreamReader xml(&file);

    if(!(xml.readNextStartElement() && xml.name() == "planets-3d-universe")){
        QMessageBox::warning(NULL, tr("Error loading simulation!"), tr("\"%1\" is not a valid universe file!").arg(filename));
        return false;
    }

    deleteAll();
    while(xml.readNextStartElement()) {
        if(xml.name() == "planet"){
            Planet planet;

            planet.setMass(xml.attributes().value("mass").toString().toFloat());
            QRgb color = QColor(xml.attributes().value("color").toString()).rgb();

            while(xml.readNextStartElement()){
                if(xml.name() == "position"){
                    planet.position.setX(xml.attributes().value("x").toString().toFloat());
                    planet.position.setY(xml.attributes().value("y").toString().toFloat());
                    planet.position.setZ(xml.attributes().value("z").toString().toFloat());
                    xml.readNext();
                }else if(xml.name() == "velocity"){
                    planet.velocity.setX(xml.attributes().value("x").toString().toFloat() * velocityfac);
                    planet.velocity.setY(xml.attributes().value("y").toString().toFloat() * velocityfac);
                    planet.velocity.setZ(xml.attributes().value("z").toString().toFloat() * velocityfac);
                    xml.readNext();
                }
            }
            addPlanet(planet, color);
            xml.readNext();
        }
    }

    if(xml.hasError()){
        deleteAll();
        QMessageBox::warning(NULL, tr("Error loading simulation!"), tr("Error in file \"%1\": %2").arg(filename).arg(xml.errorString()));
        return false;
    }

    return true;
}

bool PlanetsUniverse::save(const QString &filename){
    QFile file(filename);

    if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
        QMessageBox::warning(NULL, tr("Error saving simulation!"), tr("Unable to open file \"%1\" for writing!").arg(filename));
        return false;
    }

    QXmlStreamWriter xml(&file);

    xml.setAutoFormatting(true);

    xml.writeStartDocument();
    xml.writeStartElement("planets-3d-universe");

    for(QMapIterator<QRgb, Planet> i(planets); i.hasNext();) {
        const Planet &planet = i.next().value();

        xml.writeStartElement("planet");

        xml.writeAttribute("mass", QString::number(planet.mass()));

        xml.writeAttribute("color", QColor(i.key() ^ALPHAMASK).name());

        xml.writeStartElement("position"); {
            xml.writeAttribute("x", QString::number(planet.position.x()));
            xml.writeAttribute("y", QString::number(planet.position.y()));
            xml.writeAttribute("z", QString::number(planet.position.z()));
        } xml.writeEndElement();

        xml.writeStartElement("velocity"); {
            xml.writeAttribute("x", QString::number(planet.velocity.x() / velocityfac));
            xml.writeAttribute("y", QString::number(planet.velocity.y() / velocityfac));
            xml.writeAttribute("z", QString::number(planet.velocity.z() / velocityfac));
        } xml.writeEndElement();

        xml.writeEndElement();
    }
    xml.writeEndElement();

    xml.writeEndDocument();

    if(xml.hasError()){
        QMessageBox::warning(NULL, tr("Error saving simulation!"), tr("Error writing to file \"%1\"!").arg(filename));
        return false;
    }

    return true;
}

void PlanetsUniverse::advance(float time){
    time *= simspeed;
    time /= stepsPerFrame;

    for(int s = 0; s < stepsPerFrame; s++){
        for(QMutableMapIterator<QRgb, Planet> i(planets); i.hasNext();){
            Planet &planet = i.next().value();
            QRgb planetkey = i.key();

            while(i.hasNext()){
                Planet &other = i.next().value();

                QVector3D direction = other.position - planet.position;
                float distance = direction.lengthSquared();

                if(sqrt(distance) < (planet.radius() + other.radius()) * 0.8f){
                    planet.position = (other.position * other.mass() + planet.position * planet.mass()) / (other.mass() + planet.mass());
                    planet.velocity = (other.velocity * other.mass() + planet.velocity * planet.mass()) / (other.mass() + planet.mass());
                    planet.setMass(planet.mass() + other.mass());
                    if(i.key() == selected){
                        selected = planetkey;
                    }
                    i.remove();
                    planet.path.clear();
                }else{
                    float frc = gravityconst * ((other.mass() * planet.mass()) / distance);

                    planet.velocity += direction * frc * time / planet.mass();
                    other.velocity -= direction * frc * time / other.mass();
                }
            }

            i.toFront();
            while(i.hasNext() && i.next().key() != planetkey);

            planet.position += planet.velocity * time;
            planet.updatePath();

        }
    }
}

QRgb PlanetsUniverse::addPlanet(const Planet &planet, QRgb colorhint){
    if(colorhint != 0){
        colorhint = colorhint | ALPHAMASK;
    }else{
        colorhint = qrand() | ALPHAMASK;
    }

    while(planets.contains(colorhint)){
        colorhint = qrand() | ALPHAMASK;
    }

    planets[colorhint] = planet;
    return colorhint;
}

void PlanetsUniverse::deleteAll(){
    planets.clear();
    selected = 0;
}

void PlanetsUniverse::centerAll(){
    QVector3D averagePosition, averageVelocity;
    float totalmass = 0.0f;

    foreach(const Planet &planet, planets){
        averagePosition += planet.position * planet.mass();
        averageVelocity += planet.velocity * planet.mass();
        totalmass += planet.mass();
    }

    averagePosition /= totalmass;
    averageVelocity /= totalmass;

    for(QMutableMapIterator<QRgb, Planet> i(planets); i.hasNext();) {
        Planet &planet = i.next().value();

        planet.position -= averagePosition;
        planet.velocity -= averageVelocity;
        planet.path.clear();
    }
}
<|endoftext|>


<commit_before>/***********************************************************************
 *                                                                   _
 *       _____  _                           ____  _                 |_|
 *      |  _  |/ \   ____  ____ __ ___     / ___\/ \   __   _  ____  _
 *      | |_| || |  / __ \/ __ \\ '_  \ _ / /    | |___\ \ | |/ __ \| |
 *      |  _  || |__. ___/. ___/| | | ||_|\ \___ |  _  | |_| |. ___/| |
 *      |_/ \_|\___/\____|\____||_| |_|    \____/|_| |_|_____|\____||_|
 *
 *      ================================================================
 *                 More than a coder, More than a designer
 *      ================================================================
 *
 *
 *      - Document: image.cpp
 *      - Author: aleen42
 *      - Description: image class for all the image obj
 *      - Create Time: Nov 29th, 2015
 *      - Update Time: Mar 9th, 2016
 *
 **********************************************************************/

#include "opencv_main.h"
#include "common.h"

class Image
{
protected:
	Mat img;											// the container of the image
	const char* path;									// the local path of the image
	Point2f markedPoint;								// the marked point when cropping images, which is (0, 0) by default
	Point2f dropPoint;									// stroing (dx, dy) when cropping images, which is (0, 0) by default

	/* calculate the angle according to 3 points */
	double angle(Point pt1, Point pt2, Point pt0) {
		double dx1 = pt1.x - pt0.x;
		double dy1 = pt1.y - pt0.y;
		double dx2 = pt2.x - pt0.x;
		double dy2 = pt2.y - pt0.y;
		return (dx1 * dx2 + dy1 * dy2) / sqrt((dx1 * dx1 + dy1 * dy1) * (dx2 * dx2 + dy2 * dy2) + 1e-10);
	}

	/* show the detection of squares */
	void debugSquares(vector<vector<Point> > points) {

		for (int i = 0; i < points[0].size(); i++) {
			printf("Point%d: (%d, %d)\n", i, points[0][i].x, points[0][i].y);
		}

		drawContours(img, points, 0, Scalar(255, 0, 0), 1, 8, vector<Vec4i>(), 0, Point());
		// for (int i = 0; i < points.size(); i++) {
		// 	/* draw contour */
		// 	drawContours(img, points, i, Scalar(255, 0, 0), 1, 8, vector<Vec4i>(), 0, Point());

		// 	/* draw bounding rect */
		// 	Rect rect = boundingRect(Mat(points[i]));
		// 	rectangle(img, rect.tl(), rect.br(), cv::Scalar(255, 0, 0), 2, 8, 0);

		// 	/* draw rotated rect */
		// 	RotatedRect minRect = minAreaRect(Mat(points[i]));
		// 	Point2f rect_points[4];
		// 	minRect.points(rect_points);
		// 	for (int j = 0; j < 4; j++) {
		// 		line(img, rect_points[j], rect_points[(j + 1) % 4], Scalar(0, 0, 255), 1, 8);
		// 	}
		// }
	}
public:
	/* constructors of the class */
	Image() {}
	Image(Mat img) : img(img) {
		this->markedPoint = Point2f(0.0, 0.0);
		this->dropPoint = Point2f(0.0, 0.0);
	}

	/* read the image from a local path */
	void readImage(const char* path) {
		/* set the path */
		this->path = path;
		/* read the image according to the path */
		this->img = imread(path);
		/* check existence */
		if (this->img.empty()) {
			ostringstream os;
			os << "Cannot load image from" << path;
			Common::errorPrint(os.str().c_str());
			/* exit */
			exit(-1);
		}
	}

	/* threshold algorithm */
	void thresholdImage(THRESHOLD_TYPE type) {
		Mat gray;
		const double threshold_value = 0.0;
		const double max_binary_value = 255.0;

		/* convert gray */
		cvtColor(this->img, gray, CV_BGR2GRAY);

		threshold(gray, this->img, threshold_value, max_binary_value, type);
	}

	/* detect squares algorithm by karlphillip */
	vector<vector<Point> > detectSquare(bool debug = false) {
		/* blur will enhance edge detection */
		Mat blurred(this->img);

		medianBlur(this->img, blurred, 9);

		Mat gray0(blurred.size(), CV_8U), gray;

		/* reserved Points array */
		vector<vector<Point> > reserved;
		/* contours Points array*/
		vector<vector<Point> > contours;

		/* find squares in every color plane of the image */
		for (int c = 0; c < 3; c++) {
			int ch[] = { c, 0 };

			/* canny ratio which is recommended as 3 by Canny */
			int ratio = 2;
			int lowThreshold = 10;

			mixChannels(&blurred, 1, &gray0, 1, ch, 1);

			/* try several threshold level */
			const int threshold_level = 2;
			for (int l = 0; l < threshold_level; l++) {
				/* Use Canny instead of zero threshold level */
				/* Canny helps to catch squares with gradient shading */
				if (l == 0) {
					Canny(gray0, gray, lowThreshold, ratio * lowThreshold, 3);

					/* Dilage helps to remove potential holes between edge segments */
					dilate(gray, gray, Mat(), Point(-1, -1));
				} else {
					gray = gray0 >= (l + 1) * 255 / threshold_level;
				}

				vector<Vec4i> hierarchy;
				/* find contours and store them in a list */
				findContours(gray, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

				/* draw contours to improve accuracy */
				for (size_t i = 0; i < contours.size(); i++) {
					drawContours(this->img, contours, i, Scalar(0, 0, 0), 1, 8, hierarchy, 0, Point());
				}

				/* test contours */
				vector<Point> approx;
				for (size_t i = 0; i < contours.size(); i++) {
					/* approximate contour with accuracy proportional to the contour perimeter */
					approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true) * 0.02, true);

					/*
					 * Note: absolute value of an area is used becuase
					 *       area may be positive or negative, in accordance
					 *		 with the contour orientation
					 */
					// cout << "size: " << approx.size() << endl;
					// cout << "area: " << fabs(contourArea(Mat(approx))) << endl;
					if (approx.size() == 4 && fabs(contourArea(Mat(approx))) > 1000 && isContourConvex(Mat(approx))) {
						double maxCosine = 0;

						for (int j = 2; j < 5; j++) {
							double cosine = fabs(angle(approx[j % 4], approx[j - 2], approx[j - 1]));
							maxCosine = MAX(maxCosine, cosine);
						}

						reserved.push_back(approx);

						//if (maxCosine < 0.3) {
						//	reserved.push_back(approx);
						//}
					}
				}
			}
		}

		/* check debug option */
 		if (debug && reserved.size() > 0) {
			debugSquares(reserved);
		}

		return reserved;
	}

	/* detect corner */
	vector<Point2f> detectCorner(const char* quantity, bool debug = false, const char* path = "data.json") {
		/* clock */
		time_t start = clock();

		vector<Point2f> Corners;

		/* parameters */
		const int maxCorners = atoi(quantity);
		const double qualityLevels = 0.01;
		const double minDistance = 10;
		const int blockSize = 10;
		const bool useHarrisDetector = false;
		const int r = 5;

		Mat gray;
		cvtColor(this->img, gray, COLOR_BGR2GRAY);

		goodFeaturesToTrack(gray, Corners, maxCorners, qualityLevels, minDistance, Mat(), blockSize, useHarrisDetector);

		cJSON* pointsArray = cJSON_CreateArray();

		double distance = 99999.0;
		Point2f shortestPoint;

		for (size_t i = 0; i < Corners.size(); i++) {
			double x = (double)Corners[i].x;
			double y = (double)Corners[i].y;

			if (debug) {
				circle(this->img, Corners[i], r, Scalar(255, 255, 255), 2, 8);
				// ostringstream os;
				// os << " (" << x << ", " << y << ")";
				// putText(this->img, os.str(), Corners[i], FONT_HERSHEY_SCRIPT_SIMPLEX, 0.6, Scalar(0, 0, 255), 1, 8);
			}

			double newDistance = sqrt((x - this->markedPoint.x) * (x - this->markedPoint.x) + (y - this->markedPoint.y) * (y - this->markedPoint.y));

			if (distance > newDistance) {
				shortestPoint = Point2f(x, y);
				distance = newDistance;
			}

			/* create json */
			cJSON* item = cJSON_CreateObject();
			cJSON_AddNumberToObject(item, "xPrecent",  x / this->img.cols);
			cJSON_AddNumberToObject(item, "yPrecent", y / this->img.rows);
			cJSON_AddNumberToObject(item, "x", x);
			cJSON_AddNumberToObject(item, "y", y);

			cJSON_AddItemToArray(pointsArray, item);
		}

		time_t end = clock();
		cJSON* data = cJSON_CreateObject();
		const char* duration = (Common::doubleToStr(double(end - start) / CLOCKS_PER_SEC) + "s").c_str();
		cJSON_AddStringToObject(data, "time", duration);

		cJSON_AddNumberToObject(data, "width", this->img.cols);
		cJSON_AddNumberToObject(data, "height", this->img.rows);

		cJSON* item = cJSON_CreateObject();
		cJSON_AddNumberToObject(item, "x", shortestPoint.x);
		cJSON_AddNumberToObject(item, "y", shortestPoint.y);
		cJSON_AddItemToObject(data, "neartestPoint", item);

		item = cJSON_CreateObject();
		cJSON_AddNumberToObject(item, "x", dropPoint.x);
		cJSON_AddNumberToObject(item, "y", dropPoint.y);
		cJSON_AddItemToObject(data, "dropValue", item);

		/* write the data.json */
		ofstream file;
		file.open(path, ios::out);

		if (file.is_open()) {
			cJSON_AddItemToObject(data, "data", pointsArray);
			file << cJSON_Print(data);
			file.close();
		}

		Common::successPrint(data);

		return Corners;
	}

	Image cropImageWithCircle(Point2f center, double radius) {
		/* initiate marked point */
		this->markedPoint = Point2f(center.x, center.y);

		/* for calculate x and y to see whether the start point is outside the image you want to crop */
		double x = this->markedPoint.x - radius;
		x = x < 0 ? 0.0 : x;
		double y = this->markedPoint.y - radius;
		y = y < 0 ? 0.0 : y;

		/* if the start point is outside the image, the radius should be change relatively */
		double realWidth = x == 0.0 ? this->markedPoint.x + radius : radius * 2;
		double realHeight = y == 0.0 ? this->markedPoint.y + radius : radius * 2;

		/* initiate the drop value */
		this->dropPoint = Point2f(x, y);

		/* initiate the markedPoint value of the new Image */
		this->markedPoint = Point2f(this->markedPoint.x - x, this->markedPoint.y - y);

		this->img = Mat(this->img, Rect(x, y, realWidth, realHeight));
		return *this;
	}

	void fastDetectCorner(bool debug = false) {
		vector<KeyPoint> keyPoints;

		FAST(this->img, keyPoints, 10, true, FastFeatureDetector::TYPE_7_12);

		if (debug) {
			drawKeypoints(this->img, keyPoints, this->img, Scalar(255, 255, 255), DrawMatchesFlags::DRAW_OVER_OUTIMG);
		}
	}

	/* write the image to the local path */
	void writeImage(const char* path) {
		/* save the image */
		imwrite(path, this->img);
	}

	/* a mothod to show the image */
	void showImage() {
		/* open the image */
		imshow("image", this->img);
		/* wait for the return */
		waitKey();
	}

	/* container */
	/* image */
	Mat getImage() {
		return this->img;
	}

	/* properties */
	/* width */
	int getWidth() {
		return this->img.cols;
	}

	/* height */
	int getHeight() {
		return this->img.rows;
	}

	/* size */
	Size getSize() {
		return this->img.size();
	}

	/* pointer to the matrix */
	uchar* getPtr(int n) {
		return this->img.ptr<uchar>(n);
	}

	/* set markedPoints */
	Image setMarked(float x, float y) {
		this->markedPoint = Point2f(x, y);

		return *this;
	}
};
<commit_msg>update<commit_after>/***********************************************************************
 *                                                                   _
 *       _____  _                           ____  _                 |_|
 *      |  _  |/ \   ____  ____ __ ___     / ___\/ \   __   _  ____  _
 *      | |_| || |  / __ \/ __ \\ '_  \ _ / /    | |___\ \ | |/ __ \| |
 *      |  _  || |__. ___/. ___/| | | ||_|\ \___ |  _  | |_| |. ___/| |
 *      |_/ \_|\___/\____|\____||_| |_|    \____/|_| |_|_____|\____||_|
 *
 *      ================================================================
 *                 More than a coder, More than a designer
 *      ================================================================
 *
 *
 *      - Document: image.cpp
 *      - Author: aleen42
 *      - Description: image class for all the image obj
 *      - Create Time: Nov 29th, 2015
 *      - Update Time: Mar 9th, 2016
 *
 **********************************************************************/

#include "opencv_main.h"
#include "common.h"

class Image
{
protected:
	Mat img;											// the container of the image
	const char* path;									// the local path of the image
	Point2f markedPoint;								// the marked point when cropping images, which is (0, 0) by default
	Point2f dropPoint;									// stroing (dx, dy) when cropping images, which is (0, 0) by default

	/* calculate the angle according to 3 points */
	double angle(Point pt1, Point pt2, Point pt0) {
		double dx1 = pt1.x - pt0.x;
		double dy1 = pt1.y - pt0.y;
		double dx2 = pt2.x - pt0.x;
		double dy2 = pt2.y - pt0.y;
		return (dx1 * dx2 + dy1 * dy2) / sqrt((dx1 * dx1 + dy1 * dy1) * (dx2 * dx2 + dy2 * dy2) + 1e-10);
	}

	/* show the detection of squares */
	void debugSquares(vector<vector<Point> > points) {

		for (int i = 0; i < points[0].size(); i++) {
			printf("Point%d: (%d, %d)\n", i, points[0][i].x, points[0][i].y);
		}

		drawContours(img, points, 0, Scalar(255, 0, 0), 1, 8, vector<Vec4i>(), 0, Point());
		// for (int i = 0; i < points.size(); i++) {
		// 	/* draw contour */
		// 	drawContours(img, points, i, Scalar(255, 0, 0), 1, 8, vector<Vec4i>(), 0, Point());

		// 	/* draw bounding rect */
		// 	Rect rect = boundingRect(Mat(points[i]));
		// 	rectangle(img, rect.tl(), rect.br(), cv::Scalar(255, 0, 0), 2, 8, 0);

		// 	/* draw rotated rect */
		// 	RotatedRect minRect = minAreaRect(Mat(points[i]));
		// 	Point2f rect_points[4];
		// 	minRect.points(rect_points);
		// 	for (int j = 0; j < 4; j++) {
		// 		line(img, rect_points[j], rect_points[(j + 1) % 4], Scalar(0, 0, 255), 1, 8);
		// 	}
		// }
	}
public:
	/* constructors of the class */
	Image() {}
	Image(Mat img) : img(img) {
		this->markedPoint = Point2f(0.0, 0.0);
		this->dropPoint = Point2f(0.0, 0.0);
	}

	/* read the image from a local path */
	void readImage(const char* path) {
		/* set the path */
		this->path = path;
		/* read the image according to the path */
		this->img = imread(path);
		/* check existence */
		if (this->img.empty()) {
			ostringstream os;
			os << "Cannot load image from" << path;
			Common::errorPrint(os.str().c_str());
			/* exit */
			exit(-1);
		}
	}

	/* threshold algorithm */
	void thresholdImage(THRESHOLD_TYPE type) {
		Mat gray;
		const double threshold_value = 0.0;
		const double max_binary_value = 255.0;

		/* convert gray */
		cvtColor(this->img, gray, CV_BGR2GRAY);

		threshold(gray, this->img, threshold_value, max_binary_value, type);
	}

	/* detect squares algorithm by karlphillip */
	vector<vector<Point> > detectSquare(bool debug = false) {
		/* blur will enhance edge detection */
		Mat blurred(this->img);

		medianBlur(this->img, blurred, 9);

		Mat gray0(blurred.size(), CV_8U), gray;

		/* reserved Points array */
		vector<vector<Point> > reserved;
		/* contours Points array*/
		vector<vector<Point> > contours;

		/* find squares in every color plane of the image */
		for (int c = 0; c < 3; c++) {
			int ch[] = { c, 0 };

			/* canny ratio which is recommended as 3 by Canny */
			int ratio = 2;
			int lowThreshold = 10;

			mixChannels(&blurred, 1, &gray0, 1, ch, 1);

			/* try several threshold level */
			const int threshold_level = 2;
			for (int l = 0; l < threshold_level; l++) {
				/* Use Canny instead of zero threshold level */
				/* Canny helps to catch squares with gradient shading */
				if (l == 0) {
					Canny(gray0, gray, lowThreshold, ratio * lowThreshold, 3);

					/* Dilage helps to remove potential holes between edge segments */
					dilate(gray, gray, Mat(), Point(-1, -1));
				} else {
					gray = gray0 >= (l + 1) * 255 / threshold_level;
				}

				vector<Vec4i> hierarchy;
				/* find contours and store them in a list */
				findContours(gray, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

				/* draw contours to improve accuracy */
				for (size_t i = 0; i < contours.size(); i++) {
					drawContours(this->img, contours, i, Scalar(0, 0, 0), 1, 8, hierarchy, 0, Point());
				}

				/* test contours */
				vector<Point> approx;
				for (size_t i = 0; i < contours.size(); i++) {
					/* approximate contour with accuracy proportional to the contour perimeter */
					approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true) * 0.02, true);

					/*
					 * Note: absolute value of an area is used becuase
					 *       area may be positive or negative, in accordance
					 *		 with the contour orientation
					 */
					// cout << "size: " << approx.size() << endl;
					// cout << "area: " << fabs(contourArea(Mat(approx))) << endl;
					if (approx.size() == 4 && fabs(contourArea(Mat(approx))) > 1000 && isContourConvex(Mat(approx))) {
						double maxCosine = 0;

						for (int j = 2; j < 5; j++) {
							double cosine = fabs(angle(approx[j % 4], approx[j - 2], approx[j - 1]));
							maxCosine = MAX(maxCosine, cosine);
						}

						reserved.push_back(approx);

						//if (maxCosine < 0.3) {
						//	reserved.push_back(approx);
						//}
					}
				}
			}
		}

		/* check debug option */
 		if (debug && reserved.size() > 0) {
			debugSquares(reserved);
		}

		return reserved;
	}

	/* detect corner */
	vector<Point2f> detectCorner(const char* quantity, bool debug = false, const char* path = "data.json", bool outputJSON = true) {
		/* clock */
		time_t start = clock();

		vector<Point2f> Corners;

		/* parameters */
		const int maxCorners = atoi(quantity);
		const double qualityLevels = 0.01;
		const double minDistance = 10;
		const int blockSize = 10;
		const bool useHarrisDetector = false;
		const int r = 5;

		Mat gray;
		cvtColor(this->img, gray, COLOR_BGR2GRAY);

		goodFeaturesToTrack(gray, Corners, maxCorners, qualityLevels, minDistance, Mat(), blockSize, useHarrisDetector);

		cJSON* pointsArray = cJSON_CreateArray();

		double distance = 99999.0;
		Point2f shortestPoint;

		for (size_t i = 0; i < Corners.size(); i++) {
			double x = (double)Corners[i].x;
			double y = (double)Corners[i].y;

			if (debug) {
				circle(this->img, Corners[i], r, Scalar(255, 255, 255), 2, 8);
				ostringstream os;
				os << " (" << x << ", " << y << ")";
				putText(this->img, os.str(), Corners[i], FONT_HERSHEY_SCRIPT_SIMPLEX, 0.6, Scalar(0, 0, 255), 1, 8);
			}

			double newDistance = sqrt((x - this->markedPoint.x) * (x - this->markedPoint.x) + (y - this->markedPoint.y) * (y - this->markedPoint.y));

			if (distance > newDistance) {
				shortestPoint = Point2f(x, y);
				distance = newDistance;
			}

			/* create json */
			cJSON* item = cJSON_CreateObject();
			cJSON_AddNumberToObject(item, "xPrecent",  x / this->img.cols);
			cJSON_AddNumberToObject(item, "yPrecent", y / this->img.rows);
			cJSON_AddNumberToObject(item, "x", x);
			cJSON_AddNumberToObject(item, "y", y);

			cJSON_AddItemToArray(pointsArray, item);
		}

		time_t end = clock();
		cJSON* data = cJSON_CreateObject();
		const char* duration = (Common::doubleToStr(double(end - start) / CLOCKS_PER_SEC) + "s").c_str();
		cJSON_AddStringToObject(data, "time", duration);

		cJSON_AddNumberToObject(data, "width", this->img.cols);
		cJSON_AddNumberToObject(data, "height", this->img.rows);

		cJSON* item = cJSON_CreateObject();
		cJSON_AddNumberToObject(item, "x", shortestPoint.x);
		cJSON_AddNumberToObject(item, "y", shortestPoint.y);
		cJSON_AddItemToObject(data, "neartestPoint", item);

		item = cJSON_CreateObject();
		cJSON_AddNumberToObject(item, "x", dropPoint.x);
		cJSON_AddNumberToObject(item, "y", dropPoint.y);
		cJSON_AddItemToObject(data, "dropValue", item);

		/* write the data.json */
		ofstream file;
		file.open(path, ios::out);

		if (file.is_open()) {
			cJSON_AddItemToObject(data, "data", pointsArray);
			file << cJSON_Print(data);
			file.close();
		}

		if (outputJSON) {
			Common::successPrint(data);
		}

		return Corners;
	}

	Image cropImageWithCircle(Point2f center, double width, double height) {
		/* initiate marked point */
		this->markedPoint = Point2f(center.x, center.y);

		/* for calculate x and y to see whether the start point is outside the image you want to crop */
		double x = this->markedPoint.x - width;
		x = x < 0 ? 0.0 : x;
		double y = this->markedPoint.y - height;
		y = y < 0 ? 0.0 : y;

		/* if the start point is outside the image, the radius should be change relatively */
		double realWidth = x == 0.0 ? this->markedPoint.x + width : width * 2;
		double realHeight = y == 0.0 ? this->markedPoint.y + height : height * 2;

		/* initiate the drop value */
		this->dropPoint = Point2f(x, y);

		/* initiate the markedPoint value of the new Image */
		this->markedPoint = Point2f(this->markedPoint.x - x, this->markedPoint.y - y);

		this->img = Mat(this->img, Rect(x, y, realWidth, realHeight));
		return *this;
	}

	void fastDetectCorner(bool debug = false) {
		vector<KeyPoint> keyPoints;

		FAST(this->img, keyPoints, 10, true, FastFeatureDetector::TYPE_7_12);

		if (debug) {
			drawKeypoints(this->img, keyPoints, this->img, Scalar(255, 255, 255), DrawMatchesFlags::DRAW_OVER_OUTIMG);
		}
	}

	/* write the image to the local path */
	void writeImage(const char* path) {
		/* save the image */
		imwrite(path, this->img);
	}

	/* a mothod to show the image */
	void showImage() {
		/* open the image */
		imshow("image", this->img);
		/* wait for the return */
		waitKey();
	}

	/* container */
	/* image */
	Mat getImage() {
		return this->img;
	}

	/* properties */
	/* width */
	int getWidth() {
		return this->img.cols;
	}

	/* height */
	int getHeight() {
		return this->img.rows;
	}

	Point2f getDropPoint() {
		return this->dropPoint;
	}

	/* size */
	Size getSize() {
		return this->img.size();
	}

	/* pointer to the matrix */
	uchar* getPtr(int n) {
		return this->img.ptr<uchar>(n);
	}

	/* set markedPoints */
	Image setMarked(float x, float y) {
		this->markedPoint = Point2f(x, y);

		return *this;
	}
};
<|endoftext|>