text:
/*  Copyright 2010 Anders Wallin (anders.e.e.wallin "at" gmail.com)
 *
 *  This file is part of OpenCAMlib.
 *
 *  OpenCAMlib is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  OpenCAMlib 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with OpenCAMlib.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <stdio.h>
#include <sstream>
#include <math.h>
#include <boost/progress.hpp>

#include "cutter.h"
#include "point.h"
#include "triangle.h"
#include "kdtree.h"
#include "pfinish.h"

//********   ********************** */

ParallelFinish::ParallelFinish() {
    clpoints = new std::list<Point>();
    ccpoints = new std::list<CCPoint>();
}

// drop cutter against all triangles in surface
void ParallelFinish::dropCutterSTL1(MillingCutter &cutter)
{
    // very simple drop-cutter
    std::cout << "ParallelFinish::dropCutterSTL1 " << clpoints->size() << " cl-points and " << surf->tris.size() << " triangles...";
    std::cout.flush();
    dcCalls = 0;
    BOOST_FOREACH(Point &cl, *clpoints) {
        // test against all triangles in s
        CCPoint cc;
        BOOST_FOREACH( const Triangle& t, surf->tris) {
            cutter.dropCutter(cl,cc,t);
            ++dcCalls;
        }
        ccpoints->push_back(cc);
    }
    std::cout << "done.\n";
    std::cout.flush();
    return;
}

// first search for triangles under the cutter
// then only drop cutter against found triangles
void ParallelFinish::dropCutterSTL2(MillingCutter &cutter)
{
    std::cout << "ParallelFinish::dropCutterSTL2 " << clpoints->size() << " cl-points and " << surf->tris.size() << " triangles.\n";
    std::cout.flush();
    dcCalls = 0;
    BOOST_FOREACH(Point &cl, *clpoints) { //loop through each CL-point

        // find triangles under cutter
        std::list<Triangle> *triangles_under_cutter = new std::list<Triangle>();
        KDNode::search_kdtree( triangles_under_cutter, cl, cutter, root);

        //std::cout << "found " << triangles_under_cutter->size() << " triangles at cl=" << cl << "\n";
        //char c;
        //std::cin >> c;

        CCPoint cc;
        BOOST_FOREACH( const Triangle& t, *triangles_under_cutter) {
            cutter.dropCutter(cl,cc,t);
            ++dcCalls;
        }
        triangles_under_cutter = NULL;
        ccpoints->push_back(cc);
    }
    std::cout << "done.\n";
    std::cout.flush();
    return;
}

void ParallelFinish::initSTLSurf(STLSurf &s, int bucketSize)
{
    surf = &s;
    std::cout << "Building kd-tree...";
    std::cout.flush();
    root = KDNode::build_kdtree( &(surf->tris), bucketSize );
    std::cout << " done.\n";
    //KDTree::str(root);
}

void ParallelFinish::initCLpoints(double minx,double dx, double maxx,
                       double miny, double dy,double maxy, double base_z)
{
    // initialize grid of cl-points
    std::cout << "initCLPoints in region (" << minx << ", " << miny << ") to (" << maxx << ", " << maxy << ") \n";
    int Nmax = int( ((maxx-minx)/dx) + 1 ); // number of x-rows
    int Mmax = int( ((maxy-miny)/dy) + 1 ); // number of y-columns
    double xtemp, ytemp;

    for (int n=0; n<Nmax ; ++n) {
        for (int m=0; m<Mmax ; ++m) {
            xtemp = minx + n*dx;
            ytemp = miny + m*dy;
            clpoints->push_back( Point(xtemp, ytemp, base_z) );
        }
    }
    std::cout << clpoints->size() << " cl-points initialized \n";
}

boost::python::list ParallelFinish::getTrianglesUnderCutter(Point &cl, MillingCutter &cutter)
{
    boost::python::list trilist;
    std::list<Triangle> *triangles_under_cutter = new std::list<Triangle>();
    KDNode::search_kdtree( triangles_under_cutter, cl, cutter, root);
    BOOST_FOREACH(Triangle t, *triangles_under_cutter)
    {
        trilist.append(t);
    }
    delete triangles_under_cutter;
    return trilist;
}

boost::python::list ParallelFinish::getCLPoints()
{
    boost::python::list plist;
    BOOST_FOREACH(Point p, *clpoints) {
        plist.append(p);
    }
    return plist;
}

boost::python::list ParallelFinish::getCCPoints()
{
    boost::python::list plist;
    BOOST_FOREACH(CCPoint p, *ccpoints) {
        plist.append(p);
    }
    return plist;
}

max_stars_repo_name: JohnyEngine/CNC

max_issues_repo_name: JohnyEngine/CNC

max_forks_repo_name: JohnyEngine/CNC


text:
// Copyright 2011 by BBN Technologies Corp.
// All Rights Reserved.

#include "Generic/common/leak_detection.h" // This must be the first #include

#include <iostream>
#include <boost/make_shared.hpp>
#include "SpanishModule.h"
#include "Generic/common/FeatureModule.h"
#include "Generic/common/version.h"
#include "Spanish/common/es_WordConstants.h"
#include "Spanish/parse/es_LanguageSpecificFunctions.h"
#include "Spanish/parse/ParserTrainer/es_ParserTrainerLanguageSpecificFunctions.h"
#include "Spanish/parse/es_WordFeatures.h"
#include "Spanish/trainers/es_HeadFinder.h"
#include "Spanish/parse/es_STags.h"
#include "Spanish/parse/es_NodeInfo.h"
#include "Spanish/parse/es_SignificantConstitOracle.h"
#include "Spanish/descriptors/es_DescriptorClassifier.h"
#include "Spanish/descriptors/DescriptorClassifierTrainer/es_DescriptorClassifierTrainer.h"
#include "Spanish/descriptors/discmodel/es_P1DescFeatureTypes.h"
#include "Spanish/descriptors/es_CompoundMentionFinder.h"
#include "Spanish/descriptors/es_PronounClassifier.h"
#include "Generic/edt/ReferenceResolver.h"

#include "Spanish/edt/es_DescLinkFeatureFunctions.h"
#include "Spanish/edt/es_DescLinkFunctions.h"
#include "Spanish/edt/es_Guesser.h"
#include "Spanish/edt/es_NameLinkFunctions.h"
#include "Spanish/edt/es_PronounLinker.h"
#include "Spanish/edt/es_PronounLinkerUtils.h"
#include "Spanish/edt/es_RuleDescLinker.h"
#include "Spanish/edt/es_RuleNameLinker.h"
#include "Spanish/edt/discmodel/es_DTCorefFeatureTypes.h"
#include "Spanish/edt/MentionGroups/es_MentionGroupConfiguration.h"

#include "Spanish/propositions/es_LinearPropositionFinder.h"
#include "Spanish/propositions/es_PropositionStatusClassifier.h"
#include "Spanish/propositions/es_SemTreeBuilder.h"

#include "Spanish/relations/es_RelationModel.h"
#include "Spanish/relations/es_RelationFinder.h"
#include "Spanish/relations/es_RelationUtilities.h"
#include "Spanish/relations/es_FamilyRelationFinder.h"
#include "Spanish/relations/discmodel/es_P1RelationFeatureTypes.h"

//#include "Spanish/edt/es_DescLinker.h"
//#include "Spanish/edt/es_PreLinker.h"

// Plugin setup function
extern "C" DLL_PUBLIC void* setup_Spanish() {

	LanguageSpecificFunctions::setImplementation<SpanishLanguageSpecificFunctions>();
	WordConstants::setImplementation<SpanishWordConstants>();

	// ======== Parser ============
	STags::setImplementation<SpanishSTags>();
	WordFeatures::setFactory(boost::shared_ptr<WordFeatures::Factory>
                                       (new SpanishWordFeaturesFactory()));
	HeadFinder::setFactory(boost::shared_ptr<HeadFinder::Factory>
                                       (new SpanishHeadFinderFactory()));
	ParserTrainerLanguageSpecificFunctions::setImplementation
		<SpanishParserTrainerLanguageSpecificFunctions>();
	NodeInfo::setImplementation<SpanishNodeInfo>();

	// ======== Descriptors (aka Mentions) ============
	CompoundMentionFinder::setImplementation<SpanishCompoundMentionFinder>();
	PronounClassifier::setFactory(boost::shared_ptr<PronounClassifier::Factory>
                                       (new SpanishPronounClassifierFactory()));
	SignificantConstitOracle::setFactory(boost::shared_ptr<SignificantConstitOracle::Factory>
                                       (new SpanishSignificantConstitOracleFactory()));

	DescriptorClassifier::setFactory(boost::shared_ptr<DescriptorClassifier::Factory>
                                       (new SpanishDescriptorClassifierFactory()));
	DescriptorClassifierTrainer::setFactory(boost::shared_ptr<DescriptorClassifierTrainer::Factory>
                                       (new SpanishDescriptorClassifierTrainerFactory()));
	P1DescFeatureTypes::setFactory(boost::shared_ptr<P1DescFeatureTypes::Factory>
                                       (new SpanishP1DescFeatureTypesFactory()));

	// ======== EDT (aka coref) ============
	DescLinkFeatureFunctions::setImplementation<SpanishDescLinkFeatureFunctions>();
	DescLinkFunctions::setFactory(boost::shared_ptr<DescLinkFunctions::Factory>
                                       (new SpanishDescLinkFunctionsFactory()));
	Guesser::setImplementation<SpanishGuesser>();
	NameLinkFunctions::setImplementation<SpanishNameLinkFunctions>();
	PronounLinker::setFactory(boost::shared_ptr<PronounLinker::Factory>
                                       (new SpanishPronounLinkerFactory()));
	PronounLinkerUtils::setImplementation<SpanishPronounLinkerUtils>();
	RuleDescLinker::setFactory(boost::shared_ptr<RuleDescLinker::Factory>
                                       (new SpanishRuleDescLinkerFactory()));
	RuleNameLinker::setFactory(boost::shared_ptr<RuleNameLinker::Factory>
                                       (new SpanishRuleNameLinkerFactory()));
	DTCorefFeatureTypes::setFactory(boost::shared_ptr<DTCorefFeatureTypes::Factory>
                                       (new SpanishDTCorefFeatureTypesFactory()));
	MentionGroupConfiguration::setFactory(boost::shared_ptr<MentionGroupConfiguration::Factory>
                                       (new SpanishMentionGroupConfigurationFactory()));

	// ==============Props ==================
	LinearPropositionFinder::setFactory(boost::shared_ptr<LinearPropositionFinder::Factory>
                                       (new SpanishLinearPropositionFinderFactory()));
	PropositionStatusClassifier::setFactory(boost::shared_ptr<PropositionStatusClassifier::Factory>
                                       (new SpanishPropositionStatusClassifierFactory()));
	SemTreeBuilder::setFactory(boost::shared_ptr<SemTreeBuilder::Factory>
                                       (new SpanishSemTreeBuilderFactory()));

	SerifVersion::setSerifLanguage(Language::SPANISH);

	// ==============Relations ==================
	RelationFinder::setFactory(boost::shared_ptr<RelationFinder::Factory>
		(new SpanishRelationFinderFactory()));
	RelationModel::setFactory(boost::shared_ptr<RelationModel::Factory>
		(new SpanishRelationModelFactory()));
	RelationUtilities::setFactory(boost::shared_ptr<RelationUtilities::Factory>
		(new SpanishRelationUtilitiesFactory()));
	FamilyRelationFinder::setFactory(boost::shared_ptr<FamilyRelationFinder::Factory>
		(new SpanishFamilyRelationFinderFactory()));

	// relations/discmodel
	P1RelationFeatureTypes::setFactory(boost::shared_ptr<P1RelationFeatureTypes::Factory>
		(new SpanishP1RelationFeatureTypesFactory()));

	return FeatureModule::setup_return_value();
}

ext: cpp

max_stars_repo_name: BBN-E/serif

max_issues_repo_name: BBN-E/serif

max_forks_repo_name: BBN-E/serif


text:
//Copyright (c) 2008-2016 Emil Dotchevski and Reverge Studios, Inc.

//Distributed under the Boost Software License, Version 1.0. (See accompanying

//file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <boost/qvm/operations.hpp>

#include <boost/qvm/vec.hpp>

#include "test_qvm_quaternion.hpp"

#include "test_qvm_matrix.hpp"

#include "test_qvm_vector.hpp"

#include "gold.hpp"

namespace

    {

    template <class T,class U> struct same_type_tester;

    template <class T> struct same_type_tester<T,T> { };

    template <class T,class U> void test_same_type( T, U ) { same_type_tester<T,U>(); }

    void

    test()

        {

        using namespace boost::qvm;

        for( float a=0; a<6.28f; a+=0.2f )

            {

            test_qvm::quaternion<Q1> const qx=rotx_quat(a);

            test_qvm::quaternion<Q1> const qy=roty_quat(a);

            test_qvm::quaternion<Q1> const qz=rotz_quat(a);

            test_qvm::matrix<M1,3,3> const mx=rotx_mat<3>(a);

            test_qvm::matrix<M1,3,3> const my=roty_mat<3>(a);

            test_qvm::matrix<M1,3,3> const mz=rotz_mat<3>(a);

            test_qvm::vector<V1,3> const v(42,1);

            test_same_type(vec<float,3>(),qx*v);

            test_qvm::vector<V1,3> const q_vx=qx*v;

            test_qvm::vector<V1,3> const m_vx=mx*v;

            test_qvm::vector<V1,3> const q_vy=qy*v;

            test_qvm::vector<V1,3> const m_vy=my*v;

            test_qvm::vector<V1,3> const q_vz=qz*v;

            test_qvm::vector<V1,3> const m_vz=mz*v;

            BOOST_QVM_TEST_CLOSE(q_vx.a,m_vx.a,0.001f);

            BOOST_QVM_TEST_CLOSE(q_vy.a,m_vy.a,0.001f);

            BOOST_QVM_TEST_CLOSE(q_vz.a,m_vz.a,0.001f);

            }

        }

    }

