<commit_before>/*
 * CoreAlgorithm.cpp
 *
 */

#include "CoreAlgorithm.h"

using namespace std;
using namespace lemon;

namespace ysk {

	/**
	 * Generates a set of solutions from its internal parameters (instance and parameter set)
	 * @return The solution set as CES*
	 */
	ClusterEditingSolutions* CoreAlgorithm::run()
	{

		//At the beginning of a run we set the global termination flag to false ->
		//If we use this software as a lib it might have been set to true in a previous run
		isTerminated = false;

		if (verbosity >= 5)
			cout << _instance << endl;

		// warn user about permanent and forbidden edges
		const WorkingCopyGraph g = _instance->getWorkingCopyInstance().getGraph();
		if (verbosity > 1) {
			cout << endl
					<< "Interpreting the following edges as forbidden/permanent: "
					<< endl;
			for (WorkingCopyGraph::EdgeIt e(g); e != INVALID; ++e) {
				if (_instance->getWorkingCopyInstance().isForbidden(e))
					cout << _instance->getEdgeName(e) << "\tforbidden" << endl;

				if (_instance->getWorkingCopyInstance().isPermanent(e))
					cout << _instance->getEdgeName(e) << "\tpermanent" << endl;
			}
		}

		if (verbosity > 1) {
			cout << endl << "applying FPT reduction rules..." << endl;
			cout << "number of nodes:\t"
					<< countNodes(_instance->getWorkingCopyInstance().getGraph())
					<< endl;
		}

		if (isTerminated){
			return 0;
		}

		//Initialize the flags for the solution
		SolutionFlags flags;

		//Apply reduction rules//

		bitset<NUMBER_OF_REDUCTION_RULES> rules(_parameter.rulesBitMask);
		//Generate new CER instance
		ClusterEditingReduction cer(
				(!_parameter.useHeuristic && _parameter.targetClusterCount != -1) ? bitset<NUMBER_OF_REDUCTION_RULES>("000000"): rules, //TODO: Flexible default value here
				_parameter.multiplicativeFactor,
				_parameter.nrOptimalSolutions > 1 ? true : false
				);
		//If an informer is used register it with the CER to receive info
		if (_informer != nullptr){
			cer.registerInformer(_informer);
		}
                cer.perform(*_instance);

                if ((!_parameter.useHeuristic && _parameter.targetClusterCount != -1)){
                    cout << "Warning: Reduction rules were ignored as they are not available in ILP k-cluster mode" << endl;
                }
                else if (verbosity > 1) {
                    cout << "=========================" << endl;
                    cout << "FPT reduction rules applied exhaustively." << endl;
                    cout << "time:\t" << clk << endl;
                }

		flags.totalCost += cer.getTotalCost();

		vector<ClusterReductionInstance*>& reduced = cer.getInstances();
		flags.reducedInstances = reduced.size();

		//Sort CRI by size ascending to guarantee that the small instances are solved first
		std::sort(reduced.begin(),reduced.end());

		if (verbosity > 1) {
			cout << "total cost:\t" << cer.getTotalCost() << endl;
			cout << "number of instances:\t" << reduced.size() << endl;

			cout << endl << "==================================" << endl
					<< "==================================" << endl;
			cout << endl << "solving (possibly reduced) instances..." << endl;
			cout << "-------------------------" << endl;
		}

		if (verbosity > 3){
			for (vector<ClusterReductionInstance*>::iterator it = reduced.begin();
					it != reduced.end(); it++) {
				cout << "[INSTANCE]" << (*it)->getInstance()->getSize() <<endl;

			}
		}

		// If an outside call wants the run to terminate before solving instances
		// we have no guarantee of having something worth retrieving
		// TODO: In the long run CER should realize when it has already generated an optimal solution
		// (That would also get rid of the work around in the solver for instances of size 1 !
		if (isTerminated){
			//Delete reduced instances
			for (vector<ClusterReductionInstance*>::iterator it = reduced.begin();
					it != reduced.end(); it++) {
				delete (*it)->getInstance();
			}
			return 0;
		}

		int j = 0;

		long totalNumberOfSolutions = 1;

		vector<vector<vector<vector<int> > > > instances;

		//Iterate over the remaining reduced instances
		for (vector<ClusterReductionInstance*>::iterator it = reduced.begin();
				it != reduced.end(); it++, j++) {

			if (verbosity > 1) {
				cout << "solving instance 'no " << j << "'..." << endl;
			}

			ClusterEditingInstance& i = *(*it)->getInstance();

			ClusterEditingSolutions s;

			long numberOfSolutions = 1;

			//Solve the remaining and reduced instance either with the heuristic or the ILP
			if (!_parameter.useHeuristic) {

				//ILP
				if (_parameter.targetClusterCount != -1 && !_parameter.useHeuristic)
				{
					_solver = new ILPSolver (
							_parameter.separateTriangles,
							_parameter.separatePartitionCuts,
							_parameter.nrOptimalSolutions,
							_parameter.targetClusterCount
					);
				}
				else
				{
					_solver = new ILPSolver (
							_parameter.separateTriangles,
							_parameter.separatePartitionCuts,
							_parameter.nrOptimalSolutions
					);
				}

				if (_informer != nullptr){
					_solver->registerInformer(_informer);
				}

				try {
					numberOfSolutions = _solver->solve(i, s, flags);
				} catch (IloException &e) {
					cout << "CPLEX error: " << e.getMessage() << endl;
				}
				_solver->terminate();
				delete _solver;

			} else {
				if (verbosity > 1){
					cout << "Starting heuristic!" << endl;
				}
				//HEURISTIC
				ParameterizedInstance parameterizedInstance(
						i.getWorkingCopyInstance(),
						numeric_limits<double>::signaling_NaN());
				parameterizedInstance.init();
				InducedCostsHeuristic h(parameterizedInstance);
				h.start();
				flags.totalCost += h.getSolution(s);
			}

			if (verbosity > 1)
				cout << "time:\t" << clk << endl;

			totalNumberOfSolutions = totalNumberOfSolutions * numberOfSolutions;

			//expand solutions: replace merged nodes by cluster
			vector<vector<vector<int> > > partitions;
			expandSolutions(i, s, partitions);

			instances.push_back(partitions);

			if (verbosity > 1)
				cout << endl << "==================================" << endl
						<< endl;

			delete (*it)->getInstance();
		}
		if (verbosity > 1) {
			cout << "all instances solved." << endl;
			cout << "total number of solutions:\t" << totalNumberOfSolutions
					<< endl;
			cout << "total cost (data reduction + ILP or heuristic):\t";
		}
		if (verbosity > 0)
			cout << flags.totalCost << endl;

		_result->resize(totalNumberOfSolutions);

		size_t k = 0;
		vector<vector<int> > p;
		mergeSolutions(0, k, p, *_result, instances);

		//Restore timeout flag
		_result->setFlags(flags);

	  //Heuristic K-Cluster post-processing if desired

	  if (_parameter.targetClusterCount != -1 && _parameter.useHeuristic){
			if (verbosity >= 2)
				cout << "Aiming for the following cluster count: "<<_parameter.targetClusterCount << endl;
			//Generate a new k-clustifier instance
			KClustifier clustifier(_instance,_result);
			//Iterate over all clusters and k-clustify them
			for(size_t solutionID = 0; solutionID < _result->getNumberOfSolutions();solutionID++) {
				  clustifier.kClustify(_parameter.targetClusterCount, solutionID);
				  flags.totalCost += clustifier.getCosts();
			}
		    if (verbosity > 2)
		    	cout << "Total cost (post k-clustifier): " << flags.totalCost << endl;
	  }

		return _result;
	}

	void CoreAlgorithm::cancel(){
		isTerminated = true;

		if (_solver != nullptr){
			_solver->terminate();
		}
	}

	void CoreAlgorithm::registerCplexInformer(yskLib::CplexInformer* informer){
		if (verbosity > 2) cout << "Registering CplexInformer @ CoreAlgorithm" << endl;
		_informer = informer;
	}

	void expandSolutions(ClusterEditingInstance& cei,
			ClusterEditingSolutions& ces,
			vector<vector<vector<int> > >& partitions) {
		for (size_t k = 0; k < ces.getNumberOfSolutions(); k++) {
			vector<vector<int> > partition;
			for (size_t l = 0; l < ces.getNumberOfClusters(k); ++l) {
				vector<int> cluster;
				for (vector<int>::const_iterator cit = ces.getCluster(k, l).begin();
						cit != ces.getCluster(k, l).end(); ++cit) {
					cluster.insert(cluster.end(),
							cei.getCluster(cei.getOrig().nodeFromId(*cit))->begin(),
							cei.getCluster(cei.getOrig().nodeFromId(*cit))->end());
				}
				partition.push_back(cluster);
			}
			partitions.push_back(partition);
		}
	}

	void mergeSolutions(size_t i, size_t& k, vector<vector<int> >& partition,
			ClusterEditingSolutions& solutions,
			vector<vector<vector<vector<int> > > >& instances) {
		if (i == instances.size()) {
			solutions.getSolution(k).insert(solutions.getSolution(k).end(),
					partition.begin(), partition.end());
			k++;
			return;
		}
		for (size_t j = 0; j < instances[i].size(); j++) {
			vector<vector<int> > p;
			p.insert(p.end(), partition.begin(), partition.end());
			p.insert(p.end(), instances[i][j].begin(), instances[i][j].end());

			mergeSolutions(i + 1, k, p, solutions, instances);
		}
	}
}
<commit_msg>Separated reduction process more cleanly from rest of the algorithm<commit_after>/*
 * CoreAlgorithm.cpp
 *
 */

#include "CoreAlgorithm.h"

using namespace std;
using namespace lemon;

namespace ysk {

	/**
	 * Generates a set of solutions from its internal parameters (instance and parameter set)
	 * @return The solution set as CES*
	 */
	ClusterEditingSolutions* CoreAlgorithm::run()
	{

		//At the beginning of a run we set the global termination flag to false ->
		//If we use this software as a lib it might have been set to true in a previous run
		isTerminated = false;

		if (verbosity >= 5)
			cout << _instance << endl;

		// warn user about permanent and forbidden edges
		const WorkingCopyGraph g = _instance->getWorkingCopyInstance().getGraph();
		if (verbosity > 1) {
			cout << endl
					<< "Interpreting the following edges as forbidden/permanent: "
					<< endl;
			for (WorkingCopyGraph::EdgeIt e(g); e != INVALID; ++e) {
				if (_instance->getWorkingCopyInstance().isForbidden(e))
					cout << _instance->getEdgeName(e) << "\tforbidden" << endl;

				if (_instance->getWorkingCopyInstance().isPermanent(e))
					cout << _instance->getEdgeName(e) << "\tpermanent" << endl;
			}
		}

		if (verbosity > 1) {
			cout << endl << "applying FPT reduction rules..." << endl;
			cout << "number of nodes:\t"
					<< countNodes(_instance->getWorkingCopyInstance().getGraph())
					<< endl;
		}

		if (isTerminated){
			return 0;
		}

		//Initialize the flags for the solution
		SolutionFlags flags;

                //Initialize a vector holding the instances that need to be solved (will only be one instance if no reduction is applied)
                vector<ClusterEditingInstance*> clusterEditingInstances;

                if ((!_parameter.useHeuristic && _parameter.targetClusterCount != -1)){
                    cout << "Warning: Reduction rules were ignored as they are not available in ILP k-cluster mode" << endl;
                }

                else if (verbosity > 1) {

                    //Apply reduction rules//

                    bitset<NUMBER_OF_REDUCTION_RULES> rules(_parameter.rulesBitMask);
                    //Generate new CER instance
                    ClusterEditingReduction cer(
                                    (!_parameter.useHeuristic && _parameter.targetClusterCount != -1) ? bitset<NUMBER_OF_REDUCTION_RULES>("000000"): rules, //TODO: Flexible default value here
                                    _parameter.multiplicativeFactor,
                                    _parameter.nrOptimalSolutions > 1 ? true : false
                                    );
                    //If an informer is used register it with the CER to receive info
                    if (_informer != nullptr){
                            cer.registerInformer(_informer);
                    }

                    cer.perform(*_instance);

                    flags.totalCost += cer.getTotalCost();

                    if (verbosity > 1){

                        cout << "=========================" << endl;
                        cout << "FPT reduction rules applied exhaustively." << endl;
                        cout << "time:\t" << clk << endl;
                        cout << "total cost of reduction:\t" << cer.getTotalCost() << endl;
                        cout << "number of instances:\t" << clusterEditingInstances.size() << endl;
                    }

                    vector<ClusterReductionInstance*>& reduced = cer.getInstances();
                    flags.reducedInstances = reduced.size();

                        //Move reduced instances
			for (vector<ClusterReductionInstance*>::iterator it = reduced.begin();
					it != reduced.end(); it++) {
                            clusterEditingInstances.push_back((*it)->getInstance());
			}
                }

		//Sort CRI by size ascending to guarantee that the small instances are solved first
		std::sort(clusterEditingInstances.begin(),clusterEditingInstances.end());

		if (verbosity > 1) {

			cout << endl << "==================================" << endl
					<< "==================================" << endl;
			cout << endl << "solving (possibly reduced) instances..." << endl;
			cout << "-------------------------" << endl;
		}

		if (verbosity > 3){
			for (vector<ClusterEditingInstance*>::iterator it = clusterEditingInstances.begin();
					it != clusterEditingInstances.end(); it++) {
				cout << "[INSTANCE]" << (*it)->getSize() <<endl;

			}
		}

		// If an outside call wants the run to terminate before solving instances
		// we have no guarantee of having something worth retrieving
		// TODO: In the long run CER should realize when it has already generated an optimal solution
		// (That would also get rid of the work around in the solver for instances of size 1 !
		if (isTerminated){
			//Delete reduced instances
			for (vector<ClusterEditingInstance*>::iterator it = clusterEditingInstances.begin();
					it != clusterEditingInstances.end(); it++) {
				delete (*it);
			}
			return 0;
		}

		int j = 0;

		long totalNumberOfSolutions = 1;

                //This will hold a simplified representation of the instances in the end
		vector<vector<vector<vector<int> > > > instances;

		//Iterate over the remaining reduced instances
		for (vector<ClusterEditingInstance*>::iterator it = clusterEditingInstances.begin();
				it != clusterEditingInstances.end(); it++, j++) {

			if (verbosity > 1) {
				cout << "solving instance 'no " << j << "'..." << endl;
			}

			ClusterEditingInstance& i = *(*it);

			ClusterEditingSolutions s;

			long numberOfSolutions = 1;

			//Solve the remaining and reduced instance either with the heuristic or the ILP
			if (!_parameter.useHeuristic) {

				//ILP
				if (_parameter.targetClusterCount != -1 && !_parameter.useHeuristic)
				{
					_solver = new ILPSolver (
							_parameter.separateTriangles,
							_parameter.separatePartitionCuts,
							_parameter.nrOptimalSolutions,
							_parameter.targetClusterCount
					);
				}
				else
				{
					_solver = new ILPSolver (
							_parameter.separateTriangles,
							_parameter.separatePartitionCuts,
							_parameter.nrOptimalSolutions
					);
				}

				if (_informer != nullptr){
					_solver->registerInformer(_informer);
				}

				try {
					numberOfSolutions = _solver->solve(i, s, flags);
				} catch (IloException &e) {
					cout << "CPLEX error: " << e.getMessage() << endl;
				}
				_solver->terminate();
				delete _solver;

			} else {
				if (verbosity > 1){
					cout << "Starting heuristic!" << endl;
				}
				//HEURISTIC
				ParameterizedInstance parameterizedInstance(
						i.getWorkingCopyInstance(),
						numeric_limits<double>::signaling_NaN());
				parameterizedInstance.init();
				InducedCostsHeuristic h(parameterizedInstance);
				h.start();
				flags.totalCost += h.getSolution(s);
			}

			if (verbosity > 1)
				cout << "time:\t" << clk << endl;

			totalNumberOfSolutions = totalNumberOfSolutions * numberOfSolutions;

			//expand solutions: replace merged nodes by cluster
			vector<vector<vector<int> > > partitions;
			expandSolutions(i, s, partitions);

			instances.push_back(partitions);

			if (verbosity > 1)
				cout << endl << "==================================" << endl
						<< endl;

			delete (*it);
		}
		if (verbosity > 1) {
			cout << "all instances solved." << endl;
			cout << "total number of solutions:\t" << totalNumberOfSolutions
					<< endl;
			cout << "total cost (data reduction + ILP or heuristic):\t";
		}
		if (verbosity > 0)
			cout << flags.totalCost << endl;

		_result->resize(totalNumberOfSolutions);

		size_t k = 0;
		vector<vector<int> > p;
		mergeSolutions(0, k, p, *_result, instances);

		//Restore timeout flag
		_result->setFlags(flags);

	  //Heuristic K-Cluster post-processing if desired

	  if (_parameter.targetClusterCount != -1 && _parameter.useHeuristic){
			if (verbosity >= 2)
				cout << "Aiming for the following cluster count: "<<_parameter.targetClusterCount << endl;
			//Generate a new k-clustifier instance
			KClustifier clustifier(_instance,_result);
			//Iterate over all clusters and k-clustify them
			for(size_t solutionID = 0; solutionID < _result->getNumberOfSolutions();solutionID++) {
				  clustifier.kClustify(_parameter.targetClusterCount, solutionID);
				  flags.totalCost += clustifier.getCosts();
			}
		    if (verbosity > 2)
		    	cout << "Total cost (post k-clustifier): " << flags.totalCost << endl;
	  }

		return _result;
	}

	void CoreAlgorithm::cancel(){
		isTerminated = true;

		if (_solver != nullptr){
			_solver->terminate();
		}
	}

	void CoreAlgorithm::registerCplexInformer(yskLib::CplexInformer* informer){
		if (verbosity > 2) cout << "Registering CplexInformer @ CoreAlgorithm" << endl;
		_informer = informer;
	}

	void expandSolutions(ClusterEditingInstance& cei,
			ClusterEditingSolutions& ces,
			vector<vector<vector<int> > >& partitions) {
		for (size_t k = 0; k < ces.getNumberOfSolutions(); k++) {
			vector<vector<int> > partition;
			for (size_t l = 0; l < ces.getNumberOfClusters(k); ++l) {
				vector<int> cluster;
				for (vector<int>::const_iterator cit = ces.getCluster(k, l).begin();
						cit != ces.getCluster(k, l).end(); ++cit) {
					cluster.insert(cluster.end(),
							cei.getCluster(cei.getOrig().nodeFromId(*cit))->begin(),
							cei.getCluster(cei.getOrig().nodeFromId(*cit))->end());
				}
				partition.push_back(cluster);
			}
			partitions.push_back(partition);
		}
	}

	void mergeSolutions(size_t i, size_t& k, vector<vector<int> >& partition,
			ClusterEditingSolutions& solutions,
			vector<vector<vector<vector<int> > > >& instances) {
		if (i == instances.size()) {
			solutions.getSolution(k).insert(solutions.getSolution(k).end(),
					partition.begin(), partition.end());
			k++;
			return;
		}
		for (size_t j = 0; j < instances[i].size(); j++) {
			vector<vector<int> > p;
			p.insert(p.end(), partition.begin(), partition.end());
			p.insert(p.end(), instances[i][j].begin(), instances[i][j].end());

			mergeSolutions(i + 1, k, p, solutions, instances);
		}
	}
}
<|endoftext|>


<commit_before>/*
 * bacteria-core, core for cellular automaton
 * Copyright (C) 2016 Pavel Dolgov
 *
 * See the LICENSE file for terms of use.
 */

#ifndef CORE_CONSTANTS_HPP_
#define CORE_CONSTANTS_HPP_

/** Mass increase per one 'eat' command */
const int EAT_MASS = 1;

/** Mass increase per one 'go' command */
const int GO_MASS = -1;

/** Mass increase per one 'clon' command */
const int CLON_MASS = -10;

const int MAX_COMMANDS_PER_INSTRUCTION = 100;

const int MIN_COMMANDS_PER_INSTRUCTION = 1;

/** Maximum randomly generated number of actions (per instruction) */
const int RANDOM_MAX_ACTIONS = 5;

/** Maximum number of actions per move */
const int MAX_ACTIONS = 1;