int

main()

    {

    test();

    return boost::report_errors();

    }

max_stars_repo_name: newtondev/drachtio-server

max_issues_repo_name: newtondev/drachtio-server

max_forks_repo_name: newtondev/drachtio-server


text:
#include "main.hpp"
#include "CNetwork.hpp"
#include "CServer.hpp"
#include "CUtils.hpp"
#include "CCallback.hpp"

#include <istream>
#include <boost/date_time/posix_time/posix_time.hpp>

#include "format.h"

void CNetwork::NetAlive(const boost::system::error_code &error_code, bool from_write)
{
	if (from_write == true)
		return;

	if (error_code.value() != 0)
		return;

	if (m_Socket.is_open())
	{
		static string empty_data("\n");
		m_Socket.async_send(asio::buffer(empty_data),
			boost::bind(&CNetwork::NetAlive, this, boost::asio::placeholders::error, true));
	}
	m_AliveTimer.expires_from_now(boost::posix_time::seconds(60));
	m_AliveTimer.async_wait(
		boost::bind(&CNetwork::NetAlive, this, boost::asio::placeholders::error, false));
}

bool CNetwork::Connect(string hostname, unsigned short port, unsigned short query_port)
{
	if (IsConnected())
		return false;

	boost::system::error_code error;
	tcp::resolver resolver(m_IoService);
	tcp::resolver::query query(tcp::v4(), hostname, string());
	tcp::resolver::iterator i = resolver.resolve(query, error);

	if (error)
	{
		CCallbackHandler::Get()->ForwardError(
			EErrorType::CONNECTION_ERROR, error.value(),
			fmt::format("error while resolving hostname \"{}\": {}", hostname, error.message()));
		return false;
	}

	m_SocketDest = *i;
	m_SocketDest.port(query_port);
	m_ServerPort = port;

	AsyncConnect();
	m_IoThread = new thread(boost::bind(&asio::io_service::run, boost::ref(m_IoService)));

	return true;
}

bool CNetwork::Disconnect()
{
	if (IsConnected())
	{
		Execute("quit", [this](ResultSet_t &res)
		{
			m_Connected = false;
		});

		auto start_time = boost::chrono::steady_clock::now();
		while (m_Connected == true &&
			(boost::chrono::steady_clock::now() - start_time) < boost::chrono::seconds(5))
		{
			boost::this_thread::sleep_for(boost::chrono::milliseconds(20));
		}
	}

	m_Connected = false;
	m_Socket.close();
	m_AliveTimer.cancel();
	m_IoService.stop();

	if (m_IoThread != nullptr)
	{
		if (m_IoThread->get_id() != boost::this_thread::get_id())
			m_IoThread->join();
		delete m_IoThread;
		m_IoThread = nullptr;
	}
	return true;
}

void CNetwork::AsyncRead()
{
	asio::async_read_until(m_Socket, m_ReadStreamBuf, '\r',
		boost::bind(&CNetwork::OnRead, this, _1));
}

void CNetwork::AsyncWrite(const string &data)
{
	boost::lock_guard<boost::mutex> lock_guard(m_CmdWriteBufferQueueMutex);

	m_CmdWriteBufferQueue.push(data);
	string &cmd_write_buffer = m_CmdWriteBufferQueue.back();

	if (cmd_write_buffer.back() != '\n')
		cmd_write_buffer.push_back('\n');

	m_Socket.async_send(asio::buffer(cmd_write_buffer),
		boost::bind(&CNetwork::OnWrite, this, _1));
}

void CNetwork::AsyncConnect()
{
	m_Connected = false;
	m_Socket.async_connect(m_SocketDest, boost::bind(&CNetwork::OnConnect, this, _1));
}

void CNetwork::OnConnect(const boost::system::error_code &error_code)
{
	if (error_code.value() == 0)
	{
		m_Connected = true;
		Execute(fmt::format("use port={}", m_ServerPort));
		AsyncRead();

		//start heartbeat check
		NetAlive(boost::system::error_code(), false);
	}
	else
	{
		CCallbackHandler::Get()->ForwardError(
			EErrorType::CONNECTION_ERROR, error_code.value(),
			fmt::format("error while connecting to server: {}", error_code.message()));
	}
}

/*
	- result data is sent as a string which ends with "\n\r"
	- the Teamspeak3 server can send multiple strings
	- the end of a result set is always an error result string
*/
void CNetwork::OnRead(const boost::system::error_code &error_code)
{
	if (error_code.value() == 0)
	{
		static vector<string> captured_data;
		std::istream tmp_stream(&m_ReadStreamBuf);
		string read_data;
		std::getline(tmp_stream, read_data, '\r');

#ifdef _DEBUG
		string dbg_read_data(read_data);
		bool first_line = true;
		do
		{
			logprintf("%s> %s",
				first_line == true ? ">>>" : "   ",
				dbg_read_data.substr(0, 512).c_str());
			dbg_read_data.erase(0, 512);
			first_line = false;
		} while (dbg_read_data.empty() == false);
#endif

		//regex: parse error
		//if this is an error message, it means that no other result data will come
		static const boost::regex error_rx("error id=([0-9]+) msg=([^ \n]+)");
		boost::smatch error_rx_result;
		if (boost::regex_search(read_data, error_rx_result, error_rx))
		{
			if (error_rx_result[1].str() == "0")
			{
				for (auto i = captured_data.begin(); i != captured_data.end(); ++i)
				{
					string &data = *i;
					if (data.find('|') == string::npos)
						continue;

					//we have multiple data rows with '|' as delimiter here,
					//split them up and re-insert every single row
					vector<string> result_set;
					size_t delim_pos = 0;
					do
					{
						size_t old_delim_pos = delim_pos;
						delim_pos = data.find('|', delim_pos);
						string row = data.substr(old_delim_pos, delim_pos - old_delim_pos);
						result_set.push_back(row);
					} while (delim_pos != string::npos && ++delim_pos);

					i = captured_data.erase(i);
					for (auto j = result_set.begin(), jend = result_set.end(); j != jend; ++j)
						i = captured_data.insert(i, *j);
				}

				//call callback and send next command
				m_CmdQueueMutex.lock();
				if (m_CmdQueue.empty() == false)
				{
					ReadCallback_t &callback = m_CmdQueue.front().get<1>();
					if (callback)
					{
						m_CmdQueueMutex.unlock();
						callback(captured_data); //calls the callback
						m_CmdQueueMutex.lock();
					}
					m_CmdQueue.pop();

					if (m_CmdQueue.empty() == false)
						AsyncWrite(m_CmdQueue.front().get<0>());
				}
				m_CmdQueueMutex.unlock();
			}
			else
			{
				string error_str(error_rx_result[2].str());
				unsigned int error_id = 0;

				CUtils::Get()->UnEscapeString(error_str);
				CUtils::Get()->ConvertStringToInt(error_rx_result[1].str(), error_id);

				m_CmdQueueMutex.lock();

				CCallbackHandler::Get()->ForwardError(
					EErrorType::TEAMSPEAK_ERROR, error_id,
					fmt::format("error while executing \"{}\": {}", m_CmdQueue.front().get<0>(), error_str));

				m_CmdQueue.pop();

				if (m_CmdQueue.empty() == false)
					AsyncWrite(m_CmdQueue.front().get<0>());

				m_CmdQueueMutex.unlock();
			}

			captured_data.clear();
		}
		else if (read_data.find("notify") == 0)
		{
			//check if notify is duplicate
			static string last_notify_data;
			static const vector<string> duplicate_notifies{
				"notifyclientmoved",
				"notifycliententerview",
				"notifyclientleftview"
			};
			bool is_duplicate = false;

			for (auto &s : duplicate_notifies)
			{
				if (read_data.find(s) == 0)
				{
					if (last_notify_data == read_data)
						is_duplicate = true;

					break;
				}
			}

			if (is_duplicate == false)
			{
				//notify event
				boost::smatch event_result;
				for (auto &event : m_EventList)
				{
					if (boost::regex_search(read_data, event_result, event.get<0>()))
					{
						event.get<1>()(event_result);
						break;
					}
				}
			}

			last_notify_data = read_data;
		}
		else
		{
			//stack the result data if it is not an error or notification message
			captured_data.push_back(read_data);
		}

		AsyncRead();
	}
	else //error
	{
		CCallbackHandler::Get()->ForwardError(
			EErrorType::CONNECTION_ERROR, error_code.value(),
			fmt::format("error while reading: {}", error_code.message()));

		//"disable" the plugin, since calling Disconnect() or
		//destroying CNetwork here is not very smart
		CServer::CSingleton::Destroy();
		m_Connected = false; //we're not _really_ connected, are we?
	}
}

void CNetwork::OnWrite(const boost::system::error_code &error_code)
{
	boost::lock_guard<boost::mutex> lock_guard(m_CmdWriteBufferQueueMutex);
#ifdef _DEBUG
	logprintf("<<<< %s", m_CmdWriteBufferQueue.front().c_str());
#endif
	m_CmdWriteBufferQueue.pop();
	if (error_code.value() != 0)
	{
		CCallbackHandler::Get()->ForwardError(
			EErrorType::CONNECTION_ERROR, error_code.value(),
			fmt::format("error while writing: {}", error_code.message()));
	}
}

void CNetwork::Execute(string cmd, ReadCallback_t callback)
{
	boost::lock_guard<boost::mutex> queue_lock_guard(m_CmdQueueMutex);
	m_CmdQueue.push(boost::make_tuple(boost::move(cmd), boost::move(callback)));
	if (m_CmdQueue.size() == 1)
		AsyncWrite(m_CmdQueue.front().get<0>());
}


text:
/*
	All rights reserved. Use of this source code is governed by the
	3-clause BSD License in LICENSE.txt.
*/

#include <iostream>
#include <boost/algorithm/string.hpp>
#include <nanogui/common.h>
#include <regular_expressions.h>
#include <list>

#include "editorproject.h"
#include "editorsettings.h"
#include "editorgui.h"
#include "helper.h"
#include "linkableobject.h"
#include "editorwidget.h"
#include "editorbutton.h"
#include "editortextbox.h"
#include "editorlabel.h"
#include "editorimageview.h"
#include "editorlineplot.h"
#include "editorprogressbar.h"
#include "panelscreen.h"
#include "editor.h"
#include "helper.h"
#include "structureswindow.h"
#include "propertywindow.h"
#include "patternswindow.h"
#include "screenswindow.h"
#include "objectwindow.h"
#include "curl_helper.h"
#include "resourcemanager.h"
#include "propertyformhelper.h"
#include "toolbar.h"
#include "DebugExtra.h"
#include "viewswindow.h"
#include "selectablebutton.h"
#include "startupwindow.h"
#include "userwindowwin.h"
#include "linkmanager.h"
#include "thememanager.h"

extern std::map<std::string, Structure *>structures;
extern std::list<Structure *>st_structures;
extern long collect_history;

extern const int DEBUG_ALL;
extern int debug;
#define DEBUG_BASIC ( 1 & debug)
extern int run_only;
extern std::list<Structure *>hm_structures;

void setupTheme(nanogui::Theme *theme);

Structure *EditorGUI::system_settings = 0;

ResourceManager::Factory resource_manager_factory;

class Texture {
public:
	Texture(GLTexture tex, GLTexture::handleType dat) : texture( std::move(tex)), data(std::move(dat)) {}
	GLTexture texture;
	GLTexture::handleType data;
};
std::map<std::string, Texture*> texture_cache;
std::map<GLuint, std::string> loaded_textures;

class EditorKeyboard {
public:
	static EditorKeyboard *instance() {
		if (instance_ == nullptr) instance_ = new EditorKeyboard();
		return instance_;
	}
	const std::string& keyName(int key) {
		assert(key_names.size() > 1);
		auto found = key_names.find(key);
		if (found != key_names.end())
			return (*found).second;
		return unknown_key;
	}
private:
	EditorKeyboard();
	static EditorKeyboard* instance_;
	std::map<int, std::string>key_names;
	std::string unknown_key;
};
EditorKeyboard *EditorKeyboard::instance_ = nullptr;

EditorGUI::EditorGUI(int width, int height, bool full_screen)
: ClockworkClient(nanogui::Vector2i(width, height), "Humid", !full_screen, full_screen), theme(0),
	state(GUIWELCOME),
	editor(0), w_toolbar(0), w_properties(0), w_theme(0), w_user(0), w_patterns(0),
	w_structures(0), w_connections(0), w_startup(0), w_screens(0), w_views(0), needs_update(false),
	sample_buffer_size(5000), project(0)
{
	old_size = mSize;
}