/** Maximum number of pseudo actions per move */
const int MAX_PSEUDO_ACTIONS = 30;

/** Default mass of new bacteria */
const int DEFAULT_MASS = 5;

/** Default mass of cloned bacteria */
const int DEFAULT_CLON_MASS = 2;

/** Minimum width of board */
const int MIN_WIDTH = 5;

/** Maximum width of board */
const int MAX_WIDTH = 500;

/** Minimum height of board */
const int MIN_HEIGHT = 5;

/** Maximum height of board */
const int MAX_HEIGHT = 500;

#endif
<commit_msg>Add new constant STR_MASS<commit_after>/*
 * bacteria-core, core for cellular automaton
 * Copyright (C) 2016 Pavel Dolgov
 *
 * See the LICENSE file for terms of use.
 */

#ifndef CORE_CONSTANTS_HPP_
#define CORE_CONSTANTS_HPP_

/** Mass increase per one 'eat' command */
const int EAT_MASS = 1;

/** Mass increase per one 'go' command */
const int GO_MASS = -1;

/** Mass increase per one 'clon' command */
const int CLON_MASS = -10;

/** Mass increase per one 'str' command */
const int STR_MASS = -1;

const int MAX_COMMANDS_PER_INSTRUCTION = 100;

const int MIN_COMMANDS_PER_INSTRUCTION = 1;

/** Maximum randomly generated number of actions (per instruction) */
const int RANDOM_MAX_ACTIONS = 5;

/** Maximum number of actions per move */
const int MAX_ACTIONS = 1;

/** Maximum number of pseudo actions per move */
const int MAX_PSEUDO_ACTIONS = 30;

/** Default mass of new bacteria */
const int DEFAULT_MASS = 5;

/** Default mass of cloned bacteria */
const int DEFAULT_CLON_MASS = 2;

/** Minimum width of board */
const int MIN_WIDTH = 5;

/** Maximum width of board */
const int MAX_WIDTH = 500;

/** Minimum height of board */
const int MIN_HEIGHT = 5;

/** Maximum height of board */
const int MAX_HEIGHT = 500;

#endif
<|endoftext|>


<commit_before>/*
 * Copyright 2015 Milian Wolff <mail@milianw.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "chartwidget.h"

#include <QVBoxLayout>

#include <KChartChart>
#include <KChartPlotter>

#include <KChartGridAttributes>
#include <KChartHeaderFooter>
#include <KChartCartesianCoordinatePlane>
#include <KChartLegend>
#include <KChartDataValueAttributes>
#include <KChartBackgroundAttributes>
#include <KChartFrameAttributes.h>

#include <KFormat>
#include <KLocalizedString>
#include <KColorScheme>

#include "chartproxy.h"

using namespace KChart;

namespace {
class TimeAxis : public CartesianAxis
{
    Q_OBJECT
public:
    explicit TimeAxis(AbstractCartesianDiagram* diagram = 0)
        : CartesianAxis(diagram)
    {}

    virtual const QString customizedLabel(const QString& label) const
    {
        // squeeze large numbers here
        return QString::number(label.toDouble() / 1000, 'g', 2) + QLatin1Char('s');
    }
};

class SizeAxis : public CartesianAxis
{
    Q_OBJECT
public:
    explicit SizeAxis(AbstractCartesianDiagram* diagram = 0)
        : CartesianAxis(diagram)
    {}

    virtual const QString customizedLabel(const QString& label) const
    {
        // TODO: change distance between labels to 1024 and simply use prettyCost() here
        KFormat format(QLocale::system());
        return format.formatByteSize(label.toDouble(), 1, KFormat::MetricBinaryDialect);
    }
};
}

ChartWidget::ChartWidget(QWidget* parent)
    : QWidget(parent)
    , m_chart(new KChart::Chart(this))
    , m_plotter(new Plotter(this))
{
    auto layout = new QVBoxLayout(this);
    layout->addWidget(m_chart);
    setLayout(layout);

    m_plotter->setAntiAliasing(true);
    m_plotter->setType(KChart::Plotter::Stacked);

    auto* coordinatePlane = dynamic_cast<CartesianCoordinatePlane*>(m_chart->coordinatePlane());
    Q_ASSERT(coordinatePlane);
    coordinatePlane->addDiagram(m_plotter);
}

ChartWidget::~ChartWidget() = default;

void ChartWidget::setModel(ChartModel* model, ChartModel::Columns costColumn)
{
    if (m_plotter->model()) {
        delete m_plotter->model();
    }

    Q_ASSERT(costColumn != ChartModel::TimeStampColumn);
    auto proxy = new ChartProxy(costColumn, false, this);
    proxy->setSourceModel(model);

    foreach (auto axis, m_plotter->axes()) {
        m_plotter->takeAxis(axis);
        delete axis;
    }

    KColorScheme scheme(QPalette::Active, KColorScheme::Window);
    const QPen foreground(scheme.foreground().color());
    auto bottomAxis = new TimeAxis(m_plotter);
    auto axisTextAttributes = bottomAxis->textAttributes();
    axisTextAttributes.setPen(foreground);
    bottomAxis->setTextAttributes(axisTextAttributes);
    auto axisTitleTextAttributes = bottomAxis->titleTextAttributes();
    axisTitleTextAttributes.setPen(foreground);
    bottomAxis->setTitleTextAttributes(axisTitleTextAttributes);
    bottomAxis->setTitleText(model->headerData(ChartModel::TimeStampColumn).toString());
    bottomAxis->setPosition(KChart::CartesianAxis::Bottom);
    m_plotter->addAxis(bottomAxis);

    CartesianAxis* rightAxis = costColumn == ChartModel::AllocationsColumn ? new CartesianAxis(m_plotter) : new SizeAxis(m_plotter);
    rightAxis->setTextAttributes(axisTextAttributes);
    rightAxis->setTitleTextAttributes(axisTitleTextAttributes);
    rightAxis->setTitleText(model->headerData(costColumn).toString());
    rightAxis->setPosition(CartesianAxis::Right);
    m_plotter->addAxis(rightAxis);

    m_plotter->setModel(proxy);
}

#include "chartwidget.moc"
<commit_msg>cleanup<commit_after>/*
 * Copyright 2015 Milian Wolff <mail@milianw.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "chartwidget.h"

#include <QVBoxLayout>

#include <KChartChart>
#include <KChartPlotter>

#include <KChartGridAttributes>
#include <KChartHeaderFooter>
#include <KChartCartesianCoordinatePlane>
#include <KChartLegend>
#include <KChartDataValueAttributes>
#include <KChartBackgroundAttributes>
#include <KChartFrameAttributes.h>

#include <KFormat>
#include <KLocalizedString>
#include <KColorScheme>

#include "chartproxy.h"

using namespace KChart;

namespace {
class TimeAxis : public CartesianAxis
{
    Q_OBJECT
public:
    explicit TimeAxis(AbstractCartesianDiagram* diagram = nullptr)
        : CartesianAxis(diagram)
    {}

    const QString customizedLabel(const QString& label) const override
    {
        // squeeze large numbers here
        return QString::number(label.toDouble() / 1000, 'g', 2) + QLatin1Char('s');
    }
};

class SizeAxis : public CartesianAxis
{
    Q_OBJECT
public:
    explicit SizeAxis(AbstractCartesianDiagram* diagram = nullptr)
        : CartesianAxis(diagram)
    {}

    const QString customizedLabel(const QString& label) const override
    {
        // TODO: change distance between labels to 1024 and simply use prettyCost() here
        KFormat format(QLocale::system());
        return format.formatByteSize(label.toDouble(), 1, KFormat::MetricBinaryDialect);
    }
};
}

ChartWidget::ChartWidget(QWidget* parent)
    : QWidget(parent)
    , m_chart(new Chart(this))
    , m_plotter(new Plotter(this))
{
    auto layout = new QVBoxLayout(this);
    layout->addWidget(m_chart);
    setLayout(layout);

    m_plotter->setAntiAliasing(true);
    m_plotter->setType(Plotter::Stacked);

    auto* coordinatePlane = dynamic_cast<CartesianCoordinatePlane*>(m_chart->coordinatePlane());
    Q_ASSERT(coordinatePlane);
    coordinatePlane->addDiagram(m_plotter);
}

ChartWidget::~ChartWidget() = default;

void ChartWidget::setModel(ChartModel* model, ChartModel::Columns costColumn)
{
    if (m_plotter->model()) {
        delete m_plotter->model();
    }

    Q_ASSERT(costColumn != ChartModel::TimeStampColumn);
    auto proxy = new ChartProxy(costColumn, false, this);
    proxy->setSourceModel(model);

    foreach (auto axis, m_plotter->axes()) {
        m_plotter->takeAxis(axis);
        delete axis;
    }

    KColorScheme scheme(QPalette::Active, KColorScheme::Window);
    const QPen foreground(scheme.foreground().color());
    auto bottomAxis = new TimeAxis(m_plotter);
    auto axisTextAttributes = bottomAxis->textAttributes();
    axisTextAttributes.setPen(foreground);
    bottomAxis->setTextAttributes(axisTextAttributes);
    auto axisTitleTextAttributes = bottomAxis->titleTextAttributes();
    axisTitleTextAttributes.setPen(foreground);
    bottomAxis->setTitleTextAttributes(axisTitleTextAttributes);
    bottomAxis->setTitleText(model->headerData(ChartModel::TimeStampColumn).toString());
    bottomAxis->setPosition(CartesianAxis::Bottom);
    m_plotter->addAxis(bottomAxis);

    CartesianAxis* rightAxis = costColumn == ChartModel::AllocationsColumn ? new CartesianAxis(m_plotter) : new SizeAxis(m_plotter);
    rightAxis->setTextAttributes(axisTextAttributes);
    rightAxis->setTitleTextAttributes(axisTitleTextAttributes);
    rightAxis->setTitleText(model->headerData(costColumn).toString());
    rightAxis->setPosition(CartesianAxis::Right);
    m_plotter->addAxis(rightAxis);

    m_plotter->setModel(proxy);
}

#include "chartwidget.moc"
<|endoftext|>


<commit_before>#include <algorithm>
#include <cassert>
#include <climits>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <sstream>

using namespace std;

const int NX=256, NY=NX;
const int T_FINAL = 1024;

const int X_MASK = NX-1, Y_MASK=NY-1;

const int NT=32;
const int NTO=NX/NT;
const int NF=NX/2;

double dens_initial[NX][NX];
double dens_final[NX][NX];

double yuka[NTO][NTO][1][NT+2][NT+2];
double yuka_tmp[1][NT+2][NT+2];
double kabe_y[NTO][NTO][NF+1][2][NT+2];
double kabe_x[NTO][NTO][NF+1][NT+2][2];

void initialize() {
  for (int y=0; y<NY; ++y) {
    for (int x=0; x<NX; ++x) {
      dens_initial[y][x]=(rand()/double(INT_MAX)*2 > 1 ? 1 : 0);
      dens_final[y][x]=424242;
    }
  }
}
void dump(const char* fn) {
  ofstream ofs(fn);
  for (int y=0; y<NY; ++y) {
    for(int x=0;x<NX;++x) {
      ofs << x << " " << y << " " << dens_final[y][x] << endl;
    }
    ofs << endl;
  }
}

inline double stencil_function(double o, double a, double b, double c, double d) {
  return (o+a+b+c+d)/5;
}

double ref_buf[NX][NX];
double ref_buf2[NX][NX];

void compute_reference() {
  for (int y=0;y<NY;++y) {
    for (int x=0;x<NX;++x) {
      ref_buf[y][x]=dens_initial[y][x];
    }
  }

  for (int t=1; t <=T_FINAL; ++t) {
    for (int y=0;y<NY;++y) {
      for (int x=0;x<NX;++x) {
        ref_buf2[y][x]=stencil_function
          (ref_buf[y][x],
           ref_buf[(y-1)&Y_MASK][x],
           ref_buf[(y+1)&Y_MASK][x],
           ref_buf[y][(x-1)&X_MASK],
           ref_buf[y][(x+1)&X_MASK]
           );
      }
    }
    swap(ref_buf, ref_buf2);
  }
  for (int y=0;y<NY;++y) {
    for (int x=0;x<NX;++x) {
      dens_final[y][x] = ref_buf[y][x];
    }
  }
}

double buf[NT+2][NT+2];
double buf2[NT+2][NT+2];

void pitch_kernel
(int t_orig, int y_orig, int x_orig,
 double yuka_in[1][NT+2][NT+2], double kabe_y_in[NF+1][2][NT+2], double kabe_x_in[NF+1][NT+2][2],
 double yuka_out[1][NT+2][NT+2], double kabe_y_out[NF+1][2][NT+2], double kabe_x_out[NF+1][NT+2][2])
{
  for(int t=0; t<NF+NT/2+2;++t) {
    for(int y=0; y<NT+2; ++y) {
      for(int x=0; x<NT+2; ++x) {
        int t_k=t, y_k = y-t, x_k = x-t;
        int t_dash = (2*t_k-x_k-y_k)>>2;
        int y_dash = y;
        int x_dash = x;
        double ret=0;

        const bool in_region = t_dash >=0 && t_dash < NF+1;

        if (in_region) {
          if (t+t_orig>0 && y>=2 && x>=2) {
            ret = stencil_function(buf[y-1][x-1],buf[y-2][x-1],buf[y][x-1],buf[y-1][x-2],buf[y-1][x]);
          }
          if (t_dash == 0) {
            ret = yuka_in[0][y_dash][x_dash];
          }
          if (y_dash < 2) {
            ret = kabe_y_in[t_dash][y_dash][x_dash];
          }
          if (x_dash < 2) {
            ret = kabe_x_in[t_dash][y_dash][x_dash];
          }
        }
        if (t_k + t_orig == 0) {
          ret = dens_initial[(y_k+y_orig) & Y_MASK][(x_k+x_orig) & X_MASK];
        }

        buf2[y][x] = ret;

        if (in_region) {
          if (t_dash == NF) {
            yuka_out[0][y_dash][x_dash] = ret;
          }
          if (y_dash >= NT) {
            kabe_y_out[t_dash][y_dash-NT][x_dash] = ret;
          }
          if (x_dash >= NT) {
            kabe_x_out[t_dash][y_dash][x_dash-NT] = ret;
          }
          if (t_k + t_orig == T_FINAL) {
            dens_final[(y_k+y_orig) & Y_MASK][(x_k+x_orig) & X_MASK] = ret;
          }
        }
      }
    }
    swap(buf, buf2);
  }
}

void compute_pitch(){
  for(int t_orig=-2*NX; t_orig <= T_FINAL+2*NF; t_orig+=NF) {
    int y_orig = t_orig;
    int x_orig = t_orig;
    for (int yo=0;yo<NTO;++yo) {
      for (int xo=0;xo<NTO;++xo) {
        int dy = yo*NT, dx = xo*NT;
        pitch_kernel
          (t_orig+(dx+dy)/4,
           y_orig+(3*dy-dx)/4,
           x_orig+(3*dx-dy)/4,
           yuka[yo][xo],kabe_y[yo][xo],kabe_x[yo][xo],
           yuka_tmp,kabe_y[(yo+1)%NTO][xo],kabe_x[yo][(xo+1)%NTO]);
        swap(yuka[yo][xo], yuka_tmp);
      }
    }
  }
}

int main ()
{
  initialize();
  compute_pitch();
  dump("test-2d-pitch.txt");
  compute_reference();
  dump("test-2d-ref.txt");
}
<commit_msg>2d kanzen-ni icchi<commit_after>#include <algorithm>
#include <cassert>
#include <climits>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <sstream>

using namespace std;

const int NX=256, NY=NX;
const int T_FINAL = 192;

const int X_MASK = NX-1, Y_MASK=NY-1;

const int NT=32;
const int NTO=NX/NT;
const int NF=NX/2;

double dens_initial[NX][NX];
double dens_final[NX][NX];

double yuka[NTO][NTO][1][NT+2][NT+2];
double yuka2[NTO][NTO][1][NT+2][NT+2];

double yuka_tmp[1][NT+2][NT+2];
double kabe_y[NTO][NTO][NF+1][2][NT+2];
double kabe_x[NTO][NTO][NF+1][NT+2][2];

void initialize() {
  for (int y=0; y<NY; ++y) {
    for (int x=0; x<NX; ++x) {
      dens_initial[y][x]=(rand()/double(INT_MAX)*2 > 1 ? 1 : 0);
      dens_final[y][x]=424242;
    }
  }
}
void dump(const char* fn) {
  ofstream ofs(fn);
  for (int y=0; y<NY; ++y) {
    for(int x=0;x<NX;++x) {
      ofs << x << " " << y << " " << dens_final[y][x] << endl;
    }
    ofs << endl;
  }
}

inline double stencil_function(double o, double a, double b, double c, double d) {
  return (o+a+b+c+d)/5;
}

double ref_buf[NX][NX];
double ref_buf2[NX][NX];

void compute_reference() {
  for (int y=0;y<NY;++y) {
    for (int x=0;x<NX;++x) {
      ref_buf[y][x]=dens_initial[y][x];
    }
  }

  for (int t=1; t <=T_FINAL; ++t) {
    for (int y=0;y<NY;++y) {
      for (int x=0;x<NX;++x) {
        ref_buf2[y][x]=stencil_function
          (ref_buf[y][x],
           ref_buf[(y-1)&Y_MASK][x],
           ref_buf[(y+1)&Y_MASK][x],
           ref_buf[y][(x-1)&X_MASK],
           ref_buf[y][(x+1)&X_MASK]
           );
      }
    }
    swap(ref_buf, ref_buf2);
  }
  for (int y=0;y<NY;++y) {
    for (int x=0;x<NX;++x) {
      dens_final[y][x] = ref_buf[y][x];
    }
  }
}

double buf[NT+2][NT+2];
double buf2[NT+2][NT+2];

void pitch_kernel
(int t_orig, int y_orig, int x_orig,
 double yuka_in[1][NT+2][NT+2], double kabe_y_in[NF+1][2][NT+2], double kabe_x_in[NF+1][NT+2][2],
 double yuka_out[1][NT+2][NT+2], double kabe_y_out[NF+1][2][NT+2], double kabe_x_out[NF+1][NT+2][2])
{
  for(int t=0; t<NF+NT/2+2;++t) {
    for(int y=0; y<NT+2; ++y) {
      for(int x=0; x<NT+2; ++x) {
        int t_k=t, y_k = y-t, x_k = x-t;
        int t_dash = (2*t_k-x_k-y_k)>>2;
        int y_dash = y;
        int x_dash = x;
        double ret=0;

        const bool in_region = t_dash >=0 && t_dash < NF+1;

        if (in_region) {
          if (t+t_orig>0 && y>=2 && x>=2) {
            ret = stencil_function(buf[y-1][x-1],buf[y-2][x-1],buf[y][x-1],buf[y-1][x-2],buf[y-1][x]);
          }
          if (t_dash == 0) {
            ret = yuka_in[0][y_dash][x_dash];
          }
          if (y_dash < 2) {
            ret = kabe_y_in[t_dash][y_dash][x_dash];
          }
          if (x_dash < 2) {
            ret = kabe_x_in[t_dash][y_dash][x_dash];
          }
          if (t_k + t_orig == 0) {
            ret = dens_initial[(y_k+y_orig) & Y_MASK][(x_k+x_orig) & X_MASK];
          }

          buf2[y][x] = ret;

          if (t_dash == NF) {
            yuka_out[0][y_dash][x_dash] = ret;
          }
          if (y_dash >= NT) {
            kabe_y_out[t_dash][y_dash-NT][x_dash] = ret;
          }
          if (x_dash >= NT) {
            kabe_x_out[t_dash][y_dash][x_dash-NT] = ret;
          }
          if (t_k + t_orig == T_FINAL) {
            dens_final[(y_k+y_orig) & Y_MASK][(x_k+x_orig) & X_MASK] = ret;
          }
        }
      }
    }
    swap(buf, buf2);
  }
}

void compute_pitch(){
  for(int t_orig=-10*NX; t_orig <= T_FINAL+2*NX; t_orig+=NF) {
    int y_orig = -t_orig;
    int x_orig = -t_orig;
    for (int yo=0;yo<NTO;++yo) {
      for (int xo=0;xo<NTO;++xo) {
        int dy = yo*NT, dx = xo*NT;
        pitch_kernel
          (t_orig+(dx+dy)/4,
           y_orig+(3*dy-dx)/4,
           x_orig+(3*dx-dy)/4,
           yuka[yo][xo],kabe_y[yo][xo],kabe_x[yo][xo],
           yuka_tmp,kabe_y[(yo+1)%NTO][xo],kabe_x[yo][(xo+1)%NTO]);
        swap(yuka[yo][xo], yuka_tmp);
      }
    }
    int t_orig2 =  t_orig+NF/2;
    int y_orig2 = -t_orig+3*NX/4;
    int x_orig2 = -t_orig-NX/4;
    for (int yo=0;yo<NTO;++yo) {
      for (int xo=0;xo<NTO;++xo) {
        int dy = yo*NT, dx = xo*NT;
        pitch_kernel
          (t_orig2+(dx+dy)/4,
           y_orig2+(3*dy-dx)/4,
           x_orig2+(3*dx-dy)/4,
           yuka2[yo][xo],kabe_y[yo][xo],kabe_x[yo][xo],
           yuka_tmp,kabe_y[(yo+1)%NTO][xo],kabe_x[yo][(xo+1)%NTO]);
        swap(yuka2[yo][xo], yuka_tmp);
      }
    }

  }
}

int main ()
{
  initialize();
  compute_pitch();
  dump("test-2d-pitch.txt");
  compute_reference();
  dump("test-2d-ref.txt");
}
<|endoftext|>


<commit_before>/* OpenSceneGraph example, osgparticleshader.
*
*  Permission is hereby granted, free of charge, to any person obtaining a copy
*  of this software and associated documentation files (the "Software"), to deal
*  in the Software without restriction, including without limitation the rights
*  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
*  copies of the Software, and to permit persons to whom the Software is
*  furnished to do so, subject to the following conditions:
*
*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
*  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
*  THE SOFTWARE.
*/