Structure *EditorGUI::getSettings() {
    Structure *es = EditorSettings::find("EditorSettings");
    return (es) ? es : EditorSettings::create();
}

void EditorGUI::addLinkableProperty(const std::string name, LinkableProperty*lp) {
	std::lock_guard<std::recursive_mutex>  lock(linkables_mutex);
	linkables[name] = lp;
}

nanogui::Window *EditorGUI::getNamedWindow(const std::string name) {
	if (name == "Structures") return (getStructuresWindow()) ? getStructuresWindow()->getWindow() : nullptr;
	if (name == "Properties") return (getPropertyWindow()) ? getPropertyWindow()->getWindow() : nullptr;
	if (name == "Objects") return (getObjectWindow()) ? getObjectWindow()->getWindow() : nullptr;
	if (name == "Patterns") return (getPatternsWindow()) ? getPatternsWindow()->getWindow() : nullptr;
	if (name == "Screens") return (getScreensWindow()) ? getScreensWindow()->getWindow() : nullptr;
	return nullptr;
}

bool EditorGUI::keyboardEvent(int key, int scancode , int action, int modifiers) {
	if (EDITOR->isEditMode()) {
		return nanogui::Screen::keyboardEvent(key, scancode, action, modifiers);
	}
	else {
		if (key == GLFW_KEY_ESCAPE) {
			w_user->getWindow()->requestFocus();
			return true;
		}
		if (action == GLFW_PRESS ) {
			bool function_key = false;
			std::string key_name("");
			if ((key >= GLFW_KEY_F1 && key <= GLFW_KEY_F25)) {
				char buf[10];
				snprintf(buf, 10, "KEY_F%d", key - GLFW_KEY_F1+1);
				key_name = buf;
				function_key = true;
			}
			if (key == GLFW_KEY_KP_ENTER
				  && nanogui::Screen::keyboardEvent(GLFW_KEY_ENTER, scancode, action, modifiers)) return true;
			else if (!function_key && nanogui::Screen::keyboardEvent(key, scancode, action, modifiers)) return true;

			key_name = EditorKeyboard::instance()->keyName(key);
			if (key_name.empty()) {
				return nanogui::Screen::keyboardEvent(key, scancode, action, modifiers);
			}

			if (w_user && w_user->structure()) {
				std::cout << "detected key: " << key_name << "\n";
				std::string conn;
				StructureClass *sc = w_user->structure()->getStructureDefinition();
				if (sc) {
					std::map<std::string, Value>::iterator found = sc->getOptions().find(key_name);
					if (found != sc->getOptions().end())
						conn = (*found).second.asString();
				}
				if (conn.empty() && EditorGUI::systemSettings()) {
					sc = EditorGUI::systemSettings()->getStructureDefinition();
					if (!sc) {
						std::cout << "ERROR: no class defined for system settings\n";
						return false;
					}
					std::map<std::string, Value>::iterator found = sc->getOptions().find(key_name);
					if (found != sc->getOptions().end())
						conn = (*found).second.asString();
				}
				if (conn.length()) {
					std::cout << "found key mapping: " << conn << "\n";
					size_t dpos = conn.find(':');
					if (dpos != std::string::npos) {
						std::string remote = conn.substr(dpos+1);
						conn = conn.erase(dpos);

						LinkableProperty *lp = findLinkableProperty(remote);
						if (lp) {
							std::string msgon = getIODSyncCommand(conn, 0, lp->address(), 1);
							queueMessage(conn, msgon, [](std::string s){std::cout << ": " << s << "\n"; });
							std::string msgoff = getIODSyncCommand(conn, 0, lp->address(), 0);
							queueMessage(conn, msgoff, [](std::string s){std::cout << ": " << s << "\n"; });
							return true;
						}
					}
					else {
						//search the current screen for a button with this name and click it
						for (auto w : w_user->getWindow()->children()) {
							//EditorWidget *ew = dynamic_cast<EditorWidget*>(w);
							EditorTextBox *et = dynamic_cast<EditorTextBox*>(w);
							EditorButton *eb = dynamic_cast<EditorButton*>(w);
							if (et && et->getName() == conn) {
								w->requestFocus();
								et->selectAll();
								return true;
							}
							else if (eb && eb->getName() == conn) {
								if (eb->callback()) eb->callback()();
								if (eb->changeCallback()) {
									if (eb->flags() & nanogui::Button::ToggleButton)
										eb->changeCallback()(!eb->pushed());
									else
										eb->changeCallback()(eb->pushed());
									return true;
								}
							}
						}
					}
				}
			}
		}
	}
	return nanogui::Screen::keyboardEvent(key, scancode, action, modifiers);
}

bool isURL(const std::string name) {
	//if (matches(name.c_str(), "((http[s]?|ftp):/)?/?([^:/\\s]+)((/\\w+)*/)([\\w-\\.]+[^#?\\s]+)(.*)?(#[\\w\\-]+)?")) return true;
	return matches(name.c_str(), "^http://.*");
}

void EditorGUI::freeImage(GLuint image_id) {
	auto found = loaded_textures.find(image_id);
	if (found != loaded_textures.end()) {
		std::string tex_name = (*found).second;
		auto found_tex = texture_cache.find(tex_name);
		if (found_tex != texture_cache.end()) {
			Texture *tex = (*found_tex).second;
			delete tex;
			//std::cout << "remaining textures: " << texture_cache.size() << " (" << loaded_textures.size() << ")\n";
		}
	}
}

GLuint EditorGUI::getImageId(const char *source, bool reload) {
	GLuint blank_id = 0;
	auto project_settings = findStructure("ProjectSettings");
	std::string asset_path = project_settings->getStringProperty("asset_path", ".");
	std::string blank_name = "images/blank";
	std::string name(source);
	/*
	for(auto it = mImagesData.begin(); it != mImagesData.end(); ++it) {
		const GLTexture &tex = (*it).first;
		if (tex.textureName() == name) {
			cout << "found image " << tex.textureName() << "\n";
			return tex.texture();
		}
		else if (blank_name == tex.textureName())
			blank_id = tex.texture();
	}
	*/
	if (isURL(name)) {
		std::string cache_name = "cache";
		if (!boost::filesystem::exists(cache_name))
			boost::filesystem::create_directory(cache_name);
		cache_name += "/" + shortName(name) + "." + extn(name);
		// example: http://www.valeparksoftwaredevelopment.com/cmsimages/logo-full-1.png
		if ( (reload || !boost::filesystem::exists(cache_name)) && !get_file(name, cache_name) ) {
			std::cerr << "Error fetching image file\n";
			return blank_id;
		}
		name = cache_name;
	}
	std::string tex_name = shortName(name);
	auto found = texture_cache.find(tex_name);
	if (!reload && found != texture_cache.end())
		return (*found).second->texture.texture();
	else if (reload || found == texture_cache.end()) {
		if (found != texture_cache.end()) {
			Texture *old = (*found).second;
			if (old->texture.texture()) {
				int texture_id = old->texture.texture();
				assert(texture_id);
				int refs = ResourceManager::release(texture_id);
				if (refs == 0 ) {
					texture_cache.erase(found);
				}
				else {
					// there are image views that still reference this image but we have been asked to
					// reload the cached value. We hand this over to a texture resource manager
					// and let it remove the object once the last release occurs
					TextureResourceManagerFactory factory;
					ResourceManager::handover(texture_id, factory);
					old->texture.detach(); // do not call glDeleteTextures() when old is deleted
				}
				delete old;
			}
		}

		// not already loaded, attempt to load from file
		GLTexture tex(tex_name);
		try {
			auto tex_data = tex.load(name);
			GLuint res = tex.texture();
			if (res) {
				texture_cache[tex_name] = new Texture( std::move(tex), std::move(tex_data));
				loaded_textures[res] = tex_name;
				ResourceManager::manage(res);
			}
			return res;
		}
		catch(std::invalid_argument &err) {
			std::cerr << err.what() << "\n";
		}
	}
	return blank_id;
}

void cleanupTextureCache() {
	uint64_t now = microsecs();
	if (texture_cache.size() < 2) return;
	std::map<uint64_t, Texture*> to_remove;
	auto iter = texture_cache.begin();
	while (iter != texture_cache.end()) {
		const std::pair<std::string, Texture*> &item = *iter;
		Texture *texture = item.second;
		GLuint tex = item.second->texture.texture();
		ResourceManager *manager = ResourceManager::find(tex);
		if (manager && manager->uses() == 1 && manager->lastReleaseTime() && now - manager->lastReleaseTime() > 10000000) {
			//ResourceManager::release(tex);
			//delete texture;
			//iter = texture_cache.erase(iter);
			to_remove.insert(std::make_pair(manager->lastReleaseTime(), texture));
			++iter;
		}
		else ++iter;
	}
	auto remove = to_remove.begin();
	int n = texture_cache.size();
	while (remove != to_remove.end()) {
		const std::pair<uint64_t, Texture*> &item = *remove;
		if (--n > 8 || (now - (*remove).first) > 60000000) { //minimum cache size and images older than 10m are flushed
			Texture *texture = (*remove).second;
			GLuint tex = (*remove).second->texture.texture();
			texture_cache.erase(texture_cache.find(texture->texture.textureName()));
			ResourceManager::release(tex);
			delete texture;
			remove = to_remove.erase(remove);
		}
		else ++remove;
		//std::cout << "texture cache flushed. remaining: " << texture_cache.size() << "\n";
	}
}
/*
void cleanupTextureCache(GLuint tex) {
	auto iter = texture_cache.begin();
	while (iter != texture_cache.end()) {
		const std::pair<std::string, Texture*> &item = *iter;
		if (item.second->texture.texture() == tex) {
			Texture *texture = item.second;
			delete texture;
			iter = texture_cache.erase(iter);
			return;
		}
		else ++iter;
	}
}

void cleanupTextureCache() {
	auto iter = deferred_texture_cleanup.begin();
	while (iter != deferred_texture_cleanup.end()) {
		std::pair<GLuint, uint64_t> item = *iter;
		if (item.second - now > 60000) {
			GLuint id = item.first;
			if (ResourceManager::release(id) == 0)
				cleanupTextureCache(id);

			iter = deferred_texture_cleanup.erase(iter);
		}
		else ++iter;
	}
}
*/

EditorKeyboard::EditorKeyboard() {
	key_names.insert(std::make_pair(GLFW_KEY_SPACE, "KEY_SPACE"));
	key_names.insert(std::make_pair(GLFW_KEY_APOSTROPHE, "KEY_APOSTROPHE"));
	key_names.insert(std::make_pair(GLFW_KEY_COMMA, "KEY_COMMA"));
	key_names.insert(std::make_pair(GLFW_KEY_MINUS, "KEY_MINUS"));
	key_names.insert(std::make_pair(GLFW_KEY_PERIOD, "KEY_PERIOD"));
	key_names.insert(std::make_pair(GLFW_KEY_SLASH, "KEY_SLASH"));
	key_names.insert(std::make_pair(GLFW_KEY_0, "KEY_0"));
	key_names.insert(std::make_pair(GLFW_KEY_1, "KEY_1"));
	key_names.insert(std::make_pair(GLFW_KEY_2, "KEY_2"));
	key_names.insert(std::make_pair(GLFW_KEY_3, "KEY_3"));
	key_names.insert(std::make_pair(GLFW_KEY_4, "KEY_4"));
	key_names.insert(std::make_pair(GLFW_KEY_5, "KEY_5"));
	key_names.insert(std::make_pair(GLFW_KEY_6, "KEY_6"));
	key_names.insert(std::make_pair(GLFW_KEY_7, "KEY_7"));
	key_names.insert(std::make_pair(GLFW_KEY_8, "KEY_8"));
	key_names.insert(std::make_pair(GLFW_KEY_9, "KEY_9"));
	key_names.insert(std::make_pair(GLFW_KEY_SEMICOLON, "KEY_SEMICOLON"));
	key_names.insert(std::make_pair(GLFW_KEY_EQUAL, "KEY_EQUAL"));
	key_names.insert(std::make_pair(GLFW_KEY_A, "KEY_A"));
	key_names.insert(std::make_pair(GLFW_KEY_B, "KEY_B"));
	key_names.insert(std::make_pair(GLFW_KEY_C, "KEY_C"));
	key_names.insert(std::make_pair(GLFW_KEY_D, "KEY_D"));
	key_names.insert(std::make_pair(GLFW_KEY_E, "KEY_E"));
	key_names.insert(std::make_pair(GLFW_KEY_F, "KEY_F"));
	key_names.insert(std::make_pair(GLFW_KEY_G, "KEY_G"));
	key_names.insert(std::make_pair(GLFW_KEY_H, "KEY_H"));
	key_names.insert(std::make_pair(GLFW_KEY_I, "KEY_I"));
	key_names.insert(std::make_pair(GLFW_KEY_J, "KEY_J"));
	key_names.insert(std::make_pair(GLFW_KEY_K, "KEY_K"));
	key_names.insert(std::make_pair(GLFW_KEY_L, "KEY_L"));
	key_names.insert(std::make_pair(GLFW_KEY_M, "KEY_M"));
	key_names.insert(std::make_pair(GLFW_KEY_N, "KEY_N"));
	key_names.insert(std::make_pair(GLFW_KEY_O, "KEY_O"));
	key_names.insert(std::make_pair(GLFW_KEY_P, "KEY_P"));
	key_names.insert(std::make_pair(GLFW_KEY_Q, "KEY_Q"));
	key_names.insert(std::make_pair(GLFW_KEY_R, "KEY_R"));
	key_names.insert(std::make_pair(GLFW_KEY_S, "KEY_S"));
	key_names.insert(std::make_pair(GLFW_KEY_T, "KEY_T"));
	key_names.insert(std::make_pair(GLFW_KEY_U, "KEY_U"));
	key_names.insert(std::make_pair(GLFW_KEY_V, "KEY_V"));
	key_names.insert(std::make_pair(GLFW_KEY_W, "KEY_W"));
	key_names.insert(std::make_pair(GLFW_KEY_X, "KEY_X"));
	key_names.insert(std::make_pair(GLFW_KEY_Y, "KEY_Y"));
	key_names.insert(std::make_pair(GLFW_KEY_Z, "KEY_Z"));
	key_names.insert(std::make_pair(GLFW_KEY_LEFT_BRACKET, "KEY_LEFT_BRACKET"));
	key_names.insert(std::make_pair(GLFW_KEY_BACKSLASH, "KEY_BACKSLASH"));
	key_names.insert(std::make_pair(GLFW_KEY_RIGHT_BRACKET, "KEY_RIGHT_BRACKET"));
	key_names.insert(std::make_pair(GLFW_KEY_GRAVE_ACCENT, "KEY_GRAVE_ACCENT"));
	key_names.insert(std::make_pair(GLFW_KEY_WORLD_1, "KEY_WORLD_1"));
	key_names.insert(std::make_pair(GLFW_KEY_WORLD_2, "KEY_WORLD_2"));
	key_names.insert(std::make_pair(GLFW_KEY_ESCAPE, "KEY_ESCAPE"));
	key_names.insert(std::make_pair(GLFW_KEY_ENTER, "KEY_ENTER"));
	key_names.insert(std::make_pair(GLFW_KEY_TAB, "KEY_TAB"));
	key_names.insert(std::make_pair(GLFW_KEY_BACKSPACE, "KEY_BACKSPACE"));
	key_names.insert(std::make_pair(GLFW_KEY_INSERT, "KEY_INSERT"));
	key_names.insert(std::make_pair(GLFW_KEY_DELETE, "KEY_DELETE"));
	key_names.insert(std::make_pair(GLFW_KEY_RIGHT, "KEY_RIGHT"));
	key_names.insert(std::make_pair(GLFW_KEY_LEFT, "KEY_LEFT"));
	key_names.insert(std::make_pair(GLFW_KEY_DOWN, "KEY_DOWN"));
	key_names.insert(std::make_pair(GLFW_KEY_UP, "KEY_UP"));
	key_names.insert(std::make_pair(GLFW_KEY_PAGE_UP, "KEY_PAGE_UP"));
	key_names.insert(std::make_pair(GLFW_KEY_PAGE_DOWN, "KEY_PAGE_DOWN"));
	key_names.insert(std::make_pair(GLFW_KEY_HOME, "KEY_HOME"));
	key_names.insert(std::make_pair(GLFW_KEY_END, "KEY_END"));
	key_names.insert(std::make_pair(GLFW_KEY_CAPS_LOCK, "KEY_CAPS_LOCK"));
	key_names.insert(std::make_pair(GLFW_KEY_SCROLL_LOCK, "KEY_SCROLL_LOCK"));
	key_names.insert(std::make_pair(GLFW_KEY_NUM_LOCK, "KEY_NUM_LOCK"));
	key_names.insert(std::make_pair(GLFW_KEY_PRINT_SCREEN, "KEY_PRINT_SCREEN"));
	key_names.insert(std::make_pair(GLFW_KEY_PAUSE, "KEY_PAUSE"));
	key_names.insert(std::make_pair(GLFW_KEY_F1, "KEY_F1"));
	key_names.insert(std::make_pair(GLFW_KEY_F2, "KEY_F2"));
	key_names.insert(std::make_pair(GLFW_KEY_F3, "KEY_F3"));
	key_names.insert(std::make_pair(GLFW_KEY_F4, "KEY_F4"));
	key_names.insert(std::make_pair(GLFW_KEY_F5, "KEY_F5"));
	key_names.insert(std::make_pair(GLFW_KEY_F6, "KEY_F6"));
	key_names.insert(std::make_pair(GLFW_KEY_F7, "KEY_F7"));
	key_names.insert(std::make_pair(GLFW_KEY_F8, "KEY_F8"));
	key_names.insert(std::make_pair(GLFW_KEY_F9, "KEY_F9"));
	key_names.insert(std::make_pair(GLFW_KEY_F10, "KEY_F10"));
	key_names.insert(std::make_pair(GLFW_KEY_F11, "KEY_F11"));
	key_names.insert(std::make_pair(GLFW_KEY_F12, "KEY_F12"));
	key_names.insert(std::make_pair(GLFW_KEY_F13, "KEY_F13"));
	key_names.insert(std::make_pair(GLFW_KEY_F14, "KEY_F14"));
	key_names.insert(std::make_pair(GLFW_KEY_F15, "KEY_F15"));
	key_names.insert(std::make_pair(GLFW_KEY_F16, "KEY_F16"));
	key_names.insert(std::make_pair(GLFW_KEY_F17, "KEY_F17"));
	key_names.insert(std::make_pair(GLFW_KEY_F18, "KEY_F18"));
	key_names.insert(std::make_pair(GLFW_KEY_F19, "KEY_F19"));
	key_names.insert(std::make_pair(GLFW_KEY_F20, "KEY_F20"));
	key_names.insert(std::make_pair(GLFW_KEY_F21, "KEY_F21"));
	key_names.insert(std::make_pair(GLFW_KEY_F22, "KEY_F22"));
	key_names.insert(std::make_pair(GLFW_KEY_F23, "KEY_F23"));
	key_names.insert(std::make_pair(GLFW_KEY_F24, "KEY_F24"));
	key_names.insert(std::make_pair(GLFW_KEY_F25, "KEY_F25"));
	key_names.insert(std::make_pair(GLFW_KEY_KP_0, "KEYPAD_0"));
	key_names.insert(std::make_pair(GLFW_KEY_KP_1, "KEYPAD_1"));
	key_names.insert(std::make_pair(GLFW_KEY_KP_2, "KEYPAD_2"));
	key_names.insert(std::make_pair(GLFW_KEY_KP_3, "KEYPAD_3"));
	key_names.insert(std::make_pair(GLFW_KEY_KP_4, "KEYPAD_4"));
	key_names.insert(std::make_pair(GLFW_KEY_KP_5, "KEYPAD_5"));
	key_names.insert(std::make_pair(GLFW_KEY_KP_6, "KEYPAD_6"));
	key_names.insert(std::make_pair(GLFW_KEY_KP_7, "KEYPAD_7"));
	key_names.insert(std::make_pair(GLFW_KEY_KP_8, "KEYPAD_8"));
	key_names.insert(std::make_pair(GLFW_KEY_KP_9, "KEYPAD_9"));
	key_names.insert(std::make_pair(GLFW_KEY_KP_DECIMAL, "KEYPAD_DECIMAL"));
	key_names.insert(std::make_pair(GLFW_KEY_KP_DIVIDE, "KEYPAD_DIVIDE"));
	key_names.insert(std::make_pair(GLFW_KEY_KP_MULTIPLY, "KEYPAD_MULTIPLY"));
	key_names.insert(std::make_pair(GLFW_KEY_KP_SUBTRACT, "KEYPAD_SUBTRACT"));
	key_names.insert(std::make_pair(GLFW_KEY_KP_ADD, "KEYPAD_ADD"));
	key_names.insert(std::make_pair(GLFW_KEY_KP_ENTER, "KEYPAD_ENTER"));
	key_names.insert(std::make_pair(GLFW_KEY_KP_EQUAL, "KEYPAD_EQUAL"));
	key_names.insert(std::make_pair(GLFW_KEY_LEFT_SHIFT, "KEY_LEFT_SHIFT"));
	key_names.insert(std::make_pair(GLFW_KEY_LEFT_CONTROL, "KEY_LEFT_CONTROL"));
	key_names.insert(std::make_pair(GLFW_KEY_LEFT_ALT, "KEY_LEFT_ALT"));
	key_names.insert(std::make_pair(GLFW_KEY_LEFT_SUPER, "KEY_LEFT_SUPER"));
	key_names.insert(std::make_pair(GLFW_KEY_RIGHT_SHIFT, "KEY_RIGHT_SHIFT"));
	key_names.insert(std::make_pair(GLFW_KEY_RIGHT_CONTROL, "KEY_RIGHT_CONTROL"));
	key_names.insert(std::make_pair(GLFW_KEY_RIGHT_ALT, "KEY_RIGHT_ALT"));
	key_names.insert(std::make_pair(GLFW_KEY_RIGHT_SUPER, "KEY_RIGHT_SUPER"));
	key_names.insert(std::make_pair(GLFW_KEY_MENU, "KEY_MENU"));
}

void EditorGUI::setTheme(nanogui::Theme *new_theme)  {  ClockworkClient::setTheme(new_theme); theme = new_theme; }

void EditorGUI::createWindows() {
	using namespace nanogui;
	editor = new Editor(this);

	UserWindowWin *uww = new UserWindowWin(this, "Untitled");
	user_screens.push_back(uww);
	// The user window uses the main theme
	auto uwTheme = ThemeManager::instance().findTheme("MainTheme");
	if (!uwTheme) {
		uwTheme = ThemeManager::instance().createTheme();
		ThemeManager::instance().addTheme("MainTheme", uwTheme);
	}
	extern int run_only;
	extern int full_screen_mode;
	if (size().y() == uww->size().y() || run_only || full_screen_mode) {
	    uwTheme->mWindowHeaderHeight = 0;
	}
	w_user = new UserWindow(this, uwTheme, uww);
	w_theme = new ThemeWindow(this, theme, uwTheme);
	w_properties = new PropertyWindow(this, theme);
	w_toolbar = new Toolbar(this, theme);
	w_startup = new StartupWindow(this, theme);
	w_objects = new ObjectWindow(this, theme);

	w_structures = StructuresWindow::create(this, theme);

	w_patterns = new PatternsWindow(this, theme);
	w_screens = new ScreensWindow(this, theme);
	w_views = new ViewsWindow(this, theme);
/*
	ConfirmDialog *cd = new ConfirmDialog(this, "Humid V0.23");
	cd->setCallback([cd,this]{
		cd->setVisible(false);
		this->setState(EditorGUI::GUISELECTPROJECT);
	});
	window = cd->getWindow();
	window->setTheme(theme);
	*/

	//EditorSettings::applySettings("MainWindow", this);
	EditorSettings::applySettings("ThemeSettings", w_theme->getWindow());
	EditorSettings::applySettings("Properties", w_properties->getWindow());
	EditorSettings::applySettings("Structures", w_structures->getWindow());
	EditorSettings::applySettings("Patterns", w_patterns->getWindow());
	EditorSettings::applySettings("Objects", w_objects->getWindow());
	EditorSettings::applySettings("ScreensWindow", w_screens->getWindow());

	// delayed adding windows to the view manager window until the visibility settings are loaded.
	w_views->addWindows();

	//EditorSettings::add("MainWindow", this);
	EditorSettings::add("ThemeSettings", w_theme->getWindow());
	EditorSettings::add("Properties", w_properties->getWindow());
	EditorSettings::add("Structures", w_structures->getWindow());
	EditorSettings::add("Patterns", w_patterns->getWindow());
	EditorSettings::add("Objects", w_objects->getWindow());
	EditorSettings::add("ScreensWindow", w_screens->getWindow());

	uww->setMoveListener([](nanogui::Window *value) {
		updateSettingsStructure("MainWindow", value);
	});
	w_objects->getSkeletonWindow()->setMoveListener(
		[](nanogui::Window *value) { updateSettingsStructure("Objects", value); }
	);
	w_patterns->getSkeletonWindow()->setMoveListener(
		[](nanogui::Window *value) { updateSettingsStructure("Patterns", value); }
	);
	w_structures->getSkeletonWindow()->setMoveListener(
		[](nanogui::Window *value) { updateSettingsStructure("Structures", value); }
	);
	w_screens->getSkeletonWindow()->setMoveListener(
		[](nanogui::Window *value) { updateSettingsStructure("ScreensWindow", value); }
	);
	w_screens->update();
	performLayout(mNVGContext);
	setState(EditorGUI::GUISELECTPROJECT);
}