#include <iostream>

#include <osg/ShapeDrawable>
#include <osg/MatrixTransform>
#include <osg/Point>
#include <osg/PointSprite>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgGA/TrackballManipulator>
#include <osgGA/StateSetManipulator>
#include <osgViewer/ViewerEventHandlers>
#include <osgViewer/Viewer>

#include <osgParticle/ParticleSystem>
#include <osgParticle/ParticleSystemUpdater>
#include <osgParticle/ModularEmitter>
#include <osgParticle/ModularProgram>

#include <osgParticle/AccelOperator>
#include <osgParticle/DampingOperator>
#include <osgParticle/BounceOperator>
#include <osgParticle/SinkOperator>

void createFountainEffect( osgParticle::ModularEmitter* emitter, osgParticle::ModularProgram* program )
{
    // Emit specific number of particles every frame
    osg::ref_ptr<osgParticle::RandomRateCounter> rrc = new osgParticle::RandomRateCounter;
    rrc->setRateRange( 500, 2000 );

    // Accelerate particles in the given gravity direction.
    osg::ref_ptr<osgParticle::AccelOperator> accel = new osgParticle::AccelOperator;
    accel->setToGravity();

    // Multiply each particle's velocity by a damping constant.
    osg::ref_ptr<osgParticle::DampingOperator> damping = new osgParticle::DampingOperator;
    damping->setDamping( 0.9f );

    // Bounce particles off objects defined by one or more domains.
    // Supported domains include triangle, rectangle, plane, disk and sphere.
    // Since a bounce always happens instantaneously, it will not work correctly with unstable delta-time.
    // At present, even the floating error of dt (which are applied to ParticleSystem and Operator separately)
    // causes wrong bounce results. Some one else may have better solutions for this.
    osg::ref_ptr<osgParticle::BounceOperator> bounce = new osgParticle::BounceOperator;
    bounce->setFriction( -0.05 );
    bounce->setResilience( 0.35 );
    bounce->addDiskDomain( osg::Vec3(0.0f, 0.0f, -2.0f), osg::Z_AXIS, 8.0f );
    bounce->addPlaneDomain( osg::Plane(osg::Z_AXIS, 5.0f) );

    // Kill particles going inside/outside of specified domains.
    osg::ref_ptr<osgParticle::SinkOperator> sink = new osgParticle::SinkOperator;
    sink->setSinkStrategy( osgParticle::SinkOperator::SINK_OUTSIDE );
    sink->addSphereDomain( osg::Vec3(), 20.0f );

    emitter->setCounter( rrc.get() );
    program->addOperator( accel.get() );
    program->addOperator( damping.get() );
    program->addOperator( bounce.get() );
    program->addOperator( sink.get() );
}

int main( int argc, char** argv )
{
    osg::ArgumentParser arguments( &argc, argv );

    std::string textureFile("Images/smoke.rgb");
    while ( arguments.read("--texture", textureFile) ) {}

    float pointSize = 20.0f;
    while ( arguments.read("--point", pointSize) ) {}

    double visibilityDistance = -1.0f;
    while ( arguments.read("--visibility", visibilityDistance) ) {}

    bool useShaders = true;
    while ( arguments.read("--disable-shaders") ) { useShaders = false; }

    /***
    Customize particle template and system attributes
    ***/
    osg::ref_ptr<osgParticle::ParticleSystem> ps = new osgParticle::ParticleSystem;

    ps->getDefaultParticleTemplate().setLifeTime( 5.0f );

    // The shader only supports rendering points at present.
    ps->getDefaultParticleTemplate().setShape( osgParticle::Particle::POINT );

    // Set the visibility distance of particles, due to their Z-value in the eye coordinates.
    // Particles that are out of the distance (or behind the eye) will not be rendered.
    ps->setVisibilityDistance( visibilityDistance );

    if ( useShaders )
    {
        // Set using local GLSL shaders to render particles.
        // At present, this is slightly efficient than ordinary methods. The bottlenack here seems to be the cull
        // traversal time. Operators go through the particle list again and again...
        ps->setDefaultAttributesUsingShaders( textureFile, true, 0 );
    }
    else
    {
        // The default methods uses glBegin()/glEnd() pairs. Fortunately the GLBeginEndAdapter does improve the
        // process, which mimics the immediate mode with glDrawArrays().
        ps->setDefaultAttributes( textureFile, true, false, 0 );

        // Without the help of shaders, we have to sort particles to make the visibility distance work. Sorting is
        // also useful in rendering transparent particles in back-to-front order.
        if ( visibilityDistance>0.0 )
            ps->setSortMode( osgParticle::ParticleSystem::SORT_BACK_TO_FRONT );
    }

    // At last, to make the point sprite work, we have to set the points size and the sprite attribute.
    osg::StateSet* stateset = ps->getOrCreateStateSet();
    stateset->setAttribute( new osg::Point(pointSize) );
    stateset->setTextureAttributeAndModes( 0, new osg::PointSprite, osg::StateAttribute::ON );

    /***
    Construct other particle system elements, including the emitter and program
    ***/
    osg::ref_ptr<osgParticle::ModularEmitter> emitter = new osgParticle::ModularEmitter;
    emitter->setParticleSystem( ps.get() );

    osg::ref_ptr<osgParticle::ModularProgram> program = new osgParticle::ModularProgram;
    program->setParticleSystem( ps.get() );

    createFountainEffect( emitter.get(), program.get() );

    /***
    Add the entire particle system to the scene graph
    ***/
    osg::ref_ptr<osg::MatrixTransform> parent = new osg::MatrixTransform;
    parent->addChild( emitter.get() );
    parent->addChild( program.get() );

    // The updater can receive particle systems as child drawables now. The addParticleSystem() method
    // is still usable, with which we should define another geode to contain a particle system.
    osg::ref_ptr<osgParticle::ParticleSystemUpdater> updater = new osgParticle::ParticleSystemUpdater;
    //updater->addDrawable( ps.get() );

    osg::ref_ptr<osg::Group> root = new osg::Group;
    root->addChild( parent.get() );
    root->addChild( updater.get() );

    // FIXME 2010.9.19: the updater can't be a drawable; otehrwise the ParticleEffect will not work properly. why?
    updater->addParticleSystem( ps.get() );

    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
    geode->addDrawable( ps.get() );
    root->addChild( geode.get() );

    /***
    Start the viewer
    ***/
    osgViewer::Viewer viewer;
    viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
    viewer.addEventHandler( new osgViewer::StatsHandler );
    viewer.addEventHandler( new osgViewer::WindowSizeHandler );
    viewer.setSceneData( root.get() );
    viewer.setCameraManipulator( new osgGA::TrackballManipulator );

    // A floating error of delta-time should be explained here:
    // The particles emitter, program and updater all use a 'dt' to compute the time value in every frame.
    // Because the 'dt' is a double value, it is not suitable to keep three copies of it separately, which
    // is the previous implementation. The small error makes some opeartors unable to work correctly, e.g.
    // the BounceOperator.
    // Now we make use of the getDeltaTime() of ParticleSystem to maintain and dispatch the delta time. But..
    // it is not the best solution so far, since there are still very few particles acting unexpectedly.
    return viewer.run();

    // FIXME 2010.9.19: At present, getDeltaTime() is not used and the implementations in the updater and processors still
    // use a (t - _t0) as the delta time, which is of course causing floating errors. ParticleEffect will not work if we
    // replace the delta time with getDeltaTime()... Need to find a solution.
}
<commit_msg>Removed no longer relevant comment<commit_after>/* OpenSceneGraph example, osgparticleshader.
*
*  Permission is hereby granted, free of charge, to any person obtaining a copy
*  of this software and associated documentation files (the "Software"), to deal
*  in the Software without restriction, including without limitation the rights
*  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
*  copies of the Software, and to permit persons to whom the Software is
*  furnished to do so, subject to the following conditions:
*
*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
*  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
*  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
*  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
*  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
*  THE SOFTWARE.
*/

#include <iostream>

#include <osg/ShapeDrawable>
#include <osg/MatrixTransform>
#include <osg/Point>
#include <osg/PointSprite>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgGA/TrackballManipulator>
#include <osgGA/StateSetManipulator>
#include <osgViewer/ViewerEventHandlers>
#include <osgViewer/Viewer>

#include <osgParticle/ParticleSystem>
#include <osgParticle/ParticleSystemUpdater>
#include <osgParticle/ModularEmitter>
#include <osgParticle/ModularProgram>