void EditorGUI::setState(EditorGUI::GuiState s) {
	bool editmode = false;
	bool done = false;
	while (!done) {
		switch(s) {
			case GUIWELCOME:
				done = true;
				break;
			case GUISELECTPROJECT:
				if (hm_structures.size()>1) { // files were specified on the commandline
					getStartupWindow()->setVisible(false);
					getScreensWindow()->update();

					Structure *settings = EditorSettings::find("EditorSettings");
					assert(settings);
					const Value &project_base_v(settings->getProperties().find("project_base"));
					if (project_base_v == SymbolTable::Null) {
						s = GUICREATEPROJECT; done = false; break;
					}
					s = GUIWORKING;
					done = false;
				}
				else {
					window = getStartupWindow()->getWindow();
					window->setVisible(true);
					done = true;
				}
				break;
			case GUICREATEPROJECT:
			{
				getStartupWindow()->setVisible(false);
				Structure *settings(getSettings());
				const Value &path(settings->getProperties().find("project_base"));
				if (path != SymbolTable::Null)
					project = new EditorProject(path.asString().c_str());
				if (!project)
					project = new EditorProject("UntitledProject");
				getUserWindow()->setStructure(createScreenStructure());
				getUserWindow()->setVisible(true);
				getToolbar()->setVisible(!run_only);
				done = true;
			}
				break;
			case GUIEDITMODE:
				editmode = true;
				getUserWindow()->startEditMode();
				// fall through
			case GUIWORKING:
				getUserWindow()->endEditMode();
				getUserWindow()->setVisible(true);
				//getScreensWindow()->selectFirst();
				getToolbar()->setVisible(!run_only);
				if (getPropertyWindow()) {
					nanogui::Window *w = getPropertyWindow()->getWindow();
					w->setVisible(editmode && views.get("Properties").visible);
				}
				if (getPatternsWindow()) {
					nanogui::Window *w = getPatternsWindow()->getWindow();
					w->setVisible(editmode && views.get("Patterns").visible);
				}
				if (getStructuresWindow()) {
					nanogui::Window *w = getStructuresWindow()->getWindow();
					w->setVisible(editmode && views.get("Structures").visible);
				}
				if (getObjectWindow()) {
					nanogui::Window *w = getObjectWindow()->getWindow();
					w->setVisible(editmode && views.get("Objects").visible);
				}
				if (getScreensWindow()) {
					nanogui::Window *w = getScreensWindow()->getWindow();
					w->setVisible(editmode && views.get("ScreensWindow").visible);
				}
				Value remote_screen(EditorGUI::systemSettings()->getProperties().find("remote_screen"));
				if (remote_screen != SymbolTable::Null) {
					LinkableProperty *lp = findLinkableProperty(remote_screen.asString());
					if (lp) {
						lp->apply();
						std::cout <<"\nWorking mode: setting screen to " << lp->value() << "\n\n";
					}
				}
				Value remote_dialog(EditorGUI::systemSettings()->getProperties().find("remote_dialog"));
				if (remote_dialog!= SymbolTable::Null) {
					LinkableProperty *lp = findLinkableProperty(remote_dialog.asString());
					if (lp) {
						lp->apply();
						std::cout <<"\nWorking mode: setting dialog to " << lp->value() << "\n\n";
					}
				}

				done = true;
				break;
		}
	}
	state = s;
}

nanogui::Vector2i fixPlacement(nanogui::Widget *w, nanogui::Widget *container, nanogui::Vector2i &pos) {
	if (pos.x() < 0) pos = Vector2i(0, pos.y());
	if (pos.x() + w->width() > container->width()) pos = Vector2i(container->width() - w->width(), pos.y());
	if (pos.y() < w->theme()->mWindowHeaderHeight) pos = Vector2i(pos.x(), w->theme()->mWindowHeaderHeight+1);
	if (pos.y() + w->height() > container->height()) pos = Vector2i(pos.x(), container->height() - w->height());
	return pos;
}

void EditorGUI::createStructures(const nanogui::Vector2i &p, std::set<Selectable *> selections) {
	using namespace nanogui;

	Widget *window = w_user->getWindow();
	DragHandle *drag_handle = editor->getDragHandle();

	drag_handle->incRef();
	removeChild(drag_handle);
	PropertyMonitor *pm = drag_handle->propertyMonitor();
	drag_handle->setPropertyMonitor(0);

	assert(w_user->structure());
	StructureClass *screen_sc = w_user->structure()->getStructureDefinition();
	assert(screen_sc);

	unsigned int offset = 0;
	for(Selectable *sel : selections) {
		SelectableButton *item = dynamic_cast<SelectableButton*>(sel);
		if (!item) continue;
		std::cout << "creating instance of " << item->getClass() << "\n";
		nanogui::Widget *w = item->create(window);
		if (w) {
			EditorWidget *ew = dynamic_cast<EditorWidget*>(w);
			Parameter param(ew->getName());
			ew->updateStructure();
			param.machine = ew->getDefinition();
			screen_sc->addLocal(param);
			ew->getDefinition()->setOwner(w_user->structure());

			//if (ew) ew->setName( NamedObject::nextName(ew) );
			Vector2i pos(p - window->position() - w->size()/2 + nanogui::Vector2i(0, offset));
			w->setPosition(fixPlacement(w, window, pos));
			offset += w->height() + 8;
		}
	}
	window->performLayout(nvgContext());
	for (auto child : window->children()) {
		EditorWidget *ew = dynamic_cast<EditorWidget*>(child);
		if (ew) ew->updateStructure();
	}

	window->addChild(drag_handle);
	drag_handle->setPropertyMonitor(pm);
	drag_handle->decRef();
}

DialogWindow *EditorGUI::getUserDialog() {
	return w_dialog;
}

void EditorGUI::setUserDialog(const std::string &name) {
	if (name == dialog_name) { return; }
	// TODO: check for visibility of the old dialog and deal with it.
	dialog_name = name;
}

void EditorGUI::showDialog(bool show) {
	Editor *editor = EDITOR;
	std::cout << "request to " << (show ? "show" : "hide") << " dialog " << dialog_name << "\n";
	if (show) {
		if (!w_dialog) {
			w_dialog = new DialogWindow(editor->gui(), mTheme);
			w_dialog->setVisible(false);
		}
		auto s = findScreen(dialog_name);
		if (s) {
			w_dialog->setStructure(s);
			w_dialog->setVisible(true);
		}
	}
	else if (w_dialog) {
		w_dialog->setVisible(false);
		w_dialog->dispose();
		w_dialog = nullptr;
	}
}

bool EditorGUI::mouseButtonEvent(const nanogui::Vector2i &p, int button, bool down, int modifiers) {

	using namespace nanogui;

	nanogui::Window *window = w_user->getWindow();
	if (!window || !window->visible()) return Screen::mouseButtonEvent(p, button, down, modifiers);

	Widget *clicked = findWidget(p);

	Widget *ww = dynamic_cast<Widget*>(window);

	bool is_user = EDITOR->gui()->getUserWindow()->getWindow()->focused();

	if (button != GLFW_MOUSE_BUTTON_1 || !is_user || !window->contains(p /*- window->position()*/)) {
		if (!clicked) return Screen::mouseButtonEvent(p, button, down, modifiers);
		Widget *parent = clicked->parent();
		while (parent && parent->parent()) { clicked = parent; parent = clicked->parent(); }
		if (!clicked->focused() && parent != window) clicked->requestFocus();
		return Screen::mouseButtonEvent(p, button, down, modifiers);
	}

	bool is_child = false;
	nanogui::Vector2i pos(p - window->position());
	for (auto elem : window->children()) {
		if (elem->contains(pos)) { is_child = true; break; }
	}

	nanogui::DragHandle *drag_handle = editor->getDragHandle();

	if (drag_handle && EDITOR->isEditMode()) {
		if (clicked == ww && !is_child) {
			if (getStructuresWindow()->hasSelections()) {
				if (down) {
					createStructures(p, getStructuresWindow()->getSelected());
					getStructuresWindow()->clearSelections();
				}
				return false;
			}
			else if (getObjectWindow()->hasSelections()) {
				if (down) {
					createStructures(p, getObjectWindow()->getSelected());
					getObjectWindow()->clearSelections();
				}
				return false;
			}
			else {
				if (down) {
					window->requestFocus();
					getUserWindow()->clearSelections();
					if (getPropertyWindow()) getPropertyWindow()->update();
				}
				return Screen::mouseButtonEvent(p, button, down, modifiers);
			}
		}
		else {
			if (down) {
				if (drag_handle) drag_handle->setVisible(false);
				requestFocus();
				// deselect items on the window
				if (w_user->hasSelections() && (modifiers & GLFW_MOD_CONTROL)) {
					auto selected = w_user->getSelected();
					auto tmp = selected;
					for (auto sel : tmp) {
						sel->deselect();
						nanogui::Widget *w = dynamic_cast<nanogui::Widget*>(sel);
					}
					if (getPropertyWindow())
						getPropertyWindow()->update();
				}
				return Screen::mouseButtonEvent(p, button, down, modifiers);
			}
		}
		return true;
	}
	else {
		//not edit mode
		//window->requestFocus();
		return Screen::mouseButtonEvent(p, button, down, modifiers);
	}
}

bool EditorGUI::resizeEvent(const Vector2i &new_size) {
	if (old_size == new_size) {
		return false;
	}
	nanogui::Window * windows[] = {
		this->getStructuresWindow()->getWindow(),
		this->getPropertyWindow()->getWindow(),
		this->getPatternsWindow()->getWindow(),
		this->getThemeWindow()->getWindow(),
		this->getViewsWindow()->getWindow(),
		this->getObjectWindow()->getWindow(),
		this->getScreensWindow()->getWindow()
	};
	std::list<std::pair<nanogui::Window *, nanogui::Vector2i>> positions;
	for (unsigned int i = 0; i<5; ++i) {
		nanogui::Window *w = windows[i];
		positions.push_back( std::make_pair(w, nanogui::Vector2i(w->position().x(), w->position().y())) );
	}
	float x_scale = (float)new_size.x() / (float)old_size.x();
	float y_scale = (float)new_size.y() / (float)old_size.y();
	bool res = nanogui::Screen::resizeEvent(new_size);
	for (auto it = positions.begin(); it != positions.end(); ++it) {
		std::pair<nanogui::Window *, nanogui::Vector2i> item = *it;
		nanogui::Window *w = item.first;
		nanogui::Vector2i pos = item.second;

		// items closer to the rhs stay the same distance from the rhs after scaling
		// similarly for items close to the lhs
		//pos.x() = (int) ((float)item.second.x() * x_scale);
		//pos.y() = (int) ((float)item.second.y() * y_scale);

		int lhs = pos.x(), rhs = old_size.x() - pos.x() - w->width();
		if(rhs < lhs) pos.x() = new_size.x() - rhs - w->width();

		// similarly for vertical offset
		//int top = pos.y(), bot = old_size.y() - pos.y() - w->height();
		//if(bot < lhs) pos.x() = new_size.x() - rhs - w->width();
		pos.y() = (int) ((float)item.second.y() * y_scale);

		if (pos.x() < 0) pos.x() = 0;
		if (pos.y() < 0) pos.y() = 0;
		if (pos.x() + w->width() > new_size.x()) pos.x() = new_size.x() - w->width();
		if (pos.y() + w->height() > new_size.y()) pos.y() = new_size.y() - w->height();

		item.first->setPosition(pos);
	}
	old_size = mSize;
	if (w_user) {
		//w_user->getWindow()->setFixedSize(new_size);
		//w_user->getWindow()->setPosition(nanogui::Vector2i(0,0));
		performLayout();
	}
	return true;
}

LinkableProperty *EditorGUI::findLinkableProperty(const std::string name) {
	std::lock_guard<std::recursive_mutex>  lock(linkables_mutex);
	std::map<std::string, LinkableProperty*>::iterator found = linkables.find(name);
	if (found == linkables.end()) {
		return LinkManager::instance().links(name);
	}
	return (*found).second;
}

void EditorGUI::handleClockworkMessage(ClockworkClient::Connection *conn, unsigned long now, const std::string &op, std::list<Value> *message) {
	if (op == "UPDATE") {
		if (!this->getUserWindow()) return;

		int pos = 0;
		std::string name;
		long val = 0;
		double dval = 0.0;
		CircularBuffer *buf = 0;
		LinkableProperty *lp = 0;
		for (auto &v: *message) {
			if (pos == 2) {
				name = v.asString();
				lp = findLinkableProperty(name);
				buf = w_user->getValues(name);
				if (collect_history) {
					if (!buf && lp && lp->dataType() != CircularBuffer::STR) {
						buf = w_user->addDataBuffer(name, lp->dataType(), collect_history);
						if (!buf) std::cout << "no buffer for " << name << "\n";
					}
				}
			}
			else if (pos == 4) {
				if (lp) {
					lp->setValue(v);
				}
				if (buf) {
					CircularBuffer::DataType dt = buf->getDataType();
					if (v.asInteger(val)) {
						if (dt == CircularBuffer::INT16) {
							buf->addSample(now, (int16_t)(val & 0xffff));
						}
						else if (dt == CircularBuffer::INT32) {
							buf->addSample(now, (int32_t)(val & 0xffffffff));
						}
						else
							buf->addSample(now, val);
					}
					else if (v.asFloat(dval)) {
						buf->addSample(now, dval);
					}
				}
			}
			++pos;
		}
	}
	else { std::cout << "unhandled: " << op << "\n"; }
}