#include <osgParticle/AccelOperator>
#include <osgParticle/DampingOperator>
#include <osgParticle/BounceOperator>
#include <osgParticle/SinkOperator>

void createFountainEffect( osgParticle::ModularEmitter* emitter, osgParticle::ModularProgram* program )
{
    // Emit specific number of particles every frame
    osg::ref_ptr<osgParticle::RandomRateCounter> rrc = new osgParticle::RandomRateCounter;
    rrc->setRateRange( 500, 2000 );

    // Accelerate particles in the given gravity direction.
    osg::ref_ptr<osgParticle::AccelOperator> accel = new osgParticle::AccelOperator;
    accel->setToGravity();

    // Multiply each particle's velocity by a damping constant.
    osg::ref_ptr<osgParticle::DampingOperator> damping = new osgParticle::DampingOperator;
    damping->setDamping( 0.9f );

    // Bounce particles off objects defined by one or more domains.
    // Supported domains include triangle, rectangle, plane, disk and sphere.
    // Since a bounce always happens instantaneously, it will not work correctly with unstable delta-time.
    // At present, even the floating error of dt (which are applied to ParticleSystem and Operator separately)
    // causes wrong bounce results. Some one else may have better solutions for this.
    osg::ref_ptr<osgParticle::BounceOperator> bounce = new osgParticle::BounceOperator;
    bounce->setFriction( -0.05 );
    bounce->setResilience( 0.35 );
    bounce->addDiskDomain( osg::Vec3(0.0f, 0.0f, -2.0f), osg::Z_AXIS, 8.0f );
    bounce->addPlaneDomain( osg::Plane(osg::Z_AXIS, 5.0f) );

    // Kill particles going inside/outside of specified domains.
    osg::ref_ptr<osgParticle::SinkOperator> sink = new osgParticle::SinkOperator;
    sink->setSinkStrategy( osgParticle::SinkOperator::SINK_OUTSIDE );
    sink->addSphereDomain( osg::Vec3(), 20.0f );

    emitter->setCounter( rrc.get() );
    program->addOperator( accel.get() );
    program->addOperator( damping.get() );
    program->addOperator( bounce.get() );
    program->addOperator( sink.get() );
}

int main( int argc, char** argv )
{
    osg::ArgumentParser arguments( &argc, argv );

    std::string textureFile("Images/smoke.rgb");
    while ( arguments.read("--texture", textureFile) ) {}

    float pointSize = 20.0f;
    while ( arguments.read("--point", pointSize) ) {}

    double visibilityDistance = -1.0f;
    while ( arguments.read("--visibility", visibilityDistance) ) {}

    bool useShaders = true;
    while ( arguments.read("--disable-shaders") ) { useShaders = false; }

    /***
    Customize particle template and system attributes
    ***/
    osg::ref_ptr<osgParticle::ParticleSystem> ps = new osgParticle::ParticleSystem;

    ps->getDefaultParticleTemplate().setLifeTime( 5.0f );

    // The shader only supports rendering points at present.
    ps->getDefaultParticleTemplate().setShape( osgParticle::Particle::POINT );

    // Set the visibility distance of particles, due to their Z-value in the eye coordinates.
    // Particles that are out of the distance (or behind the eye) will not be rendered.
    ps->setVisibilityDistance( visibilityDistance );

    if ( useShaders )
    {
        // Set using local GLSL shaders to render particles.
        // At present, this is slightly efficient than ordinary methods. The bottlenack here seems to be the cull
        // traversal time. Operators go through the particle list again and again...
        ps->setDefaultAttributesUsingShaders( textureFile, true, 0 );
    }
    else
    {
        ps->setDefaultAttributes( textureFile, true, false, 0 );

        // Without the help of shaders, we have to sort particles to make the visibility distance work. Sorting is
        // also useful in rendering transparent particles in back-to-front order.
        if ( visibilityDistance>0.0 )
            ps->setSortMode( osgParticle::ParticleSystem::SORT_BACK_TO_FRONT );
    }

    // At last, to make the point sprite work, we have to set the points size and the sprite attribute.
    osg::StateSet* stateset = ps->getOrCreateStateSet();
    stateset->setAttribute( new osg::Point(pointSize) );
    stateset->setTextureAttributeAndModes( 0, new osg::PointSprite, osg::StateAttribute::ON );

    /***
    Construct other particle system elements, including the emitter and program
    ***/
    osg::ref_ptr<osgParticle::ModularEmitter> emitter = new osgParticle::ModularEmitter;
    emitter->setParticleSystem( ps.get() );

    osg::ref_ptr<osgParticle::ModularProgram> program = new osgParticle::ModularProgram;
    program->setParticleSystem( ps.get() );

    createFountainEffect( emitter.get(), program.get() );

    /***
    Add the entire particle system to the scene graph
    ***/
    osg::ref_ptr<osg::MatrixTransform> parent = new osg::MatrixTransform;
    parent->addChild( emitter.get() );
    parent->addChild( program.get() );

    // The updater can receive particle systems as child drawables now. The addParticleSystem() method
    // is still usable, with which we should define another geode to contain a particle system.
    osg::ref_ptr<osgParticle::ParticleSystemUpdater> updater = new osgParticle::ParticleSystemUpdater;
    //updater->addDrawable( ps.get() );

    osg::ref_ptr<osg::Group> root = new osg::Group;
    root->addChild( parent.get() );
    root->addChild( updater.get() );

    // FIXME 2010.9.19: the updater can't be a drawable; otehrwise the ParticleEffect will not work properly. why?
    updater->addParticleSystem( ps.get() );

    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
    geode->addDrawable( ps.get() );
    root->addChild( geode.get() );

    /***
    Start the viewer
    ***/
    osgViewer::Viewer viewer;
    viewer.addEventHandler( new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet()) );
    viewer.addEventHandler( new osgViewer::StatsHandler );
    viewer.addEventHandler( new osgViewer::WindowSizeHandler );
    viewer.setSceneData( root.get() );
    viewer.setCameraManipulator( new osgGA::TrackballManipulator );

    // A floating error of delta-time should be explained here:
    // The particles emitter, program and updater all use a 'dt' to compute the time value in every frame.
    // Because the 'dt' is a double value, it is not suitable to keep three copies of it separately, which
    // is the previous implementation. The small error makes some opeartors unable to work correctly, e.g.
    // the BounceOperator.
    // Now we make use of the getDeltaTime() of ParticleSystem to maintain and dispatch the delta time. But..
    // it is not the best solution so far, since there are still very few particles acting unexpectedly.
    return viewer.run();

    // FIXME 2010.9.19: At present, getDeltaTime() is not used and the implementations in the updater and processors still
    // use a (t - _t0) as the delta time, which is of course causing floating errors. ParticleEffect will not work if we
    // replace the delta time with getDeltaTime()... Need to find a solution.
}
<|endoftext|>


<commit_before>/** local **/
#include "logger.hpp"
/** STL **/
#include <sstream>
#include <chrono>
#include <ctime>
using std::chrono::system_clock;

Logger::Logger()
{
  filename = "LOG_" + timestamp() + ".html";
  file.open(filename.c_str());

  file <<"<!DOCTYPE html>\n\n\n"
       <<"  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n"
       <<"  "
       <<"\n\n";
}

void Logger::addEvent(LogEvent e)
{
  file <<"\n
<font color=#"<< adjustColor(e.getColor()) <<">"
       << e.getPrefix() << e.getContent() <<"
";
}

void Logger::addEvent(std::string s)
{
  addEvent(LogEvent(s));
}

void Logger::variable(std::string s, bool b)
{
  std::ostringstream sout;
  sout << s <<" = "<< std::boolalpha << b;
  addEvent(LogEvent("[VARIABLE] " + sout.str()));
}

void Logger::variable(std::string s, int i)
{
  std::ostringstream sout;
  sout << s <<" = "<< i;
  addEvent(LogEvent("[VARIABLE] " + sout.str()));
}

void Logger::variable(std::string s, double d)
{
  std::ostringstream sout;
  sout << s <<" = "<< d;
  addEvent(LogEvent("[VARIABLE] " + sout.str()));
}

void Logger::dump(const std::map<std::string, std::string>& data, std::string title, bool showQuotes)
{
  std::string quote = showQuotes ? "\"" : "";

  std::string prefix = "[DUMP] " + title + "
\n";
  std::string content = "
\n
Key	Value
\n";

  for(auto it = data.begin(); it != data.end(); ++it)
    content += "
" + quote + it->first + quote + "	" + quote + it->second + quote + "
\n";

  content += "
\n  ";

  addEvent(LogEvent(prefix, 0x000000, content));
}

void Logger::separator()
{
  file <<"\n
";
}

void Logger::close()
{
  file <<"\n\n"<< std::endl;
  file.close();
}

std::string Logger::adjustColor(int color)
{
  std::ostringstream cstr;
  cstr << std::hex << color;
  std::string col = cstr.str();

  while(col.size() < 6) // insert zeros so the final string will look like "0000FF" instead of "FF"
    col.insert(0, "0");

  return col;
}

std::string Logger::timestamp()
{
  // convert time
  time_t now = system_clock::to_time_t(system_clock::now());
  std::ostringstream sout;
  sout << ctime(&now);

  std::string time = sout.str();

  // strip colons
  size_t pos = time.find(":");
  while(pos != std::string::npos)
  {
    time.replace(pos, 1, "_");
    pos = time.find(":");
  }

  // strip spaces
  pos = time.find(" ");
  while(pos != std::string::npos)
  {
    time.replace(pos, 1, "-");
    pos = time.find(" ");
  }

  return time.substr(0, time.size() - 1); // strip '\n' character
}
<commit_msg>Update logger.cpp<commit_after>/** local **/
#include "logger.hpp"
/** STL **/
#include <sstream>
#include <chrono>
#include <ctime>
using std::chrono::system_clock;

Logger::Logger()
{
  filename = "LOG_" + timestamp() + ".html";
  file.open(filename.c_str());

  file <<"<!DOCTYPE html>\n\n\n"
       <<"  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n"
       <<"  "
       <<"\n\n";
}

void Logger::addEvent(LogEvent e)
{
  file <<"\n
<font color=#"<< adjustColor(e.getColor()) <<">"
       << e.getPrefix() << e.getContent() <<"
";
}