void EditorGUI::processModbusInitialisation(const std::string group_name, cJSON *obj) {
	int num_params = cJSON_GetArraySize(obj);
	if (num_params)
	{
		for (int i=0; i<num_params; ++i)
		{
			cJSON *item = cJSON_GetArrayItem(obj, i);
			if (item->type == cJSON_Array)
			{
				Value group(MessageEncoding::valueFromJSONObject(cJSON_GetArrayItem(item, 0), 0));
				Value addr(MessageEncoding::valueFromJSONObject(cJSON_GetArrayItem(item, 1), 0));
				Value kind(MessageEncoding::valueFromJSONObject(cJSON_GetArrayItem(item, 2), 0));
				Value name(MessageEncoding::valueFromJSONObject(cJSON_GetArrayItem(item, 3), 0));
				Value len(MessageEncoding::valueFromJSONObject(cJSON_GetArrayItem(item, 4), 0));
				Value value(MessageEncoding::valueFromJSONObject(cJSON_GetArrayItem(item, 5), 0));
				if (DEBUG_BASIC)
					std::cout << name << ": " << group << " " << addr << " " << len << " " << value <<  "\n";
				if (value.kind == Value::t_string) {
					std::string valstr = value.asString();
					//insert((int)group.iValue, (int)addr.iValue-1, valstr.c_str(), valstr.length()+1); // note copying null
				}

				if (name.kind == Value::t_string || name.kind == Value::t_symbol) {

					size_t n = name.sValue.length();
					const char *p = name.sValue.c_str();
					if (n>2 && p[0] == '"' && p[n-1] == '"')
					{
						char buf[n];
						memcpy(buf, p+1, n-2);
						buf[n-2] = 0;
						name = Value(buf, Value::t_string);
					}
				}

				std::string prop_name(name.asString());
				{
					size_t p = prop_name.find(".cmd_");

					if (p != std::string::npos)
						prop_name = prop_name.erase(p+1,4);
				}

				//else
				//	insert((int)group.iValue, (int)addr.iValue-1, (int)value.iValue, len.iValue);
				LinkableProperty *lp = findLinkableProperty(prop_name);
				if (!lp) {
					std::lock_guard<std::recursive_mutex>  lock(linkables_mutex);
					char buf[10];
					snprintf(buf, 10, "'%d%4d", (int)group.iValue, (int)addr.iValue);
					std::string addr_str(buf);

					lp = new LinkableProperty(group_name, group.iValue, prop_name, addr_str, "", len.iValue);
					linkables[prop_name] = lp;
				}
				if (lp) {
					if (group.iValue != lp->address_group())
						std::cout << prop_name << " change of group from " << lp->address_group() << " to " << group << "\n";
					if (addr.iValue != lp->address())
						std::cout << prop_name << " change of address from " << lp->address() << " to " << addr << "\n";
					lp->setAddressStr(group.iValue, addr.iValue);
					lp->setValue(value);

					//w_user->fixLinks(lp);

					if (collect_history) {
						CircularBuffer *buf = getUserWindow()->getValues(prop_name);
						if (buf) {
							long v;
							double fv;
							buf->clear();
							if (value.asInteger(v))
								buf->addSample( buf->getZeroTime(), v);
							else if (value.asFloat(fv))
								buf->addSample( buf->getZeroTime(), fv);
						}
					}

				}
			}
			else
			{
				char *node = cJSON_Print(item);
				std::cerr << "item " << i << " is not of the expected format: " << node << "\n";
				free(node);
			}
		}
	}
	std::cout << "Total linkable properties is now: " << linkables.size() << "\n";
}

void EditorGUI::update(ClockworkClient::Connection *connection) {
	if (connection->getStartupState() != sDONE && connection->getStartupState() != sRELOAD) {
		// if the tag file is loaded, get initial values
		if (/*linkables.size() && */ connection->getStartupState() == sINIT) {
			if (connection) {
				std::cout << "Sending data initialisation request\n";
				connection->setState(sSENT);
				queueMessage( connection->getName(), "MODBUS REFRESH",
					[this, connection](std::string s) {
						if (s != "failed") {
							cJSON *obj = cJSON_Parse(s.c_str());
							if (!obj) {
								connection->setState(sINIT);
								return;
							}
							if (obj->type == cJSON_Array) {
								processModbusInitialisation(connection->getName(), obj);
								w_objects->rebuildWindow();
								if (w_user && getState() == GUIWORKING) {
									w_user->setStructure(w_user->structure());
									const Value remote_screen(EditorGUI::systemSettings()->getProperties().find("remote_screen"));
									if (remote_screen != SymbolTable::Null) {
										LinkableProperty *lp = findLinkableProperty(remote_screen.asString());
										if (lp) {
											lp->link(getUserWindow());
										}
									}
									const Value remote_dialog(EditorGUI::systemSettings()->getProperties().find("remote_dialog"));
									if (remote_dialog != SymbolTable::Null) {
										LinkableProperty *lp = EDITOR->gui()->findLinkableProperty(remote_dialog.asString());
										if (lp) {
											class DialogNameTarget : public LinkTarget {
												EditorGUI *m_gui;
											public:
												DialogNameTarget(EditorGUI *gui) : m_gui(gui) {}
												void update(const Value &value) override {
													m_gui->setUserDialog(value.asString());
												}
											};
											lp->clear();
											lp->link(new LinkableObject(new DialogNameTarget(EDITOR->gui())));
										}
									}

									Value remote_dialog_visible(EditorGUI::systemSettings()->getProperties().find("remote_dialog_visibility"));
									if (remote_dialog_visible != SymbolTable::Null) {
										LinkableProperty *lp = EDITOR->gui()->findLinkableProperty(remote_dialog_visible.asString());
										if (lp) {
											class DialogVisibilityTarget : public LinkTarget {
												EditorGUI *m_gui;
											public:
												DialogVisibilityTarget(EditorGUI *gui) : m_gui(gui) {}
												void update(const Value &value) override {
													long visible;
													if (value.asInteger(visible)) {
														m_gui->showDialog(visible);
													}
													else {
														std::cout << "could not parse " << value << " as an integer to show/hide dialog\n";
													}
												}
											};
											lp->clear();
											lp->link(new LinkableObject(new DialogVisibilityTarget(EDITOR->gui())));
										}
									}

								}

							}
							connection->setState(sRELOAD);
						}
						else
							connection->setState(sINIT);
					}
				);
			}
		}
	}
	if (connection->getStartupState() == sDONE) {
		if (connection->needsRefresh()) {
			connection->setNeedsRefresh(false);
			connection->setState(sINIT);
		}
	}

	if (connection->getStartupState() == sDONE || connection->getStartupState() == sRELOAD) {
		if (w_user && getState() == GUIWORKING) {
			//bool changed = false;
			const Value &active(EditorGUI::systemSettings()->getProperties().find("active_screen"));
			if (active != SymbolTable::Null) {
				if (connection->getStartupState() == sRELOAD || (w_user->structure() && w_user->structure()->getName() != active.asString())) {
					Structure *s = findScreen(active.asString());
					if (connection->getStartupState() == sRELOAD || (s && w_user->structure() != s) ) {
						w_user->getWindow()->requestFocus();
						w_user->clearSelections();
						w_user->setStructure(s);
						std::cout << "Loaded active screen " << active << "\n";
						if (connection->getStartupState() == sRELOAD) connection->setState(sDONE);
					}
					else if (connection->getStartupState() != sRELOAD) std::cout << "Active screen " << active << " cannot be found\n";
				}
			}
		}
	}

	if (w_user)
		w_user->update();
	if (needs_update) {
		w_properties->getWindow()->performLayout(nvgContext());
		needs_update = false;
	}
	EditorSettings::flush();
/*
	auto iter = texture_cache.begin();
	while (iter != texture_cache.end()) {
		const std::pair<std::string, GLTexture *> &item = *iter;
		if (item.second && item.second->texture()) {
			if
		}
	}
*/
}

max_stars_repo_name: latproc/humid

max_issues_repo_name: latproc/humid

max_forks_repo_name: latproc/humid


text:
//  bind_tests_advanced.cpp  -- The Boost Lambda Library ------------------
//
// Copyright (C) 2000-2003 Jaakko Jarvi (jaakko.jarvi@cs.utu.fi)
// Copyright (C) 2000-2003 Gary Powell (powellg@amazon.com)
// Copyright (C) 2010 Steven Watanabe
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// For more information, see www.boost.org

// -----------------------------------------------------------------------

#include <boost/test/minimal.hpp>    // see "Header Implementation Option"

#include "boost/lambda/lambda.hpp"
#include "boost/lambda/bind.hpp"

#include "boost/any.hpp"
#include "boost/type_traits/is_reference.hpp"
#include "boost/mpl/assert.hpp"
#include "boost/mpl/if.hpp"

#include <iostream>

#include <functional>

#include <algorithm>

using namespace boost::lambda;
namespace bl = boost::lambda;

int sum_0() { return 0; }
int sum_1(int a) { return a; }
int sum_2(int a, int b) { return a+b; }

int product_2(int a, int b) { return a*b; }

// unary function that returns a pointer to a binary function
typedef int (*fptr_type)(int, int);
fptr_type sum_or_product(bool x) {
  return x ? sum_2 : product_2;
}

// a nullary functor that returns a pointer to a unary function that
// returns a pointer to a binary function.
struct which_one {
  typedef fptr_type (*result_type)(bool x);
  template <class T> struct sig { typedef result_type type; };

  result_type operator()() const { return sum_or_product; }
};

void test_nested_binds()
{
  int j = 2; int k = 3;

// bind calls can be nested (the target function can be a lambda functor)
// The interpretation is, that the innermost lambda functor returns something
// that is bindable (another lambda functor, function pointer ...)
  bool condition;

  condition = true;
  BOOST_CHECK(bind(bind(&sum_or_product, _1), 1, 2)(condition)==3);
  BOOST_CHECK(bind(bind(&sum_or_product, _1), _2, _3)(condition, j, k)==5);

  condition = false;
  BOOST_CHECK(bind(bind(&sum_or_product, _1), 1, 2)(condition)==2);
  BOOST_CHECK(bind(bind(&sum_or_product, _1), _2, _3)(condition, j, k)==6);

  which_one wo;
  BOOST_CHECK(bind(bind(bind(wo), _1), _2, _3)(condition, j, k)==6);

  return;
}

// unlambda -------------------------------------------------

  // Sometimes it may be necessary to prevent the argument substitution of
  // taking place. For example, we may end up with a nested bind expression
  // inadvertently when using the target function is received as a parameter

template<class F>
int call_with_100(const F& f) {

  //  bind(f, _1)(make_const(100));
  // This would result in;
  // bind(_1 + 1, _1)(make_const(100)) , which would be a compile time error

  return bl::bind(unlambda(f), _1)(make_const(100));

  // for other functors than lambda functors, unlambda has no effect
  // (except for making them const)
}

template<class F>
int call_with_101(const F& f) {

  return bind(unlambda(f), _1)(make_const(101));

}

void test_unlambda() {

  int i = 1;

  BOOST_CHECK(unlambda(_1 + _2)(i, i) == 2);
  BOOST_CHECK(unlambda(++var(i))() == 2);
  BOOST_CHECK(call_with_100(_1 + 1) == 101);

  BOOST_CHECK(call_with_101(_1 + 1) == 102);

  BOOST_CHECK(call_with_100(bl::bind(std_functor(std::bind1st(std::plus<int>(), 1)), _1)) == 101);

  // std_functor insturcts LL that the functor defines a result_type typedef
  // rather than a sig template.
  bl::bind(std_functor(std::plus<int>()), _1, _2)(i, i);
}

// protect ------------------------------------------------------------

// protect protects a lambda functor from argument substitution.
// protect is useful e.g. with nested stl algorithm calls.

namespace ll {

struct for_each {

  // note, std::for_each returns it's last argument
  // We want the same behaviour from our ll::for_each.
  // However, the functor can be called with any arguments, and
  // the return type thus depends on the argument types.

  // 1. Provide a sig class member template:

  // The return type deduction system instantiate this class as:
  // sig<Args>::type, where Args is a boost::tuples::cons-list
  // The head type is the function object type itself
  // cv-qualified (so it is possilbe to provide different return types
  // for differently cv-qualified operator()'s.

  // The tail type is the list of the types of the actual arguments the
  // function was called with.
  // So sig should contain a typedef type, which defines a mapping from
  // the operator() arguments to its return type.
  // Note, that it is possible to provide different sigs for the same functor
  // if the functor has several operator()'s, even if they have different
  // number of arguments.

  // Note, that the argument types in Args are guaranteed to be non-reference
  // types, but they can have cv-qualifiers.

    template <class Args>
  struct sig {
    typedef typename boost::remove_const<
          typename boost::tuples::element<3, Args>::type
       >::type type;
  };

  template <class A, class B, class C>
  C
  operator()(const A& a, const B& b, const C& c) const
  { return std::for_each(a, b, c);}
};

} // end of ll namespace