void Logger::dump(const std::map<std::string, std::string>& data, std::string title, bool showQuotes)
{
  std::string quote = showQuotes ? "\"" : "";

  std::string prefix = "[DUMP] " + title + "
\n";
  std::string content = "
\n
Key	Value
\n";

  for(auto it = data.begin(); it != data.end(); ++it)
    content += "
" + quote + it->first + quote + "	" + quote + it->second + quote + "
\n";

  content += "
\n  ";

  addEvent(LogEvent(prefix, 0x000000, content));
}

void Logger::close()
{
  file <<"\n\n"<< std::endl;
  file.close();
}

std::string Logger::adjustColor(int color)
{
  std::ostringstream cstr;
  cstr << std::hex << color;
  std::string col = cstr.str();

  while(col.size() < 6) // insert zeros so the final string will look like "0000FF" instead of "FF"
    col.insert(0, "0");

  return col;
}

std::string Logger::timestamp()
{
  // convert time
  time_t now = system_clock::to_time_t(system_clock::now());
  std::ostringstream sout;
  sout << ctime(&now);

  std::string time = sout.str();

  // strip colons
  size_t pos = time.find(":");
  while(pos != std::string::npos)
  {
    time.replace(pos, 1, "_");
    pos = time.find(":");
  }

  // strip spaces
  pos = time.find(" ");
  while(pos != std::string::npos)
  {
    time.replace(pos, 1, "-");
    pos = time.find(" ");
  }

  return time.substr(0, time.size() - 1); // strip '\n' character
}
<|endoftext|>


<commit_before>#include "susi/DuktapeEngine.h"

Susi::Duktape::JSEngine *Susi::Duktape::enginePtr;

void Susi::Duktape::JSEngine::start() {
    Susi::Duktape::enginePtr = this;

    //keep the asio event loop running;
    _kickoffKeepAlive();
    _runloop = std::move(std::thread{[this](){
      _ioservice.run();
    }});

    init();

    if (_script != "") {
        if (duk_peval_file(ctx, _script.c_str()) != 0) {
            std::cerr << "processing usersource: " << duk_safe_to_string(ctx, -1) << std::endl;
        }
    }

    std::cout << "started Susi::duktape engine and loaded " << _script <<std::endl;
}

void Susi::Duktape::JSEngine::init() {
    ctx = duk_create_heap_default();
    if (!ctx) {
        printf("Failed to create a Susi::Duktape heap.\n");
        exit(1);
    }
    duk_push_global_object(ctx);
    duk_push_c_function(ctx, js_registerConsumer, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "_registerConsumer");
    duk_push_c_function(ctx, js_registerProcessor, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "_registerProcessor");
    duk_push_c_function(ctx, js_unregisterProcessor, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "_unregisterProcessor");
    duk_push_c_function(ctx, js_unregisterConsumer, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "_unregisterConsumer");
    duk_push_c_function(ctx, js_publish, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "_publish");
    duk_push_c_function(ctx, js_ack, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "_ack");
    duk_push_c_function(ctx, js_dismiss, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "_dismiss");
    duk_push_c_function(ctx, js_log, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "log");
    duk_push_c_function(ctx, js_setTimeout, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "_setTimeout");

    if (duk_peval_lstring(ctx, Susi::Duktape::susiJS.c_str(), Susi::Duktape::susiJS.size()) != 0) {
        std::cerr << "processing susiJS: " << duk_safe_to_string(ctx, -1) << std::endl;
    }
}

void Susi::Duktape::JSEngine::stop() {
    //unsubscribeAll();
}

duk_ret_t Susi::Duktape::JSEngine::js_registerConsumer(duk_context *ctx) {
    size_t sz;
    const char *val = duk_require_lstring(ctx, 0, &sz);
    enginePtr->registerConsumer(val);
    duk_push_true(ctx);
    return 1;
}

duk_ret_t Susi::Duktape::JSEngine::js_registerProcessor(duk_context *ctx) {
    size_t sz;
    const char *val = duk_require_lstring(ctx, 0, &sz);
    enginePtr->registerProcessor(val);
    duk_push_true(ctx);
    return 1;
}

duk_ret_t Susi::Duktape::JSEngine::js_publish(duk_context *ctx) {
    size_t sz;
    const char *val = duk_require_lstring(ctx, 0, &sz);
    try {
        enginePtr->publish(val);
    } catch (const std::exception & e) {
        std::cerr << "publish failed: " << e.what() << std::endl;
        duk_push_false(ctx);
        return 1;
    }
    duk_push_true(ctx);
    return 1;
}

duk_ret_t Susi::Duktape::JSEngine::js_ack(duk_context *ctx) {
    size_t sz;
    const char *val = duk_require_lstring(ctx, 0, &sz);
    if (val != nullptr) {
        enginePtr->ack(val);
        duk_push_true(ctx);
    } else {
        duk_push_false(ctx);
    }
    return 1;
}

duk_ret_t Susi::Duktape::JSEngine::js_dismiss(duk_context *ctx) {
    size_t sz;
    const char *val = duk_require_lstring(ctx, 0, &sz);
    if (val != nullptr) {
        enginePtr->dismiss(val);
        duk_push_true(ctx);
    } else {
        duk_push_false(ctx);
    }
    return 1;
}

duk_ret_t Susi::Duktape::JSEngine::js_log(duk_context *ctx) {
    size_t sz;
    const char *val = duk_require_lstring(ctx, 0, &sz);
    std::cout << val << std::endl;
    duk_push_true(ctx);
    return 1;
}

duk_ret_t Susi::Duktape::JSEngine::js_setTimeout(duk_context *ctx) {
    int arg = duk_require_int(ctx, 0 /*index*/);
    auto timeout = std::make_shared<boost::asio::deadline_timer>(enginePtr->_ioservice, boost::posix_time::milliseconds(arg));
    timeout->async_wait([timeout,ctx](const boost::system::error_code&){
        std::lock_guard<std::mutex> lock{enginePtr->mutex};
        duk_push_global_object(ctx);
        duk_get_prop_string(ctx, -1 /*index*/, "_checkTimeouts");
        if (duk_pcall(ctx, 0 /*nargs*/) != 0) {
            std::string err = duk_safe_to_string(ctx, -1);
            std::cerr << err << std::endl;
            throw std::runtime_error{err};
        }
        duk_pop(ctx);  /* pop result/error */
    });
    return 1;
}

duk_ret_t Susi::Duktape::JSEngine::js_unregisterConsumer(duk_context *ctx) {
    size_t sz;
    const char *topic = duk_require_lstring(ctx, 0, &sz);
    enginePtr->unregisterConsumer(topic);
    duk_push_true(ctx);
    return 1;
}

duk_ret_t Susi::Duktape::JSEngine::js_unregisterProcessor(duk_context *ctx) {
    size_t sz;
    const char *topic = duk_require_lstring(ctx, 0, &sz);
    enginePtr->unregisterProcessor(topic);
    duk_push_true(ctx);
    return 1;
}

void Susi::Duktape::JSEngine::registerProcessor(std::string topic) {
    registerIdsProcessor[topic] = _client.registerProcessor(topic, [this, topic](Susi::EventPtr event) {
        const std::string eventID = event->id;
        try {
            std::lock_guard<std::mutex> lock{mutex};
            auto eventString = event->toString();
            pendingEvents[eventID] = std::move(event);
            duk_push_global_object(ctx);
            duk_get_prop_string(ctx, -1 /*index*/, "_processProcessorEvent");
            duk_push_string(ctx, eventString.c_str());
            duk_push_string(ctx, topic.c_str());
            if (duk_pcall(ctx, 2 /*nargs*/) != 0) {
                std::string err = duk_safe_to_string(ctx, -1);
                std::cerr << err << std::endl;
                throw std::runtime_error{err};
            }
            duk_pop(ctx);  /* pop result/error */
        } catch (const std::exception & e) {
            std::string err = e.what();
            auto & pendingEvent = pendingEvents.at(eventID);
            pendingEvent->headers.push_back({"error", err});
            pendingEvents.erase(eventID);
        }
    });
}

void Susi::Duktape::JSEngine::unregisterProcessor(std::string topic) {
    _client.unregisterProcessor(registerIdsProcessor[topic]);
    registerIdsProcessor.erase(topic);
}

void Susi::Duktape::JSEngine::unregisterConsumer(std::string topic) {
    _client.unregisterConsumer(registerIdsConsumer[topic]);
    registerIdsConsumer.erase(topic);
}

void Susi::Duktape::JSEngine::registerConsumer(std::string topic) {
    Susi::Consumer consumer = [this, topic](Susi::SharedEventPtr event) {
        std::lock_guard<std::mutex> lock{mutex};
        duk_push_global_object(ctx);
        duk_get_prop_string(ctx, -1 /*index*/, "_processConsumerEvent");
        duk_push_string(ctx, event->toString().c_str());
        duk_push_string(ctx, topic.c_str());
        if (duk_pcall(ctx, 2 /*nargs*/) != 0) {
            std::cerr << (std::string{"Error: "} +duk_safe_to_string(ctx, -1)) << std::endl;
        }
        duk_pop(ctx);  /* pop result/error */
    };
    registerIdsConsumer[topic] = _client.registerConsumer(topic, consumer);
}

void Susi::Duktape::JSEngine::publish(std::string eventData) {
    auto any = BSON::Value::fromJSON(eventData);
    Susi::Event rawEvent{any};
    auto event = _client.createEvent(rawEvent.topic);
    *event = rawEvent;
    _client.publish(std::move(event), [this](Susi::SharedEventPtr event) {
        std::lock_guard<std::mutex> lock{mutex};
        duk_push_global_object(ctx);
        duk_get_prop_string(ctx, -1 /*index*/, "_processAck");
        duk_push_string(ctx, event->toString().c_str());
        if (duk_pcall(ctx, 1 /*nargs*/) != 0) {
            std::cerr << (std::string{"Error: "} +duk_safe_to_string(ctx, -1)) << std::endl;
        }
        duk_pop(ctx);  /* pop result/error */
    });
}

void Susi::Duktape::JSEngine::ack(std::string eventData) {
    auto any = BSON::Value::fromJSON(eventData);
    Susi::Event rawEvent{any};
    try {
        auto & pendingEvent = pendingEvents.at(rawEvent.id);
        *pendingEvent = rawEvent;
        _client.ack(std::move(pendingEvent));
        pendingEvents.erase(rawEvent.id);
    } catch (...) {}
}

void Susi::Duktape::JSEngine::dismiss(std::string eventData) {
    auto any = BSON::Value::fromJSON(eventData);
    Susi::Event rawEvent{any};
    try {
        auto & pendingEvent = pendingEvents.at(rawEvent.id);
        *pendingEvent = rawEvent;
        _client.dismiss(std::move(pendingEvent));
        pendingEvents.erase(rawEvent.id);
    } catch (...) {}
}
<commit_msg>[jsengine] make setTimeout(fn,0) work as toplevel command (it segfaulted if evaluation of source was not yet ready);<commit_after>#include "susi/DuktapeEngine.h"

Susi::Duktape::JSEngine *Susi::Duktape::enginePtr;

void Susi::Duktape::JSEngine::start() {
    Susi::Duktape::enginePtr = this;

    //keep the asio event loop running;
    _kickoffKeepAlive();
    _runloop = std::move(std::thread{[this](){
      _ioservice.run();
    }});

    init();

    if (_script != "") {
        std::lock_guard<std::mutex> lock{enginePtr->mutex};
        if (duk_peval_file(ctx, _script.c_str()) != 0) {
            std::cerr << "processing usersource: " << duk_safe_to_string(ctx, -1) << std::endl;
        }
    }

    std::cout << "started Susi::duktape engine and loaded " << _script <<std::endl;
}

void Susi::Duktape::JSEngine::init() {
    ctx = duk_create_heap_default();
    if (!ctx) {
        printf("Failed to create a Susi::Duktape heap.\n");
        exit(1);
    }
    duk_push_global_object(ctx);
    duk_push_c_function(ctx, js_registerConsumer, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "_registerConsumer");
    duk_push_c_function(ctx, js_registerProcessor, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "_registerProcessor");
    duk_push_c_function(ctx, js_unregisterProcessor, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "_unregisterProcessor");
    duk_push_c_function(ctx, js_unregisterConsumer, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "_unregisterConsumer");
    duk_push_c_function(ctx, js_publish, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "_publish");
    duk_push_c_function(ctx, js_ack, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "_ack");
    duk_push_c_function(ctx, js_dismiss, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "_dismiss");
    duk_push_c_function(ctx, js_log, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "log");
    duk_push_c_function(ctx, js_setTimeout, 1 /*nargs*/);
    duk_put_prop_string(ctx, -2, "_setTimeout");

    if (duk_peval_lstring(ctx, Susi::Duktape::susiJS.c_str(), Susi::Duktape::susiJS.size()) != 0) {
        std::cerr << "processing susiJS: " << duk_safe_to_string(ctx, -1) << std::endl;
    }
}

void Susi::Duktape::JSEngine::stop() {
    //unsubscribeAll();
}

duk_ret_t Susi::Duktape::JSEngine::js_registerConsumer(duk_context *ctx) {
    size_t sz;
    const char *val = duk_require_lstring(ctx, 0, &sz);
    enginePtr->registerConsumer(val);
    duk_push_true(ctx);
    return 1;
}

duk_ret_t Susi::Duktape::JSEngine::js_registerProcessor(duk_context *ctx) {
    size_t sz;
    const char *val = duk_require_lstring(ctx, 0, &sz);
    enginePtr->registerProcessor(val);
    duk_push_true(ctx);
    return 1;
}

duk_ret_t Susi::Duktape::JSEngine::js_publish(duk_context *ctx) {
    size_t sz;
    const char *val = duk_require_lstring(ctx, 0, &sz);
    try {
        enginePtr->publish(val);
    } catch (const std::exception & e) {
        std::cerr << "publish failed: " << e.what() << std::endl;
        duk_push_false(ctx);
        return 1;
    }
    duk_push_true(ctx);
    return 1;
}

duk_ret_t Susi::Duktape::JSEngine::js_ack(duk_context *ctx) {
    size_t sz;
    const char *val = duk_require_lstring(ctx, 0, &sz);
    if (val != nullptr) {
        enginePtr->ack(val);
        duk_push_true(ctx);
    } else {
        duk_push_false(ctx);
    }
    return 1;
}

duk_ret_t Susi::Duktape::JSEngine::js_dismiss(duk_context *ctx) {
    size_t sz;
    const char *val = duk_require_lstring(ctx, 0, &sz);
    if (val != nullptr) {
        enginePtr->dismiss(val);
        duk_push_true(ctx);
    } else {
        duk_push_false(ctx);
    }
    return 1;
}

duk_ret_t Susi::Duktape::JSEngine::js_log(duk_context *ctx) {
    size_t sz;
    const char *val = duk_require_lstring(ctx, 0, &sz);
    std::cout << val << std::endl;
    duk_push_true(ctx);
    return 1;
}

duk_ret_t Susi::Duktape::JSEngine::js_setTimeout(duk_context *ctx) {
    int arg = duk_require_int(ctx, 0 /*index*/);
    auto timeout = std::make_shared<boost::asio::deadline_timer>(enginePtr->_ioservice, boost::posix_time::milliseconds(arg));
    timeout->async_wait([timeout,ctx](const boost::system::error_code&){
        std::lock_guard<std::mutex> lock{enginePtr->mutex};
        duk_push_global_object(ctx);
        duk_get_prop_string(ctx, -1 /*index*/, "_checkTimeouts");
        if (duk_pcall(ctx, 0 /*nargs*/) != 0) {
            std::string err = duk_safe_to_string(ctx, -1);
            std::cerr << err << std::endl;
            throw std::runtime_error{err};
        }
        duk_pop(ctx);  /* pop result/error */
    });
    return 1;
}

duk_ret_t Susi::Duktape::JSEngine::js_unregisterConsumer(duk_context *ctx) {
    size_t sz;
    const char *topic = duk_require_lstring(ctx, 0, &sz);
    enginePtr->unregisterConsumer(topic);
    duk_push_true(ctx);
    return 1;
}

duk_ret_t Susi::Duktape::JSEngine::js_unregisterProcessor(duk_context *ctx) {
    size_t sz;
    const char *topic = duk_require_lstring(ctx, 0, &sz);
    enginePtr->unregisterProcessor(topic);
    duk_push_true(ctx);
    return 1;
}

void Susi::Duktape::JSEngine::registerProcessor(std::string topic) {
    registerIdsProcessor[topic] = _client.registerProcessor(topic, [this, topic](Susi::EventPtr event) {
        const std::string eventID = event->id;
        try {
            std::lock_guard<std::mutex> lock{mutex};
            auto eventString = event->toString();
            pendingEvents[eventID] = std::move(event);
            duk_push_global_object(ctx);
            duk_get_prop_string(ctx, -1 /*index*/, "_processProcessorEvent");
            duk_push_string(ctx, eventString.c_str());
            duk_push_string(ctx, topic.c_str());
            if (duk_pcall(ctx, 2 /*nargs*/) != 0) {
                std::string err = duk_safe_to_string(ctx, -1);
                std::cerr << err << std::endl;
                throw std::runtime_error{err};
            }
            duk_pop(ctx);  /* pop result/error */
        } catch (const std::exception & e) {
            std::string err = e.what();
            auto & pendingEvent = pendingEvents.at(eventID);
            pendingEvent->headers.push_back({"error", err});
            pendingEvents.erase(eventID);
        }
    });
}

void Susi::Duktape::JSEngine::unregisterProcessor(std::string topic) {
    _client.unregisterProcessor(registerIdsProcessor[topic]);
    registerIdsProcessor.erase(topic);
}

void Susi::Duktape::JSEngine::unregisterConsumer(std::string topic) {
    _client.unregisterConsumer(registerIdsConsumer[topic]);
    registerIdsConsumer.erase(topic);
}

void Susi::Duktape::JSEngine::registerConsumer(std::string topic) {
    Susi::Consumer consumer = [this, topic](Susi::SharedEventPtr event) {
        std::lock_guard<std::mutex> lock{mutex};
        duk_push_global_object(ctx);
        duk_get_prop_string(ctx, -1 /*index*/, "_processConsumerEvent");
        duk_push_string(ctx, event->toString().c_str());
        duk_push_string(ctx, topic.c_str());
        if (duk_pcall(ctx, 2 /*nargs*/) != 0) {
            std::cerr << (std::string{"Error: "} +duk_safe_to_string(ctx, -1)) << std::endl;
        }
        duk_pop(ctx);  /* pop result/error */
    };
    registerIdsConsumer[topic] = _client.registerConsumer(topic, consumer);
}

void Susi::Duktape::JSEngine::publish(std::string eventData) {
    auto any = BSON::Value::fromJSON(eventData);
    Susi::Event rawEvent{any};
    auto event = _client.createEvent(rawEvent.topic);
    *event = rawEvent;
    _client.publish(std::move(event), [this](Susi::SharedEventPtr event) {
        std::lock_guard<std::mutex> lock{mutex};
        duk_push_global_object(ctx);
        duk_get_prop_string(ctx, -1 /*index*/, "_processAck");
        duk_push_string(ctx, event->toString().c_str());
        if (duk_pcall(ctx, 1 /*nargs*/) != 0) {
            std::cerr << (std::string{"Error: "} +duk_safe_to_string(ctx, -1)) << std::endl;
        }
        duk_pop(ctx);  /* pop result/error */
    });
}

void Susi::Duktape::JSEngine::ack(std::string eventData) {
    auto any = BSON::Value::fromJSON(eventData);
    Susi::Event rawEvent{any};
    try {
        auto & pendingEvent = pendingEvents.at(rawEvent.id);
        *pendingEvent = rawEvent;
        _client.ack(std::move(pendingEvent));
        pendingEvents.erase(rawEvent.id);
    } catch (...) {}
}

void Susi::Duktape::JSEngine::dismiss(std::string eventData) {
    auto any = BSON::Value::fromJSON(eventData);
    Susi::Event rawEvent{any};
    try {
        auto & pendingEvent = pendingEvents.at(rawEvent.id);
        *pendingEvent = rawEvent;
        _client.dismiss(std::move(pendingEvent));
        pendingEvents.erase(rawEvent.id);
    } catch (...) {}
}
<|endoftext|>