void test_protect()
{
  int i = 0;
  int b[3][5];
  int* a[3];

  for(int j=0; j<3; ++j) a[j] = b[j];

  std::for_each(a, a+3,
           bind(ll::for_each(), _1, _1 + 5, protect(_1 = ++var(i))));

  // This is how you could output the values (it is uncommented, no output
  // from a regression test file):
  //  std::for_each(a, a+3,
  //                bind(ll::for_each(), _1, _1 + 5,
  //                     std::cout << constant("\nLine ") << (&_1 - a) << " : "
  //                     << protect(_1)
  //                     )
  //               );

  int sum = 0;

  std::for_each(a, a+3,
           bind(ll::for_each(), _1, _1 + 5,
                protect(sum += _1))
               );
  BOOST_CHECK(sum == (1+15)*15/2);

  sum = 0;

  std::for_each(a, a+3,
           bind(ll::for_each(), _1, _1 + 5,
                sum += 1 + protect(_1)) // add element count
               );
  BOOST_CHECK(sum == (1+15)*15/2 + 15);

  (1 + protect(_1))(sum);

  int k = 0;
  ((k += constant(1)) += protect(constant(2)))();
  BOOST_CHECK(k==1);

  k = 0;
  ((k += constant(1)) += protect(constant(2)))()();
  BOOST_CHECK(k==3);

  // note, the following doesn't work:

  //  ((var(k) = constant(1)) = protect(constant(2)))();

  // (var(k) = constant(1))() returns int& and thus the
  // second assignment fails.

  // We should have something like:
  // bind(var, var(k) = constant(1)) = protect(constant(2)))();
  // But currently var is not bindable.

  // The same goes with ret. A bindable ret could be handy sometimes as well
  // (protect(std::cout << _1), std::cout << _1)(i)(j); does not work
  // because the comma operator tries to store the result of the evaluation
  // of std::cout << _1 as a copy (and you can't copy std::ostream).
  // something like this:
  // (protect(std::cout << _1), bind(ref, std::cout << _1))(i)(j);

  // the stuff below works, but we do not want extra output to
  // cout, must be changed to stringstreams but stringstreams do not
  // work due to a bug in the type deduction. Will be fixed...
#if 0
  // But for now, ref is not bindable. There are other ways around this:

    int x = 1, y = 2;
    (protect(std::cout << _1), (std::cout << _1, 0))(x)(y);

  // added one dummy value to make the argument to comma an int
  // instead of ostream&

  // Note, the same problem is more apparent without protect
  //   (std::cout << 1, std::cout << constant(2))(); // does not work

    (boost::ref(std::cout << 1), std::cout << constant(2))(); // this does

#endif

}

void test_lambda_functors_as_arguments_to_lambda_functors() {

// lambda functor is a function object, and can therefore be used
// as an argument to another lambda functors function call object.

  // Note however, that the argument/type substitution is not entered again.
  // This means, that something like this will not work:

    (_1 + _2)(_1, make_const(7));
    (_1 + _2)(bind(&sum_0), make_const(7));

    // or it does work, but the effect is not to call
    // sum_0() + 7, but rather
    // bind(sum_0) + 7, which results in another lambda functor
    // (lambda functor + int) and can be called again
  BOOST_CHECK((_1 + _2)(bind(&sum_0), make_const(7))() == 7);

  int i = 3, j = 12;
  BOOST_CHECK((_1 - _2)(_2, _1)(i, j) == j - i);

  // also, note that lambda functor are no special case for bind if received
  // as a parameter. In oder to be bindable, the functor must
  // defint the sig template, or then
  // the return type must be defined within the bind call. Lambda functors
  // do define the sig template, so if the return type deduction system
  // covers the case, there is no need to specify the return type
  // explicitly.

  int a = 5, b = 6;

  // Let type deduction find out the return type
  BOOST_CHECK(bind(_1, _2, _3)(unlambda(_1 + _2), a, b) == 11);

  //specify it yourself:
  BOOST_CHECK(bind(_1, _2, _3)(ret<int>(_1 + _2), a, b) == 11);
  BOOST_CHECK(ret<int>(bind(_1, _2, _3))(_1 + _2, a, b) == 11);
  BOOST_CHECK(bind<int>(_1, _2, _3)(_1 + _2, a, b) == 11);

  bind(_1,1.0)(_1+_1);
  return;

}

void test_const_parameters() {

  //  (_1 + _2)(1, 2); // this would fail,

  // Either make arguments const:
  BOOST_CHECK((_1 + _2)(make_const(1), make_const(2)) == 3);

  // Or use const_parameters:
  BOOST_CHECK(const_parameters(_1 + _2)(1, 2) == 3);

}

void test_rvalue_arguments()
{
  // Not quite working yet.
  // Problems with visual 7.1
  // BOOST_CHECK((_1 + _2)(1, 2) == 3);
}

void test_break_const()
{

  // break_const is currently unnecessary, as LL supports perfect forwarding
  // for up to there argument lambda functors, and LL does not support
  // lambda functors with more than 3 args.

  // I'll keep the test case around anyway, if more arguments will be supported
  // in the future.

  // break_const breaks constness! Be careful!
  // You need this only if you need to have side effects on some argument(s)
  // and some arguments are non-const rvalues and your lambda functors
  // take more than 3 arguments.

  int i = 1;
  //  OLD COMMENT: (_1 += _2)(i, 2) // fails, 2 is a non-const rvalue
  //  OLD COMMENT:  const_parameters(_1 += _2)(i, 2) // fails, side-effect to i
  break_const(_1 += _2)(i, 2); // ok
  BOOST_CHECK(i == 3);
}

template<class T>
struct func {
  template<class Args>
  struct sig {
    typedef typename boost::tuples::element<1, Args>::type arg1;
    // If the argument type is not the same as the expected type,
    // return void, which will cause an error.  Note that we
    // can't just assert that the types are the same, because
    // both const and non-const versions can be instantiated
    // even though only one is ultimately used.
    typedef typename boost::mpl::if_<boost::is_same<arg1, T>,
      typename boost::remove_const<arg1>::type,
      void
    >::type type;
  };
  template<class U>
  U operator()(const U& arg) const {
    return arg;
  }
};

void test_sig()
{
  int i = 1;
  BOOST_CHECK(bind(func<int>(), 1)() == 1);
  BOOST_CHECK(bind(func<const int>(), _1)(static_cast<const int&>(i)) == 1);
  BOOST_CHECK(bind(func<int>(), _1)(i) == 1);
}

class base {
public:
  virtual int foo() = 0;
};

class derived : public base {
public:
  virtual int foo() {
    return 1;
  }
};

void test_abstract()
{
  derived d;
  base& b = d;
  BOOST_CHECK(bind(&base::foo, var(b))() == 1);
  BOOST_CHECK(bind(&base::foo, *_1)(&b) == 1);
}

int test_main(int, char *[]) {

  test_nested_binds();
  test_unlambda();
  test_protect();
  test_lambda_functors_as_arguments_to_lambda_functors();
  test_const_parameters();
  test_rvalue_arguments();
  test_break_const();
  test_sig();
  test_abstract();
  return 0;
}

max_stars_repo_name: zyiacas/boost-doc-zh

max_issues_repo_name: sdfict/boost-doc-zh

max_forks_repo_name: sdfict/boost-doc-zh


text:
#include "engine/guidance/collapse_turns.hpp"
#include "extractor/guidance/constants.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include "engine/guidance/collapse_scenario_detection.hpp"
#include "engine/guidance/collapsing_utility.hpp"
#include "util/bearing.hpp"
#include "util/guidance/name_announcements.hpp"

#include <cstddef>

#include <boost/assert.hpp>

using osrm::extractor::guidance::TurnInstruction;
using osrm::util::angularDeviation;
using namespace osrm::extractor::guidance;

namespace osrm
{
namespace engine
{
namespace guidance
{

namespace
{
const constexpr double MAX_COLLAPSE_DISTANCE = 30;

// find the combined turn angle for two turns. Not in all scenarios we can easily add both angles
// (e.g 90 degree left followed by 90 degree right would be no turn at all).
double findTotalTurnAngle(const RouteStep &entry_step, const RouteStep &exit_step)
{
    if (entry_step.geometry_begin > exit_step.geometry_begin)
        return findTotalTurnAngle(exit_step, entry_step);

    const auto exit_intersection = exit_step.intersections.front();
    const auto exit_step_exit_bearing = exit_intersection.bearings[exit_intersection.out];
    const auto exit_step_entry_bearing =
        util::bearing::reverse(exit_intersection.bearings[exit_intersection.in]);

    const auto entry_intersection = entry_step.intersections.front();
    const auto entry_step_entry_bearing =
        util::bearing::reverse(entry_intersection.bearings[entry_intersection.in]);
    const auto entry_step_exit_bearing = entry_intersection.bearings[entry_intersection.out];

    const auto exit_angle =
        util::bearing::angleBetween(exit_step_entry_bearing, exit_step_exit_bearing);
    const auto entry_angle =
        util::bearing::angleBetween(entry_step_entry_bearing, entry_step_exit_bearing);

    const double total_angle =
        util::bearing::angleBetween(entry_step_entry_bearing, exit_step_exit_bearing);

    // both angles are in the same direction, the total turn gets increased
    //
    // a ---- b
    //           \
    //              c
    //              |
    //              d
    //
    // Will be considered just like
    //
    // a -----b
    //        |
    //        c
    //        |
    //        d
    const auto use_total_angle = [&]() {
        // only consider actual turns in combination:
        if (angularDeviation(total_angle, 180) < 0.5 * NARROW_TURN_ANGLE)
            return false;

        // entry step is short and the exit and the exit step does not have intersections??
        if (entry_step.distance < MAX_COLLAPSE_DISTANCE)
            return true;

        // both go roughly in the same direction
        if ((entry_angle <= 185 && exit_angle <= 185) || (entry_angle >= 175 && exit_angle >= 175))
            return true;

        return false;
    }();

    // We allow for minor deviations from a straight line
    if (use_total_angle)
    {
        return total_angle;
    }
    else
    {
        // to prevent ignoring angles like
        //
        // a -- b
        //      |
        //      c -- d
        //
        // We don't combine both turn angles here but keep the very first turn angle.
        // We choose the first one, since we consider the first maneuver in a merge range the
        // important one
        return entry_angle;
    }
}

inline void handleSliproad(RouteStepIterator sliproad_step)
{
    // find the next step after the sliproad step itself (this is not necessarily the next step,
    // since we might have to skip over traffic lights/node penalties)
    auto next_step = [&sliproad_step]() {
        auto next_step = findNextTurn(sliproad_step);
        while (isTrafficLightStep(*next_step))
        {
            // in sliproad checks, we should have made sure not to include invalid modes
            BOOST_ASSERT(haveSameMode(*sliproad_step, *next_step));
            sliproad_step->ElongateBy(*next_step);
            next_step->Invalidate();
            next_step = findNextTurn(next_step);
        }
        BOOST_ASSERT(haveSameMode(*sliproad_step, *next_step));
        return next_step;
    }();

    // have we reached the end?
    if (hasWaypointType(*next_step))
    {
        setInstructionType(*sliproad_step, TurnType::Turn);
    }
    else
    {
        const auto previous_step = findPreviousTurn(sliproad_step);
        const auto connecting_same_name_roads = haveSameName(*previous_step, *next_step);
        auto sliproad_turn_type = connecting_same_name_roads ? TurnType::Continue : TurnType::Turn;
        setInstructionType(*sliproad_step, sliproad_turn_type);
        combineRouteSteps(*sliproad_step,
                          *next_step,
                          AdjustToCombinedTurnAngleStrategy(),
                          TransferSignageStrategy(),
                          TransferLanesStrategy());
    }
}

} // namespace

// STRATEGIES

// keep signage/other entries in route step intact
void NoModificationStrategy::operator()(RouteStep &, const RouteStep &) const
{
    // actually do nothing.
}

// transfer turn type from a different turn
void TransferTurnTypeStrategy::operator()(RouteStep &step_at_turn_location,
                                          const RouteStep &transfer_from_step) const
{
    step_at_turn_location.maneuver = transfer_from_step.maneuver;
}

void AdjustToCombinedTurnAngleStrategy::operator()(RouteStep &step_at_turn_location,
                                                   const RouteStep &transfer_from_step) const
{
    // Forks point to left/right. By doing a combined angle, we would risk ending up with
    // unreasonable fork instrucitons. The direction of a fork only depends on the forking location,
    // not further angles coming up
    //
    //          d
    //       .  c
    // a - b
    //
    // could end up as `fork left` for `a-b-c`, instead of fork-right
    if (hasTurnType(step_at_turn_location, TurnType::Fork))
        return;

    // TODO assert transfer_from_step == step_at_turn_location + 1
    const auto angle = findTotalTurnAngle(step_at_turn_location, transfer_from_step);
    step_at_turn_location.maneuver.instruction.direction_modifier = getTurnDirection(angle);
}

AdjustToCombinedTurnStrategy::AdjustToCombinedTurnStrategy(
    const RouteStep &step_prior_to_intersection)
    : step_prior_to_intersection(step_prior_to_intersection)
{
}

void AdjustToCombinedTurnStrategy::operator()(RouteStep &step_at_turn_location,
                                              const RouteStep &transfer_from_step) const
{
    const auto angle = findTotalTurnAngle(step_at_turn_location, transfer_from_step);

    // Forks and merges point to left/right. By doing a combined angle, we would risk ending up with
    // unreasonable fork instrucitons. The direction of a fork or a merge only depends on the
    // location,
    // not further angles coming up
    //
    //          d
    //       .  c
    // a - b
    //
    // could end up as `fork left` for `a-b-c`, instead of fork-right
    const auto new_modifier = hasTurnType(step_at_turn_location, TurnType::Fork) ||
                                      hasTurnType(step_at_turn_location, TurnType::Merge)
                                  ? step_at_turn_location.maneuver.instruction.direction_modifier
                                  : getTurnDirection(angle);

    // a turn that is a new name or straight (turn/continue)
    const auto is_non_turn = [](const RouteStep &step) {
        return hasTurnType(step, TurnType::NewName) ||
               (hasTurnType(step, TurnType::Turn) &&
                hasModifier(step, DirectionModifier::Straight)) ||
               (hasTurnType(step, TurnType::Continue) &&
                hasModifier(step, DirectionModifier::Straight));
    };

    // check if the first part is the actual turn
    const auto transferring_from_non_turn = is_non_turn(transfer_from_step);

    // or if the maneuver location does not perform an actual turn
    const auto maneuver_at_non_turn = is_non_turn(step_at_turn_location) ||
                                      hasTurnType(step_at_turn_location, TurnType::Suppressed);

    // creating turns if the original instrution wouldn't be a maneuver (also for turn straights)`
    if (transferring_from_non_turn || maneuver_at_non_turn)
    {
        if (hasTurnType(step_at_turn_location, TurnType::Suppressed))
        {
            if (new_modifier == DirectionModifier::Straight)
                setInstructionType(step_at_turn_location, TurnType::NewName);
            else
                step_at_turn_location.maneuver.instruction.type =
                    haveSameName(step_prior_to_intersection, transfer_from_step)
                        ? TurnType::Continue
                        : TurnType::Turn;
        }
        else if (hasTurnType(step_at_turn_location, TurnType::NewName) &&
                 hasTurnType(transfer_from_step, TurnType::Suppressed) &&
                 new_modifier != DirectionModifier::Straight)
        {
            setInstructionType(step_at_turn_location, TurnType::Turn);
        }
        else if (hasTurnType(step_at_turn_location, TurnType::Continue) &&
                 !haveSameName(step_prior_to_intersection, transfer_from_step))
        {
            setInstructionType(step_at_turn_location, TurnType::Turn);
        }
        else if (hasTurnType(step_at_turn_location, TurnType::Turn) &&
                 haveSameName(step_prior_to_intersection, transfer_from_step))
        {
            setInstructionType(step_at_turn_location, TurnType::Continue);
        }
    }
    // if we are turning onto a ramp, we carry the ramp (e.g. a turn onto a ramp that is modelled
    // later only)
    else if (hasTurnType(transfer_from_step, TurnType::OnRamp))
    {
        setInstructionType(step_at_turn_location, TurnType::OnRamp);
    }
    // switch two turns to a single continue, if necessary
    else if (hasTurnType(step_at_turn_location, TurnType::Turn) &&
             hasTurnType(transfer_from_step, TurnType::Turn) &&
             haveSameName(step_prior_to_intersection, transfer_from_step))
    {
        setInstructionType(step_at_turn_location, TurnType::Continue);
    }
    // switch continue to turn, if possible
    else if (hasTurnType(step_at_turn_location, TurnType::Continue) &&
             hasTurnType(transfer_from_step, TurnType::Turn) &&
             !haveSameName(step_prior_to_intersection, transfer_from_step))
    {
        setInstructionType(step_at_turn_location, TurnType::Turn);
    }

    // finally set our new modifier
    step_at_turn_location.maneuver.instruction.direction_modifier = new_modifier;
}

StaggeredTurnStrategy::StaggeredTurnStrategy(const RouteStep &step_prior_to_intersection)
    : step_prior_to_intersection(step_prior_to_intersection)
{
}

void StaggeredTurnStrategy::operator()(RouteStep &step_at_turn_location,
                                       const RouteStep &transfer_from_step) const
{
    step_at_turn_location.maneuver.instruction.direction_modifier = DirectionModifier::Straight;
    step_at_turn_location.maneuver.instruction.type =
        haveSameName(step_prior_to_intersection, transfer_from_step) ? TurnType::Suppressed
                                                                     : TurnType::NewName;
}

SetFixedInstructionStrategy::SetFixedInstructionStrategy(
    const extractor::guidance::TurnInstruction instruction)
    : instruction(instruction)
{
}

void SetFixedInstructionStrategy::operator()(RouteStep &step_at_turn_location,
                                             const RouteStep &) const
{
    step_at_turn_location.maneuver.instruction = instruction;
}

void TransferSignageStrategy::operator()(RouteStep &step_at_turn_location,
                                         const RouteStep &transfer_from_step) const
{
    step_at_turn_location.AdaptStepSignage(transfer_from_step);
    step_at_turn_location.rotary_name = transfer_from_step.rotary_name;
    step_at_turn_location.rotary_pronunciation = transfer_from_step.rotary_pronunciation;
}

void TransferLanesStrategy::operator()(RouteStep &step_at_turn_location,
                                       const RouteStep &transfer_from_step) const
{
    step_at_turn_location.intersections.front().lanes =
        transfer_from_step.intersections.front().lanes;
    step_at_turn_location.intersections.front().lane_description =
        transfer_from_step.intersections.front().lane_description;
}

void suppressStep(RouteStep &step_at_turn_location, RouteStep &step_after_turn_location)
{
    return combineRouteSteps(step_at_turn_location,
                             step_after_turn_location,
                             NoModificationStrategy(),
                             NoModificationStrategy(),
                             NoModificationStrategy());
}

// OTHER IMPLEMENTATIONS
OSRM_ATTR_WARN_UNUSED
RouteSteps collapseTurnInstructions(RouteSteps steps)
{
    // make sure we can safely iterate over all steps (has depart/arrive with TurnType::NoTurn)
    BOOST_ASSERT(!hasTurnType(steps.front()) && !hasTurnType(steps.back()));
    BOOST_ASSERT(hasWaypointType(steps.front()) && hasWaypointType(steps.back()));

    if (steps.size() <= 2)
        return steps;

    // start of with no-op
    for (auto current_step = steps.begin() + 1; current_step + 1 != steps.end(); ++current_step)
    {
        if (entersRoundabout(current_step->maneuver.instruction) ||
            staysOnRoundabout(current_step->maneuver.instruction))
        {
            // If postProcess is called before then all corresponding leavesRoundabout steps are
            // removed and the current roundabout step can be ignored by directly proceeding to
            // the next step.
            // If postProcess is not called before then all steps till the next leavesRoundabout
            // step must be skipped to prevent incorrect roundabouts post-processing.

            // are we done for good?
            if (current_step + 1 == steps.end())
                break;
            else
                continue;
        }

        // only operate on actual turns
        if (!hasTurnType(*current_step))
            continue;

        // handle all situations involving the sliproad turn type
        if (hasTurnType(*current_step, TurnType::Sliproad))
        {
            handleSliproad(current_step);
            continue;
        }

        // don't collapse next step if it is a waypoint alread
        const auto next_step = findNextTurn(current_step);
        if (hasWaypointType(*next_step))
            break;

        const auto previous_step = findPreviousTurn(current_step);

        // don't collapse anything that does change modes
        if (current_step->mode != next_step->mode)
            continue;

        // handle staggered intersections:
        // a staggered intersection describes to turns in rapid succession that go in opposite
        // directions (e.g. right + left) with a very short segment in between
        if (isStaggeredIntersection(previous_step, current_step, next_step))
        {
            combineRouteSteps(*current_step,
                              *next_step,
                              StaggeredTurnStrategy(*previous_step),
                              TransferSignageStrategy(),
                              NoModificationStrategy());
        }
        else if (isUTurn(previous_step, current_step, next_step))
        {
            combineRouteSteps(
                *current_step,
                *next_step,
                SetFixedInstructionStrategy({TurnType::Continue, DirectionModifier::UTurn}),
                TransferSignageStrategy(),
                NoModificationStrategy());
        }
        else if (isNameOszillation(previous_step, current_step, next_step))
        {
            // first deactivate the second name switch
            suppressStep(*current_step, *next_step);
            // and then the first (to ensure both iterators to be valid)
            suppressStep(*previous_step, *current_step);
        }
        else if (maneuverPreceededByNameChange(previous_step, current_step, next_step) ||
                 maneuverPreceededBySuppressedDirection(current_step, next_step))
        {
            const auto strategy = AdjustToCombinedTurnStrategy(*previous_step);
            strategy(*next_step, *current_step);
            // suppress previous step
            suppressStep(*previous_step, *current_step);
        }
        else if (maneuverSucceededByNameChange(current_step, next_step) ||
                 nameChangeImmediatelyAfterSuppressed(current_step, next_step) ||
                 maneuverSucceededBySuppressedDirection(current_step, next_step) ||
                 closeChoicelessTurnAfterTurn(current_step, next_step))
        {
            combineRouteSteps(*current_step,
                              *next_step,
                              AdjustToCombinedTurnStrategy(*previous_step),
                              TransferSignageStrategy(),
                              NoModificationStrategy());
        }
        else if (straightTurnFollowedByChoiceless(current_step, next_step))
        {
            combineRouteSteps(*current_step,
                              *next_step,
                              AdjustToCombinedTurnStrategy(*previous_step),
                              TransferSignageStrategy(),
                              NoModificationStrategy());
        }
        else if (suppressedStraightBetweenTurns(previous_step, current_step, next_step))
        {
            const auto far_back_step = findPreviousTurn(previous_step);
            previous_step->ElongateBy(*current_step);
            current_step->Invalidate();
            combineRouteSteps(*previous_step,
                              *next_step,
                              AdjustToCombinedTurnStrategy(*far_back_step),
                              TransferSignageStrategy(),
                              NoModificationStrategy());
        }
        // if the current collapsing triggers, we can check for advanced scenarios that only are
        // possible after an inital collapse step (e.g. name change right after a u-turn)
        //
        // f - e - d
        //     |   |
        // a - b - c
        //
        // In this scenario, bc and de might belong to a different road than a-b and f-e (since
        // there are no fix conventions how to label them in segregated intersections). These steps
        // might only become apparent after some initial collapsing
        const auto new_next_step = findNextTurn(current_step);
        if (doubleChoiceless(current_step, new_next_step))
        {
            combineRouteSteps(*current_step,
                              *new_next_step,
                              AdjustToCombinedTurnStrategy(*previous_step),
                              TransferSignageStrategy(),
                              NoModificationStrategy());
        }
        if (!hasWaypointType(*previous_step))
        {
            const auto far_back_step = findPreviousTurn(previous_step);
            // due to name changes, we can find u-turns a bit late. Thats why we check far back as
            // well
            if (isUTurn(far_back_step, previous_step, current_step))
            {
                combineRouteSteps(
                    *previous_step,
                    *current_step,
                    SetFixedInstructionStrategy({TurnType::Continue, DirectionModifier::UTurn}),
                    TransferSignageStrategy(),
                    NoModificationStrategy());
            }
        }
    }
    return steps;
}

} // namespace guidance
} // namespace engine
} // namespace osrm

max_stars_repo_name: neilbu/osrm-backend

max_issues_repo_name: neilbu/osrm-backend

max_forks_repo_name: neilbu/osrm-backend


text:
#ifndef ASLAM_EUCLIDEAN_EXPRESSION_HPP
#define ASLAM_EUCLIDEAN_EXPRESSION_HPP

#include <Eigen/Core>
#include <boost/shared_ptr.hpp>
#include <aslam/backend/JacobianContainer.hpp>
#include <set>

#include "VectorExpression.hpp"

namespace aslam {
  namespace backend {
    class HomogeneousExpression;
    template <int D> class VectorExpressionNode;
    typedef VectorExpressionNode<3> EuclideanExpressionNode;
    class ScalarExpression;

    class EuclideanExpression : public VectorExpression<3>
    {
    public:
      SM_DEFINE_EXCEPTION(Exception, std::runtime_error);

      typedef Eigen::Vector3d value_t;
      typedef Eigen::Vector3d vector_t;

      EuclideanExpression() = default;
      EuclideanExpression(EuclideanExpressionNode * root) : VectorExpression(root) {}
      EuclideanExpression(boost::shared_ptr<EuclideanExpressionNode> root) : VectorExpression<3>(root) {}
      EuclideanExpression(const VectorExpression<3> & vectorExpression) : VectorExpression<3>(vectorExpression){}

      // Create a constant expression
      EuclideanExpression(const Eigen::Vector3d & p) : VectorExpression<3>(p) {}

      value_t toEuclidean() const { return evaluate(); }
      HomogeneousExpression toHomogeneousExpression() const;

      void evaluateJacobians(JacobianContainer & outJacobians) const;
      void evaluateJacobians(JacobianContainer & outJacobians, const Eigen::MatrixXd & applyChainRule) const;

      EuclideanExpression cross(const EuclideanExpression & p) const;
      EuclideanExpression operator+(const EuclideanExpression & p) const;
      EuclideanExpression operator-(const EuclideanExpression & p) const;
      EuclideanExpression operator-(const Eigen::Vector3d & p) const;
      EuclideanExpression operator-() const;
      EuclideanExpression operator*(const ScalarExpression& s) const;
      EuclideanExpression elementwiseMultiply(const EuclideanExpression & p) const;

    private:
      friend class RotationExpression;
      friend class MatrixExpression;
    };

  } // namespace backend
} // namespace aslam

#endif /* ASLAM_EUCLIDEAN_EXPRESSION_HPP */

ext: hpp