//  Copyright 2020 John Buonagurio
//
//  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

#pragma once

#include <xll/config.hpp>

#include <xll/detail/assert.hpp>
#include <xll/detail/memory.hpp>

#include <algorithm>
#include <iterator>
#include <limits>
#include <memory>
#include <type_traits>

#include <boost/container/vector.hpp>
#include <boost/numeric/ublas/storage.hpp>

namespace xll {
namespace detail {

// Boost.uBLAS Storage concept using a fixed size buffer and configurable
// stored size type (SST). Used with FP12 arrays.

template<class T, std::size_t N, class SST = std::size_t>
struct static_array
    : public boost::numeric::ublas::storage_array<static_array<T, N, SST>>
{
    static_assert(N <= std::numeric_limits<SST>::max());

    using value_type = T;
    using size_type = SST;
    using difference_type = std::make_signed_t<SST>;
    using const_reference = const T&;
    using reference = T&;
    using const_pointer = const T*;
    using pointer = T*;
    using const_iterator = const_pointer;
    using iterator = pointer;
    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
    using reverse_iterator = std::reverse_iterator<iterator>;

public:
    inline static_array() noexcept
    {}

    explicit inline static_array(size_type size)
    {
        (void)size;
        XLL_ASSERT(size == N);
        std::uninitialized_value_construct(begin(), end());
    }

    inline static_array(size_type size, value_type init)
    {
        XLL_ASSERT(size == N);
        std::uninitialized_fill(begin(), end(), init);
    }

    inline static_array(const static_array& rhs)
    {
        std::uninitialized_copy(rhs.begin(), rhs.end(), begin());
    }

    inline static_array(static_array&& rhs)
        noexcept(std::is_nothrow_move_constructible_v<value_type>)
    {
        std::uninitialized_move(rhs.begin(), rhs.end(), begin());
    }

    inline size_type size() const
    {
        return static_cast<size_type>(N);
    }

    inline size_type max_size() const
    {
        return static_cast<size_type>(N);
    }

    inline const_reference operator[](size_type i) const
    {
        XLL_ASSERT(i < N);
        return begin()[i];
    }

    inline reference operator[](size_type i)
    {
        XLL_ASSERT(i < N);
        return begin()[i];
    }

    inline static_array& operator=(const static_array& rhs)
    {
        if (this != &rhs) {
            std::uninitialized_copy(rhs.begin(), rhs.end(), begin());
        }
        return *this;
    }

    inline static_array& operator=(static_array&& rhs)
    {
        if (this != &rhs) {
            std::uninitialized_move(rhs.begin(), rhs.end(), begin());
        }
        return *this;
    }

    inline static_array& assign_temporary(static_array& a)
    {
        *this = a;
        return *this;
    }

    inline void swap(static_array& rhs)
    {
        if (this != &rhs) {
            std::swap_ranges(begin(), begin() + N, rhs.begin());
        }
    }

    friend void swap(static_array &a1, static_array &a2) {
        a1.swap(a2);
    }

    inline const_iterator begin() const
    {
        return reinterpret_cast<const_iterator>(&data_);
    }

    inline const_iterator cbegin() const
    {
        return reinterpret_cast<const_iterator>(&data_);
    }

    inline const_iterator end() const
    {
        return reinterpret_cast<const_iterator>(&data_) + N;
    }

    inline const_iterator cend() const
    {
        return reinterpret_cast<const_iterator>(&data_) + N;
    }

    inline iterator begin()
    {
        return reinterpret_cast<iterator>(&data_);
    }

    inline iterator end()
    {
        return reinterpret_cast<iterator>(&data_) + N;
    }

    inline const_reverse_iterator rbegin() const
    {
        return const_reverse_iterator(end());
    }

    inline const_reverse_iterator crbegin() const
    {
        return const_reverse_iterator(end());
    }

    inline const_reverse_iterator rend() const
    {
        return const_reverse_iterator(begin());
    }

    inline const_reverse_iterator crend() const
    {
        return const_reverse_iterator(begin());
    }

    inline reverse_iterator rbegin()
    {
        return reverse_iterator(end());
    }

    inline reverse_iterator rend()
    {
        return reverse_iterator(begin());
    }

private:
    std::aligned_storage_t<sizeof(T)*N, alignof(T)> data_;
};

} // namespace detail
} // namespace xll


///////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2008-2012  Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>
//
//  See accompanying file COPYING.TXT file for licensing details.
//
///////////////////////////////////////////////////////////////////////////////
#define CPPCMS_SOURCE
#include <cppcms/session_cookies.h>
#include <cppcms/cppcms_error.h>
#include <cppcms/session_interface.h>
#include <cppcms/base64.h>
#include <booster/log.h>

#include <time.h>
#include <string.h>

namespace cppcms {
namespace sessions {

using namespace std;

struct session_cookies::_data {};

session_cookies::session_cookies(std::auto_ptr<encryptor> enc) :
	encryptor_(enc)
{
}
session_cookies::~session_cookies()
{
}

void session_cookies::save(session_interface &session,string const &data,time_t timeout,bool /*not_used*/,bool on_server)
{
	if(on_server)
		throw cppcms_error("Can't use cookies backend when data should be stored on server");
	std::string real_data;
	real_data.reserve(data.size() + sizeof(timeout));
	real_data.append(reinterpret_cast<char *>(&timeout),sizeof(timeout));
	real_data+=data;
	std::string cipher = encryptor_->encrypt(real_data);
	string cdata="C" + b64url::encode(cipher);
	session.set_session_cookie(cdata);
}

bool session_cookies::load(session_interface &session,string &data,time_t &timeout_out)
{
	string cdata=session.get_session_cookie();
	if(cdata.empty())
		return false;
	if(cdata[0]!='C') {
		session.clear_session_cookie();
		return false;
	}
	std::string cipher;
	if(!b64url::decode(cdata.substr(1),cipher)) {
		session.clear_session_cookie();
		return false;
	}
	string tmp;
	if(!encryptor_->decrypt(cipher,tmp)) {
		BOOSTER_WARNING("cppcms") << "User supplied session cookie is not valid";
		session.clear_session_cookie();
		return false;
	}
	if(tmp.size() < sizeof(time_t)) {
		session.clear_session_cookie();
		return false;
	}

	time_t timeout;
	memcpy(&timeout,tmp.data(),sizeof(time_t));
	if(timeout < time(0)) {
		session.clear_session_cookie();
		return false;
	}
	data = tmp.substr(sizeof(time_t));
	timeout_out=timeout;
	return true;
}

void session_cookies::clear(session_interface &session)
{
	session.clear_session_cookie();
}

bool session_cookies::is_blocking()
{
	return false;
}

} // sessions
} // cppcms


#include <boost/icl/type_traits/is_numeric.hpp>


/*
    This file is part of Nori, a simple educational ray tracer

    Copyright (c) 2012 by Wenzel Jakob and Steve Marschner.

    Nori is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License Version 3
    as published by the Free Software Foundation.

    Nori 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, see <http://www.gnu.org/licenses/>.
*/

#include <nori/kdtree.h>
#include <Eigen/Geometry>

NORI_NAMESPACE_BEGIN

KDTree::KDTree() : m_primitiveCount(0) {
	m_sizeMap.push_back(0);
}

KDTree::~KDTree() {
	for (size_t i=0; i<m_meshes.size(); ++i)
		delete m_meshes[i];
}

void KDTree::build() {
	SizeType primCount = getPrimitiveCount();
	cout << "Constructing a SAH kd-tree (" << primCount << " triangles, "
		 << getCoreCount() << " threads) .." << endl;
	Parent::buildInternal();
}

void KDTree::addMesh(Mesh *mesh) {
	m_primitiveCount += mesh->getTriangleCount();
	m_meshes.push_back(mesh);
	m_sizeMap.push_back(m_sizeMap.back() + mesh->getTriangleCount());
}

bool KDTree::rayIntersect(const Ray3f &ray, Intersection &its, bool shadowRay) const {
	/// KD-tree traversal stack
	struct {
		/* Pointer to the far child */
		const KDNode * __restrict node;
		/* Distance traveled along the ray (entry or exit) */
		float t;
		/* Previous stack item */
		uint32_t prev;
		/* Associated point */
		Point3f p;
	} stack[NORI_KD_MAXDEPTH];

	its.t = std::numeric_limits<float>::infinity();

	/* Use an adaptive ray epsilon */
	float mint = ray.mint, maxt = ray.maxt;
	if (mint == Epsilon)
		mint = std::max(mint, mint * ray.o.array().abs().maxCoeff());

	float bboxMinT, bboxMaxT;
	if (!m_bbox.rayIntersect(ray, bboxMinT, bboxMaxT))
		return false;

	mint = std::max(mint, bboxMinT);
	maxt = std::min(maxt, bboxMaxT);

	if (maxt < mint)
		return false;

	/* Set up the entry point */
	uint32_t enPt = 0;
	stack[enPt].t = mint;
	stack[enPt].p = ray(mint);

	/* Set up the exit point */
	uint32_t exPt = 1;
	stack[exPt].t = maxt;
	stack[exPt].p = ray(maxt);
	stack[exPt].node = NULL;

	bool foundIntersection = false;
	uint32_t foundPrimIndex = 0;
	const KDNode * __restrict currNode = m_nodes;
	while (currNode != NULL) {
		while (EXPECT_TAKEN(!currNode->isLeaf())) {
			const float splitVal = (float) currNode->getSplit();
			const int axis = currNode->getAxis();
			const KDNode * __restrict farChild;

			if (stack[enPt].p[axis] <= splitVal) {
				if (stack[exPt].p[axis] <= splitVal) {
					/* Cases N1, N2, N3, P5, Z2 and Z3 (see thesis) */
					currNode = currNode->getLeft();
					continue;
				}

				/* Typo in Havran's thesis:
				   (it specifies "stack[exPt].p == splitVal", which
				    is clearly incorrect) */
				if (stack[enPt].p[axis] == splitVal) {
					/* Case Z1 */
					currNode = currNode->getRight();
					continue;
				}

				/* Case N4 */
				currNode = currNode->getLeft();
				farChild = currNode + 1; // getRight()
			} else { /* stack[enPt].p[axis] > splitVal */
				if (splitVal < stack[exPt].p[axis]) {
					/* Cases P1, P2, P3 and N5 */
					currNode = currNode->getRight();
					continue;
				}
				/* Case P4 */
				farChild = currNode->getLeft();
				currNode = farChild + 1; // getRight()
			}

			/* Cases P4 and N4 -- calculate the distance to the split plane */
			float distToSplit = (splitVal - ray.o[axis]) * ray.dRcp[axis];

			/* Set up a new exit point */
			const uint32_t tmp = exPt++;
			if (exPt == enPt) /* Do not overwrite the entry point */
				++exPt;

			stack[exPt].prev = tmp;
			stack[exPt].t = distToSplit;
			stack[exPt].node = farChild;
			stack[exPt].p = ray(distToSplit);
			stack[exPt].p[axis] = splitVal;
		}

		/* Reached a leaf node */
		for (IndexType entry=currNode->getPrimStart(),
				last = currNode->getPrimEnd(); entry != last; entry++) {
			IndexType primIndex = m_indices[entry];
			IndexType meshIndex = findMesh(primIndex);
			const Mesh *mesh = m_meshes[meshIndex];

			float u, v, t;
			bool success = mesh->rayIntersect(primIndex, ray, u, v, t);

			if (success && t >= mint && t <= maxt) {
				if (shadowRay)
					return true;
				maxt = t;
				its.t = t;
				its.uv = Point2f(u, v);
				its.mesh = mesh;
				foundPrimIndex = primIndex;
				foundIntersection = true;
			}
		}

		if (stack[exPt].t > maxt)
			break;

		/* Pop from the stack and advance to the next node on the interval */
		enPt = exPt;
		currNode = stack[exPt].node;
		exPt = stack[enPt].prev;
	}

	if (foundIntersection && !shadowRay) {
		/* Find the barycentric coordinates */
		Vector3f bary;
		bary << 1-its.uv.sum(), its.uv;

		/* Look up the vertex indices */
		const Mesh *mesh = its.mesh;
		const uint32_t *indices = mesh->getIndices(),
				  idx0 = indices[3*foundPrimIndex+0],
				  idx1 = indices[3*foundPrimIndex+1],
				  idx2 = indices[3*foundPrimIndex+2];

		const Point3f  *positions = mesh->getVertexPositions();
		const Normal3f *normals   = mesh->getVertexNormals();
		const Point2f  *texCoords = mesh->getVertexTexCoords();

		Point3f p0 = positions[idx0],
			p1 = positions[idx1],
			p2 = positions[idx2];

		/* Compute the intersection positon accurately
		   using barycentric coordinates */
		its.p = bary.x() * p0 + bary.y() * p1 + bary.z() * p2;

		/* Compute proper texture coordinates if provided by the mesh */
		if (texCoords)
			its.uv = bary.x() * texCoords[idx0] +
				bary.y() * texCoords[idx1] +
				bary.z() * texCoords[idx2];

		/* Compute the geometry frame */
		its.geoFrame = Frame((p1-p0).cross(p2-p0).normalized());

		if (normals) {
			/* Compute the shading frame. Note that for simplicity,
			   the current implementation doesn't attempt to provide
			   tangents that are continuous across the surface. That
			   means that this code will need to be modified to be able
			   use anisotropic BRDFs, which need tangent continuity */

			its.shFrame = Frame(
				(bary.x() * normals[idx0] +
				 bary.y() * normals[idx1] +
				 bary.z() * normals[idx2]).normalized());
		} else {
			its.shFrame = its.geoFrame;
		}
	}

	return foundIntersection;
}

NORI_NAMESPACE_END


#include <boost/units/base_units/angle/radian.hpp>


/**
 * Copyright (c) 2015 Carnegie Mellon University, Daniel Maturana <dimatura@cmu.edu>
 *
 * For License information please see the LICENSE file in the root directory.
 *
 */

#ifndef GRID_TYPES_HPP_HJ46RUAT
#define GRID_TYPES_HPP_HJ46RUAT

#include <stdint.h>

#include <Eigen/Core>

namespace ca
{

// a compact bitwise representation of ijk coordinates
typedef uint64_t hash_ix_t;
typedef Eigen::Matrix<hash_ix_t, Eigen::Dynamic, 1> HashIxVector;

// indexes dense grid arrays in ram. we use signed to avoid arithmetic bugs.
typedef int64_t mem_ix_t;
typedef Eigen::Matrix<mem_ix_t, Eigen::Dynamic, 1> MemIxVector;

typedef int32_t grid_ix_t;
typedef Eigen::Matrix<grid_ix_t, 2, 1> Vec2Ix;
typedef Eigen::Matrix<grid_ix_t, 3, 1> Vec3Ix;
typedef Eigen::Matrix<grid_ix_t, 4, 1> Vec4Ix;
// note that we are using an eigen-friendly representation
typedef Eigen::Matrix<grid_ix_t, 2, Eigen::Dynamic> Mat2Ix;
typedef Eigen::Matrix<grid_ix_t, 3, Eigen::Dynamic> Mat3Ix;
typedef Eigen::Matrix<grid_ix_t, 4, Eigen::Dynamic> Mat4Ix;

} /* ca */

#endif /* end of include guard: GRID_TYPES_HPP_HJ46RUAT */


// This will be an interface for PCMRI service and blueberry extension point etc.

/**
* This is similar to the 3D reconstruction but also over time. Time is treated as just another dimension,
* however it is imposed periodic. This is achieved by extending input data up to 2 b-spline grid points on
* each side along the time direction (or is it just 1?). Also, temporal resolution is also increased over
* the multiple scales is not divided by 2, instead it goes from 4 control points to 10 (maybe change this,
* we will see). Another difference is that temporal variation can be penalised. For starters, we don't
* really do this.
*
* (c) Alberto Gomez 2014 - King's College London
* Marija Marcan 2017 - King's College London
*/

#include <gsl.h>

#include <queue>
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/map.hpp>
#include <boost/multi_array.hpp>

#include "IPCMRIKernel.h"
#include "ISolidModelKernel.h"
#include "IMeshingKernel.h"

#include <mitkPlanarCircle.h>
#include <mitkPlanarEllipse.h>
#include <mitkPlaneGeometry.h>
#include <mitkPoint.h>
#include <mitkImage.h>
#include <mitkRotationOperation.h>
#include <mitkInteractionConst.h>
#include <mitkImagePixelReadAccessor.h>
#include <mitkImageVtkReadAccessor.h>

#include <mitkContourModelToSurfaceFilter.h>
#include <mitkImageStatisticsCalculator.h>
#include <mitkExtractSliceFilter.h>
#include <mitkVtkImageOverwrite.h>

#include "itkNearestNeighborInterpolateImageFunction.h"
#include <itkNeighborhoodBinaryThresholdImageFunction.h>
#include <itkMetaDataObject.h>

#include <vtkDistancePolyDataFilter.h>
#include <vtkPointData.h>
#include <vtkNew.h>
#include <vtkProbeFilter.h>
#include <vtkIdList.h>
#include <vtkPolyData.h>
#include <vtkDoubleArray.h>
#include <vtkShortArray.h>
#include <vtkCellData.h>
#include <vtkMassProperties.h>
#include <vtkTriangleFilter.h>

#include <mitkContourModelSet.h>
#include <mitkContourModel.h>
#include "mitkImageAccessByItk.h"

#include <Wm5IntrRay3Plane3.h>

#include <chrono>
#include <numeric>
#include <math.h>

#define _USE_MATH_DEFINES

// Variables that are accessed by many methods

/// misc
const bool KEEP_SMALL_control_pointsES = true; /// If this is on, control_pointses are adaptively subdivided
/// For mac
//const int MAX_COLS_PER_control_points = 25000; /// For the A matrix
//const int MAX_POINTS_PER_control_points = 1E+07; /// For the B matrix

/// For linux
int MAX_COLS_PER_control_points = 10000; /// For the A matrix
int MAX_POINTS_PER_control_points = 2.5E+05; /// For the B matrix
float MAX_MEMORY = -1;

const double CMS_TO_MS = 1E-02;
const double MS_TO_CMS = 100.0;

const unsigned int NDIMS = 2;
const unsigned int OUTPUT_DIMENSIONS = 1;

const double MAX_ERROR = 1E-05;
const double B_TO_MB = 1 / 1024.0 / 1024.0;
const double GB_TO_MB = 1024.0;
unsigned int number_of_control_pointses;
const double ZERO = 1E-10;
double spacing_reduction_factor_space[4] = { 2.0, 2.0, 2.0, 1.2 };
double minimum_grid_size[4] = { -1, -1, -1, -1 };
bool isCartesian = false;
bool fitData = false;
bool writeErrors = false;
/// mandatory arguments
int n_images;
std::string reference_image_filename = "";
std::string input_image_flename;
std::string extra = "";
std::string input_motion_mesh_config_file;
std::string output_prefix;
/// default arguments
bool debug = false;
bool parallel_on = false;
int threads_to_use = 2;
bool verbose = false;
unsigned int border = 0;
unsigned int bsd = 3;
unsigned int max_levels = 3;
unsigned int points_between_two_nodes = 2;
double user_bounds[2 * 2];

double image_downsampling[4] = { -1, -1, -1, -1 };

bool cyclictime = false;
double cycle_time[2];

bool allLevels = false;
bool use_user_bounds = false;
double th_velocity = -1;
double remove_nodes_th = 0.0;
bool use_extra_components = false;
bool v_in_mps = false;
double wall_velocity_factor = 1.0;
std::string wall_velocity = "wall_velocity";
int initialLevel = 0;
std::vector <std::string> initial_coeffs_filename;

// parallel computing
/// Macros for the maximun value out of three scalars or the three components of one vector
#define MAX2(x,y,z) std::max( std::max(x,y),z  )
#define MAX2_V(x) std::max( std::max(x[0],x[1]),x[2]  )
#define MAX2_V_abs(x) std::max( std::max( std::abs(x[0]), std::abs(x[1])), std::abs(x[2])  )

namespace crimson
{

	static void setupCatchSystemSignals()
	{
		static bool catchSignalsWasSetup = false;
		if (!catchSignalsWasSetup) {
			catchSignalsWasSetup = true;
		}
	}

	static double calculateParRecScaling(mitk::Image::Pointer pcmriImage)
	{
		int numPhases = 0;
		int phaseEncodingVelocityValue = 0;
		std::vector<short> phaseValues;
		double max_ph = 0;

		pcmriImage->GetPropertyList()->GetIntProperty("mapping.venc", phaseEncodingVelocityValue);
		pcmriImage->GetPropertyList()->GetIntProperty("PAR.MaxNumberOfCardiacPhases", numPhases);

		for (int i = 2*numPhases; i < pcmriImage->GetDimensions()[3]; i++)
		{
			for (int j = 0; j < pcmriImage->GetDimensions()[0]; j++)
			{
				for (int z = 0; z < pcmriImage->GetDimensions()[1]; z++)
				{
					itk::Index<3> idx;
					idx[0] = j;
					idx[1] = z;
					idx[2] = 0;
					phaseValues.push_back(pcmriImage->GetPixelValueByIndex(idx, i));
					auto pixValue = pcmriImage->GetPixelValueByIndex(idx, i);
					if (pcmriImage->GetPixelValueByIndex(idx, i)>max_ph)
						max_ph = pcmriImage->GetPixelValueByIndex(idx, i);
				}
			}
		}

		int power = ceil(log2(max_ph));
		double scale = (pow(2, power)) / 2;

		return scale;
	}

	static double calculateDicomScaling(mitk::Image::Pointer magnitudeImage, mitk::Image::Pointer phaseImage)
	{
		auto propList = phaseImage->GetPropertyList();
		auto propList2 = magnitudeImage->GetPropertyList();
		double venscale = 0;
		int phaseEncodingVelocityValue = 0;
		double scale = 0;

		phaseImage->GetPropertyList()->GetIntProperty("mapping.venc", phaseEncodingVelocityValue);
		//phaseImage->GetPropertyList()->GetDoubleProperty("Private_0019_10e2", venscale); //Private_0019_10e2
		phaseImage->GetPropertyList()->GetDoubleProperty("mapping.venscale", venscale); //Private_0019_10e2

		double vscale = (venscale*M_PI) / (double)phaseEncodingVelocityValue;

		return vscale;
	}

	//////////////////////////////////////////////////////////////////////////
	// Mapping of the PCMRI onto a model mesh face.
	//////////////////////////////////////////////////////////////////////////

	class MappingTask : public crimson::async::TaskWithResult<mitk::BaseData::Pointer>
	{
	public:
		MappingTask(const IPCMRIKernel::ContourSet& contoursPCMRI, const mitk::PlanarFigure::Pointer contourModel,
			const MeshData::Pointer mesh, const FaceIdentifier face,
			mitk::Point2D pcmriLandmark, mitk::Point2D mraLandmark, WholeImageTypeFloat::Pointer pcmriImage,
			bool imageFlipped, std::string meshNodeUID, int cardiacFrequency, int startIndex)
			: contoursPCMRI(contoursPCMRI)
			, contourModel(contourModel)
			, mesh(mesh)
			, face(face)
			, pcmriLandmark(pcmriLandmark)
			, mraLandmark(mraLandmark)
			, pcmriImage(pcmriImage)
			, imageFlipped(imageFlipped)
			, meshNodeUID(meshNodeUID)
			, cardiacFrequency(cardiacFrequency)
			, startIndex(startIndex)

		{
		}

		std::tuple<State, std::string> runTask() override
		{
			setupCatchSystemSignals();

			//setup progress tracking

			int progressConvert = contoursPCMRI.size();
			int progressLeft = progressConvert;

			stepsAddedSignal(progressLeft);

			//setup stuff

			Eigen::setNbThreads(threads_to_use);
			Eigen::initParallel();

			/// We are assuming that the first point is the reference point for the rotation.
			/// If this is determined by other means it does not matter, in the end what we need
			/// is the value 'a' of the angle between the two (computed below)

			int Ninterp = 75; /// Number of corresponding points
			double lambda = 0.00001;
			int ncontrol_pts = 4;

			//extract face nodes
			std::vector<int> faceNodes = mesh->getNodeIdsForFace(face);

			std::vector<mitk::Point3D> coordinatesMesh;
			for (std::vector<int>::iterator it = faceNodes.begin(); it < faceNodes.end(); it++) {
				coordinatesMesh.push_back(mesh->getNodeCoordinates(*it));
			}

			//transform model points to 2D

			std::vector<mitk::Point2D> coordinatesOut;

			mitk::PlaneGeometry::Pointer plane = contourModel->GetPlaneGeometry()->Clone();

			for (std::vector<mitk::Point3D>::iterator it = coordinatesMesh.begin(); it < coordinatesMesh.end(); it++) {
				mitk::Point2D projectedPoint;
				plane->Map(*it, projectedPoint);
				coordinatesOut.push_back(projectedPoint);
			}

			//transfer model contour points into std::vector< PointType > format

			std::vector< mitk::Point2D > polyLineModel(contourModel->GetNumberOfControlPoints());
			for (int i = 0; i < contourModel->GetNumberOfControlPoints(); i++)
				polyLineModel[i] = (contourModel->GetControlPoint(i));

			std::vector<IPCMRIKernel::BsplineGridType::PointType> contourModel2D;
			contourModel2D.reserve(polyLineModel.size());    //  avoids unnecessary reallocations
			std::transform(polyLineModel.begin(), polyLineModel.end(),
				std::back_inserter(contourModel2D),
				IPCMRIKernel::ToBSGridType());

			//find centroid of model 2D face contour and center the contour

			IPCMRIKernel::BsplineGridType::PointType centroidModel;
			centroidModel[0] = 0; centroidModel[1] = 0;
			for (int i = 0; i < contourModel2D.size(); i++){
				centroidModel += contourModel2D[i];
			}
			centroidModel /= contourModel2D.size();

			//check if the PC-MRI image is flipped and setup things accordingly
			std::vector< IPCMRIKernel::BsplineGridType::PointType > centred_coordinatesModel(contourModel2D.size());
			for (int i = 0; i < contourModel2D.size(); i++){
				centred_coordinatesModel[i] = contourModel2D[i] - centroidModel;
				if (imageFlipped)
					centred_coordinatesModel[i][0] = -centred_coordinatesModel[i][0];
			}

			//the resulting vectors
			boost::multi_array<double, 2> _mappedPCMRIvalues(boost::extents[coordinatesOut.size()][contoursPCMRI.size()]);
			boost::multi_array<mitk::Vector3D, 2> _mappedPCMRIvectors(boost::extents[coordinatesOut.size()][contoursPCMRI.size()]);

			std::vector<double> _originalFlow(contoursPCMRI.size());
			std::vector<double> _originalFlow2;

			if (isCancelling()) {
				return std::make_tuple(State_Cancelled, std::string("Operation cancelled."));
			}

			try {

				//do stuff for one contour at a time
				for (int slice = 0; slice < contoursPCMRI.size(); ++slice) {

					//extract PCMRI boundary points into an array
					const mitk::PlanarFigure::Pointer figure = contoursPCMRI[slice];

					//transfer model contour points into std::vector< PointType > format
					std::vector< mitk::Point2D > polyLineImage(figure->GetNumberOfControlPoints());
					for (int i = 0; i < figure->GetNumberOfControlPoints(); i++)
						polyLineImage[i] = (figure->GetControlPoint(i));

					auto planePCMRI = figure->GetPlaneGeometry();

					std::vector< mitk::Point2D > polyLineImage2;
					for (const mitk::Point2D& polyLineElement : figure->GetPolyLine(0)) {
						polyLineImage2.push_back(polyLineElement);
					}

					//transfer model contour points into std::vector< PointType > format
					std::vector< mitk::Point3D > polyLineImage3(figure->GetNumberOfControlPoints());
					for (int i = 0; i < figure->GetNumberOfControlPoints(); i++)
						polyLineImage3[i] = figure->GetWorldControlPoint(i);

					std::vector< mitk::Point2D > polyLineImage2D;

					for (std::vector<mitk::Point3D>::iterator it = polyLineImage3.begin(); it < polyLineImage3.end(); it++) {
						mitk::Point2D projectedPoint;
						planePCMRI->Map(*it, projectedPoint);
						polyLineImage2D.push_back(projectedPoint);
					}

					std::vector<IPCMRIKernel::PointType> coordinatesImage;
					coordinatesImage.reserve(polyLineImage2.size());    //  avoids unnecessary reallocations
					std::transform(polyLineImage2.begin(), polyLineImage2.end(),
						std::back_inserter(coordinatesImage),
						IPCMRIKernel::ToBSGridType());

					///****************************************************************************
					///*                            Find centroids                             *
					///****************************************************************************

					//find centroid of PCMRI contour
					IPCMRIKernel::BsplineGridType::PointType centroidImage;

					centroidImage[0] = 0; centroidImage[1] = 0;
					for (int i = 0; i < coordinatesImage.size(); i++){
						centroidImage += coordinatesImage[i];
					}
					centroidImage /= coordinatesImage.size();

					//centre image spline points (store transformation as displacementPCMRI) displacementPCMRI=centroidImage here

					std::vector< IPCMRIKernel::BsplineGridType::PointType > centred_coordinatesImage(coordinatesImage.size());

					for (int i = 0; i < coordinatesImage.size(); i++){
						centred_coordinatesImage[i] = coordinatesImage[i] - centroidImage;
					}

					///****************************************************************************
					///*                            Find the rotation                             *
					///****************************************************************************
					IPCMRIKernel::BsplineGridType::PointType vA, vB; //vA model landmark, vB image landmark
					//landmark needs to be centred as well!
					vA[0] = mraLandmark[0] - centroidModel[0];
					vA[1] = mraLandmark[1] - centroidModel[1];
					vB[0] = pcmriLandmark[0] - centroidImage[0];
					vB[1] = pcmriLandmark[1] - centroidImage[1];

					if (imageFlipped)
						vA[0] = -vA[0];

					double angleA = std::atan2(vA[1], vA[0]);
					double angleB = std::atan2(vB[1], vB[0]);
					double a = angleB - angleA;

					IPCMRIKernel::BsplineGridType::DenseMatrixType Rot(2, 2);
					Rot << std::cos(a), -std::sin(a),
						std::sin(a), std::cos(a);

					std::vector< IPCMRIKernel::BsplineGridType::PointType> rotated_centred_coordinatesModel(centred_coordinatesModel.size());

					for (int i = 0; i < centred_coordinatesModel.size(); i++){
						/// not very elegant, sorry !
						rotated_centred_coordinatesModel[i][0] = Rot(0, 0) * centred_coordinatesModel[i][0] + Rot(0, 1) * centred_coordinatesModel[i][1];
						rotated_centred_coordinatesModel[i][1] = Rot(1, 0) * centred_coordinatesModel[i][0] + Rot(1, 1) * centred_coordinatesModel[i][1];
					}

					//warp the rest of the model face

					std::vector<IPCMRIKernel::PointType> coordinatesOutBS;
					coordinatesOutBS.reserve(coordinatesOut.size());    //  avoids unnecessary reallocations
					std::transform(coordinatesOut.begin(), coordinatesOut.end(),
						std::back_inserter(coordinatesOutBS),
						IPCMRIKernel::ToBSGridType());

					std::vector<IPCMRIKernel::BsplineGridType::PointType> coordinatesOut_centred(coordinatesOutBS);
					for (int i = 0; i < coordinatesOut.size(); i++){
						coordinatesOut_centred[i] = coordinatesOutBS[i] - centroidModel;
						if (imageFlipped)
							coordinatesOut_centred[i][0] = -coordinatesOut_centred[i][0];
					}

					std::vector< IPCMRIKernel::BsplineGridType::PointType> rotated_centred_coordinatesOut(coordinatesOut_centred.size());

					for (int i = 0; i < coordinatesOut_centred.size(); i++){
						/// not very elegant, sorry !
						rotated_centred_coordinatesOut[i][0] = Rot(0, 0) * coordinatesOut_centred[i][0] + Rot(0, 1) * coordinatesOut_centred[i][1];
						rotated_centred_coordinatesOut[i][1] = Rot(1, 0) * coordinatesOut_centred[i][0] + Rot(1, 1) * coordinatesOut_centred[i][1];
					}

					///****************************************************************************
					///*                            FIT A CYCLIC B-spline to each contour         *
					///****************************************************************************

					//fit image spline points

					std::vector< IPCMRIKernel::BsplineGrid1DType::PointType > interpolated_contour_angle(Ninterp);
					for (int i = 0; i < Ninterp; i++){
						interpolated_contour_angle[i] = i * 2 * M_PI / Ninterp + angleB;
					}

					//fit n-points spline to rotated 2D model (b)

					std::vector< IPCMRIKernel::BsplineGridType::PointType> interpolated_contourA, interpolated_contourB;

					interpolateClosedContour<IPCMRIKernel::BsplineGrid1DType, IPCMRIKernel::BsplineGridType>(rotated_centred_coordinatesModel, interpolated_contour_angle, interpolated_contourA);
					interpolateClosedContour<IPCMRIKernel::BsplineGrid1DType, IPCMRIKernel::BsplineGridType>(centred_coordinatesImage, interpolated_contour_angle, interpolated_contourB);

					///****************************************************************************
					///                Calculate the vectors from one contour to another          *
					///****************************************************************************
					std::vector< IPCMRIKernel::BsplineGridType::CoefficientType> contour_deformation_vectorsx(Ninterp), contour_deformation_vectorsy(Ninterp);
					for (int i = 0; i < Ninterp; i++){

						auto vec = interpolated_contourB[i] - interpolated_contourA[i];
						contour_deformation_vectorsx[i][0] = vec[0];
						contour_deformation_vectorsy[i][0] = vec[1];
					}

					///****************************************************************************
					///             Interpolate the deformation field densely                     *
					///****************************************************************************

					//interpolated_contourA; // coordinates
					//contour_deformation_vectors; // values

					// compute bounds
					double bounds[IPCMRIKernel::BsplineGridType::IDims * 2];
					for (int i = 0; i < IPCMRIKernel::BsplineGridType::IDims; i++){
						bounds[2 * i] = std::numeric_limits<double>::max();
						bounds[2 * i + 1] = -std::numeric_limits<double>::max();
					}
					for (int i = 0; i < Ninterp; i++){
						for (int j = 0; j < IPCMRIKernel::BsplineGridType::IDims; j++){
							  if (interpolated_contourA[i][j]<bounds[2*j]){
							      bounds[2*j]  =interpolated_contourA[i][j];
							  }
							if (interpolated_contourB[i][j] < bounds[2 * j]){
								bounds[2 * j] = interpolated_contourB[i][j];
							}
							  if (interpolated_contourA[i][j]>bounds[2*j+1]){
							      bounds[2*j+1]  =interpolated_contourA[i][j];
							  }
							if (interpolated_contourB[i][j] > bounds[2 * j + 1]){
								bounds[2 * j + 1] = interpolated_contourB[i][j];
							}
						}
					}

					IPCMRIKernel::BsplineGridType::PointType grid_spacing;
					for (int j = 0; j < IPCMRIKernel::BsplineGridType::IDims; j++){
						grid_spacing[j] = (bounds[2 * j + 1] - bounds[2 * j]) / ncontrol_pts;
					}

					IPCMRIKernel::BsplineGridType::Pointer control_points2D(new IPCMRIKernel::BsplineGridType(bounds, bsd, grid_spacing, 0));
					/// the control point grid does not need border. This grid covers the whole ROI and there is no control_points division.
					control_points2D->SetParallel(parallel_on, threads_to_use);

					IPCMRIKernel::BsplineGridType::DenseVectorType values_vectorx(IPCMRIKernel::BsplineGridType::ODims*contour_deformation_vectorsx.size(), 1);
					IPCMRIKernel::BsplineGridType::DenseVectorType values_vectory(IPCMRIKernel::BsplineGridType::ODims*contour_deformation_vectorsy.size(), 1);
					std::vector< IPCMRIKernel::BsplineGridType::CoefficientType>::const_iterator const_iter;
					IPCMRIKernel::BsplineGridType::MatrixElementType *eigen_iter;

					for (const_iter = contour_deformation_vectorsx.begin(), eigen_iter = values_vectorx.data(); const_iter != contour_deformation_vectorsx.end(); ++const_iter)
					{
						for (int nd = 0; nd < IPCMRIKernel::BsplineGridType::ODims; nd++){
							(*eigen_iter) = (*const_iter)[nd];
							++eigen_iter;
						}
					}

					for (const_iter = contour_deformation_vectorsy.begin(), eigen_iter = values_vectory.data(); const_iter != contour_deformation_vectorsy.end(); ++const_iter)
					{
						for (int nd = 0; nd < IPCMRIKernel::BsplineGridType::ODims; nd++){
							(*eigen_iter) = (*const_iter)[nd];
							++eigen_iter;
						}
					}

					IPCMRIKernel::BsplineGridType::SparseMatrixType A;
					IPCMRIKernel::BsplineGridType::DenseVectorType b;
					std::vector<unsigned int> kept_nodes;
					std::vector<unsigned int> corresponding_nodes;

					std::vector<IPCMRIKernel::BsplineGridType::CoefficientType> interpolated_vectorsx, interpolated_vectorsy, interpolated_vectorsxBorder, interpolated_vectorsyBorder;
					IPCMRIKernel::BsplineGridType::SparseMatrixType B = control_points2D->createSamplingMatrix(interpolated_contourA, remove_nodes_th, kept_nodes, corresponding_nodes);
					IPCMRIKernel::BsplineGridType::SparseMatrixType Agrad;
					if (lambda)
					{
						//BsplineGridType::SparseMatrixType Adiv = control_points2D->createContinuousDivergenceSamplingMatrix(kept_nodes,corresponding_nodes);
						Agrad = control_points2D->createContinuousGradientSamplingMatrix(remove_nodes_th, kept_nodes);
					}

					///---------------------------------------
					///     Do For X
					///---------------------------------------
					{

						{ /// Keep scope to save memory
							Eigen::setNbThreads(8);
							A = B.transpose() *B;
							b = B.transpose()*values_vectorx;
						}

						// Regularisation
						if (lambda){
							A = A + lambda / (1 - lambda)*Agrad;/// See notes on p118 of my thesis for an explanation
						}

						if (debug) std::cout << "\tCompute coefficients in this control_points" << std::endl;
						/// Available additional solvers
						/// BiCGSTAB + IncompletLUT preconditioner
						/// SparseQRf
						/// SPQR
						/// PastixLU

						IPCMRIKernel::BsplineGridType::DenseVectorType x;//,x2,x3,x4;
						echo::solvers::ConfigurationParameter config;
						config.max_iterations = 10000; /// This number might look huge but we need to get to the desired tolerance
						config.tol = 1E-06;
						config.verbose = true;
						std::string str_solver;

						/// Add some condition like if the matrix is larger than e.g. 5000 x 5000 then use an iterative method such as BiCGSTAB
						if (debug) std::cout << "\tSolve linear system where A is " << A.rows() << "x" << A.cols() << std::endl;
						//echo::solvers::solveWithSPQR<BsplineGridType::SparseMatrixType, BsplineGridType::DenseVectorType>(A,b,x,config); str_solver="_SPQR";
						/// This can happen if there is a region with very densely populated points
						echo::solvers::solveWithGMRES<IPCMRIKernel::BsplineGridType::SparseMatrixType, IPCMRIKernel::BsplineGridType::DenseVectorType>(A, b, x, config); str_solver = "_GMRES";
						//echo::solvers::solveWithBiCGSTAB<BsplineGridType::SparseMatrixType, BsplineGridType::DenseVectorType>(A,b,x,config); str_solver="_BiCGSTAB";

						if (debug) std::cout << "\tCoefficients computed, replace control_points values with the calculated coefficients" << std::endl;
						/// Reorganize x into an array of coefficients, taking into account the reordering
						control_points2D->setAll<IPCMRIKernel::BsplineGridType::DenseVectorType>(x, kept_nodes);

						interpolated_vectorsx = control_points2D->evaluate(rotated_centred_coordinatesOut); //TODO or coordinatesOut_centred?
						interpolated_vectorsxBorder = control_points2D->evaluate(interpolated_contourA);
					}

					///---------------------------------------
					///     Do For Y
					///---------------------------------------
					{

						{ /// Keep scope to save memory
							Eigen::setNbThreads(8);
							A = B.transpose() *B;
							b = B.transpose()*values_vectory;
						}

						// Regularisation
						if (lambda){
							A = A + lambda / (1 - lambda)*Agrad;/// See notes on p118 of my thesis for an explanation
						}

						if (debug) std::cout << "\tCompute coefficients in this control_points" << std::endl;
						/// Available additional solvers
						/// BiCGSTAB + IncompletLUT preconditioner
						/// SparseQR
						/// SPQR
						/// PastixLU

						IPCMRIKernel::BsplineGridType::DenseVectorType x;//,x2,x3,x4;
						echo::solvers::ConfigurationParameter config;
						config.max_iterations = 10000; /// This number might look huge but we need to get to the desired tolerance
						config.tol = 1E-06;
						config.verbose = true;
						std::string str_solver;

						/// Add some condition like if the matrix is larger than e.g. 5000 x 5000 then use an iterative method such as BiCGSTAB
						if (debug) std::cout << "\tSolve linear system where A is " << A.rows() << "x" << A.cols() << std::endl;
						//echo::solvers::solveWithSPQR<BsplineGridType::SparseMatrixType, BsplineGridType::DenseVectorType>(A,b,x,config); str_solver="_SPQR";
						/// This can happen if there is a region with very densely populated points
						echo::solvers::solveWithGMRES<IPCMRIKernel::BsplineGridType::SparseMatrixType, IPCMRIKernel::BsplineGridType::DenseVectorType>(A, b, x, config); str_solver = "_GMRES";
						//echo::solvers::solveWithBiCGSTAB<BsplineGridType::SparseMatrixType, BsplineGridType::DenseVectorType>(A,b,x,config); str_solver="_BiCGSTAB";

						if (debug) std::cout << "\tCoefficients computed, replace control_points values with the calculated coefficients" << std::endl;
						/// Reorganize x into an array of coefficients, taking into account the reordering
						control_points2D->setAll<IPCMRIKernel::BsplineGridType::DenseVectorType>(x, kept_nodes);

						interpolated_vectorsy = control_points2D->evaluate(rotated_centred_coordinatesOut);
						interpolated_vectorsyBorder = control_points2D->evaluate(interpolated_contourA);
					}

					//apply displacement on rotated model 2D
					//warp the rest of the model face
					//displace the result above for displacementPCMRI - we are now in the same frame of reference as the PCMRI image

					std::vector<mitk::Point2D> coordinatesDisplaced;
					std::vector<mitk::Point2D> coordinatesDisplacedBorder;
					std::vector<mitk::Point2D> coordinatesDisplacedIndex;
					std::vector<mitk::Point2D> coordinatesDisplacedIndexMapped;

					for (int i = 0; i < interpolated_contourA.size(); i++)
					{
						mitk::Point2D pointDisplacedBorder;
						pointDisplacedBorder[0] = interpolated_contourA[i][0] + interpolated_vectorsxBorder[i][0] + centroidImage[0];
						pointDisplacedBorder[1] = interpolated_contourA[i][1] + interpolated_vectorsyBorder[i][0] + centroidImage[1];
						coordinatesDisplacedBorder.push_back(pointDisplacedBorder);
					}
					for (int i = 0; i < rotated_centred_coordinatesOut.size(); i++){
						mitk::Point2D pointDisplaced;
						pointDisplaced[0] = rotated_centred_coordinatesOut[i][0] + interpolated_vectorsx[i][0] + centroidImage[0];
						pointDisplaced[1] = rotated_centred_coordinatesOut[i][1] + interpolated_vectorsy[i][0] + centroidImage[1];
						coordinatesDisplaced.push_back(pointDisplaced);

						itk::Index<4> idx, idx2;
						itk::Point<float, 4> phyPoint, phyPoint2;
						mitk::Point2D pointDisplacedIndex, pointDisplacedIndexMapped;
						mitk::Point3D pointMapped;
						phyPoint[0] = pointDisplaced[0];
						phyPoint[1] = pointDisplaced[1];
						phyPoint[2] = 0;
						phyPoint[3] = slice;
						pcmriImage->TransformPhysicalPointToIndex(phyPoint, idx);
						pointDisplacedIndex[0] = idx[0];
						pointDisplacedIndex[1] = idx[1];
						coordinatesDisplacedIndex.push_back(pointDisplacedIndex);

						plane->Map(pointDisplaced, pointMapped);
						phyPoint2[0] = pointMapped[0];
						phyPoint2[1] = pointMapped[1];
						phyPoint2[2] = pointMapped[2];
						phyPoint2[3] = slice;
						pcmriImage->TransformPhysicalPointToIndex(phyPoint2, idx2);
						pointDisplacedIndexMapped[0] = idx2[0];
						pointDisplacedIndexMapped[1] = idx2[1];
						coordinatesDisplacedIndexMapped.push_back(pointDisplacedIndexMapped);
					}

					//calculate the scaling factor for velocities in case of different contour vs model areas - to preserve flow volume
					auto areaContour = ISolidModelKernel::contourArea(figure);
					double area = mesh->calculateArea(face);
					auto radius = sqrt(area / 3.14);
					auto scalingFactor = areaContour / area;

					MITK_INFO << "Planar figure area " << areaContour;
					MITK_INFO << "Mesh face area " << area;
					MITK_INFO << "Area scaling factor " << scalingFactor;

					//interpolate pre-calculated profile points to mesh warped points
					typedef itk::LinearInterpolateImageFunction<WholeImageTypeFloat, float>	LinearInterpolatorType;
					auto interpolator = LinearInterpolatorType::New();
					interpolator->SetInputImage(pcmriImage);

					for (int i = 0; i < coordinatesDisplaced.size(); i++){
						itk::ContinuousIndex<float, 4> idx;
						itk::Point<float, 4> phyPoint;
						double value;
						mitk::Point2D point2D;
						mitk::Point3D point3D;
						point2D[0] = coordinatesDisplaced[i][0];
						point2D[1] = coordinatesDisplaced[i][1];
						planePCMRI->Map(point2D, point3D);

						phyPoint[0] = point3D[0];
						phyPoint[1] = point3D[1];
						phyPoint[2] = point3D[2];
						phyPoint[3] = slice + startIndex;

						pcmriImage->TransformPhysicalPointToContinuousIndex(phyPoint, idx);

						value = interpolator->EvaluateAtContinuousIndex(idx);

						if (imageFlipped)
							value = -value;
						_mappedPCMRIvalues[i][slice] = value * scalingFactor;
					}

					progressMadeSignal(1);

				}

				if (isCancelling()) {
					return std::make_tuple(State_Cancelled, std::string("Operation cancelled."));
				}

				//calculate flow waveform
				auto flowWaveform = IPCMRIKernel::calculateFlowWaveform(_mappedPCMRIvalues, mesh, face);
				std::vector<double> flowWaveformAbs;
				for (std::vector<double>::iterator itflow = flowWaveform.begin(); itflow < flowWaveform.end(); itflow++)
				{
					flowWaveformAbs.push_back(abs(*itflow));
				}

				double area = mesh->calculateArea(face);
				auto radius = sqrt(area / 3.14);

				//setting the surface representation for the PCMRIData to a scaled profile timepoint with maximum flow
				double max = *std::max_element(flowWaveformAbs.begin(), flowWaveformAbs.end());
				int maxIndex = std::distance(flowWaveformAbs.begin(), std::max_element(flowWaveformAbs.begin(), flowWaveformAbs.end()));

				// scale the values of the flow velocity!
				std::vector<double> timePointPCMRIValues;
				for (int j = 0; j < coordinatesMesh.size(); j++)
				{
					timePointPCMRIValues.push_back(abs(_mappedPCMRIvalues[j][maxIndex]));
				}

				double maxValue = *std::max_element(timePointPCMRIValues.begin(), timePointPCMRIValues.end());

				//scaling factor should be such that maximum profile value is equivalent to double the size of the face diameter
				double scaleFactor = maxValue / (radius * 4);

				// Finally, create the output MITK object
				mitk::BaseData::Pointer output;

				//set the output to point to results of the mapping
				PCMRIData::Pointer data = PCMRIData::New();

				data->setMappedPCMRIvalues(_mappedPCMRIvalues);
				data->setMesh(mesh.GetPointer());
				data->setMeshNodeUID(meshNodeUID);
				data->setFaceIdentifierIndex(mesh->getFaceIdentifierMap().faceIdentifierIndex(face));

				//set fixed properties needed for time interpolation
				auto normal = contourModel->GetPlaneGeometry()->GetNormal();
				data->setTimeInterpolationParameters(cardiacFrequency, normal, contoursPCMRI.size());
				data->setVisualizationParameters(maxIndex, scaleFactor);

				output = data.GetPointer();

				if (isCancelling()) {
					return std::make_tuple(State_Cancelled, std::string("Operation cancelled."));
				}

				setResult(output.GetPointer());
				return std::make_tuple(State_Finished, std::string("Mapping finished successfully."));
			}
			catch (...) {
				progressMadeSignal(progressLeft);

				std::string errorMessage = "Exception caught during mapping\n";

				return std::make_tuple(State_Failed, std::string(errorMessage));
			}
		}

	private:
		const IPCMRIKernel::ContourSet contoursPCMRI;
		const mitk::PlanarFigure::Pointer contourModel;
		const MeshData::Pointer mesh;
		const FaceIdentifier face;
		const mitk::Point2D pcmriLandmark;
		const mitk::Point2D mraLandmark;
		const WholeImageTypeFloat::Pointer pcmriImage;
		const bool imageFlipped;
		const std::string meshNodeUID;
		const int cardiacFrequency;
		const int startIndex;
	};

	std::shared_ptr<async::TaskWithResult<mitk::BaseData::Pointer>>
		IPCMRIKernel::createMapTask(const ContourSet& contoursPCMRI, const mitk::PlanarFigure::Pointer& contourModel,
		MeshData::Pointer mesh, FaceIdentifier face, mitk::Point2D pcmriLandmark,
		mitk::Point2D mraLandmark, WholeImageTypeFloat::Pointer pcmriImage,
		bool imageFlipped, std::string meshNodeUID, int cardiacFrequency, int startIndex)
	{
		return std::static_pointer_cast<crimson::async::TaskWithResult<mitk::BaseData::Pointer>>(std::make_shared<MappingTask>(
			contoursPCMRI, contourModel, mesh, face, pcmriLandmark, mraLandmark, pcmriImage, imageFlipped, meshNodeUID, cardiacFrequency, startIndex));
	}

	mitk::Surface::Pointer IPCMRIKernel::generateSurfaceRepresentation(std::vector<mitk::Vector3D> timePointPCMRIVectorsInterpolated, mitk::Surface::Pointer meshSurfaceRepresentation)
	{

		vtkPolyData* meshSurfaceOriginal = meshSurfaceRepresentation->GetVtkPolyData();
		vtkNew<vtkPolyData> polyData;
		vtkNew<vtkPoints> points;
		polyData->SetPoints(points.GetPointer());

		polyData->DeepCopy(meshSurfaceOriginal);

		auto nPoints = meshSurfaceOriginal->GetNumberOfPoints();
		for (vtkIdType a = 0; a < meshSurfaceOriginal->GetNumberOfPoints(); a++)
		{
			double templatepoint[3];
			meshSurfaceOriginal->GetPoint(a, templatepoint);

			double newPoint[3];
			if (a < timePointPCMRIVectorsInterpolated.size())
			{
				newPoint[0] = templatepoint[0] + timePointPCMRIVectorsInterpolated[a][0];
				newPoint[1] = templatepoint[1] + timePointPCMRIVectorsInterpolated[a][1];
				newPoint[2] = templatepoint[2] + timePointPCMRIVectorsInterpolated[a][2];

			}
			else
			{
				newPoint[0] = templatepoint[0];
				newPoint[1] = templatepoint[1];
				newPoint[2] = templatepoint[2];
			}

			points->InsertNextPoint(newPoint);
			points->Modified();
		}

		polyData->SetPoints(points.GetPointer());
		auto _surfaceRepresentation = mitk::Surface::New();
		_surfaceRepresentation->SetVtkPolyData(polyData.GetPointer());

		return _surfaceRepresentation;

	}

	double computeSubvolume(std::vector<double> velocities, mitk::Point3D point1, mitk::Point3D point2, mitk::Point3D point3, double& areaTotal)
	{
		itk::Vector<double, 3> vec1 = (point2 - point1);
		itk::Vector<double, 3> vec2 = (point3 - point1);
		auto cross = itk::CrossProduct(vec1, vec2);
		auto area = (itk::CrossProduct(vec1, vec2)).GetNorm() / 2;
		areaTotal += area;
		double volume = (velocities[0] + velocities[1] + velocities[2]) * area / 3;
		return volume;
	}

	std::vector<double> IPCMRIKernel::calculateFlowWaveform(boost::multi_array<double, 2> mappedPCMRIvalues, const MeshData::Pointer mesh,
		const FaceIdentifier face)
	{
		std::vector<double> flowWaveform;
		double areaTotal = 0;

		int size = mappedPCMRIvalues.shape()[1];
		int nPoints = mappedPCMRIvalues.shape()[0];
		std::vector<crimson::MeshData::MeshFaceInfo> faceInfo = mesh->crimson::MeshData::getMeshFaceInfoForFace(face);

		//do for every timestep
		for (int i = 0; i < size; i++)
		{
			double volume = 0;

			std::map<int, double> velocitiesMap;
			auto meshNodesFace = mesh->getNodeIdsForFace(face);
			int j = 0;
			for (std::vector<int>::iterator it = meshNodesFace.begin(); it < meshNodesFace.end(); it++)
			{
				velocitiesMap.insert(std::pair<int, double>(*it, mappedPCMRIvalues[j][i]));
				j++;
			}

			//calculate subvolume for every face node
			for (std::vector<crimson::MeshData::MeshFaceInfo>::iterator it = faceInfo.begin(); it < faceInfo.end(); it++) {
				int nodeIdx[3];
				std::vector<double> velocities;
				nodeIdx[0] = (*it).nodeIds[0];
				nodeIdx[1] = (*it).nodeIds[1];
				nodeIdx[2] = (*it).nodeIds[2];
				mitk::Point3D point1, point2, point3;
				point1 = mesh->getNodeCoordinates(nodeIdx[0]);
				point2 = mesh->getNodeCoordinates(nodeIdx[1]);
				point3 = mesh->getNodeCoordinates(nodeIdx[2]);
				double vel1 = 0, vel2 = 0, vel3 = 0;
				if (velocitiesMap.find(nodeIdx[0]) != velocitiesMap.end())
					vel1 = velocitiesMap.find(nodeIdx[0])->second;
				if (velocitiesMap.find(nodeIdx[1]) != velocitiesMap.end())
					vel2 = velocitiesMap.find(nodeIdx[1])->second;
				if (velocitiesMap.find(nodeIdx[2]) != velocitiesMap.end())
					vel3 = velocitiesMap.find(nodeIdx[2])->second;
				velocities = { vel1, vel2, vel3 };
				volume += computeSubvolume(velocities, point1, point2, point3, areaTotal);
			}

			flowWaveform.push_back(volume);
		}

		return flowWaveform;
	}

} // namespace crimson


//   Copyright 2015 Patrick Putnam
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
#ifndef CLOTHO_POSITION_GENERATOR_HPP_
#define CLOTHO_POSITION_GENERATOR_HPP_

#include <boost/property_tree/ptree.hpp>

#include "clotho/data_spaces/generators/position_distribution_helper.hpp"

namespace clotho {
namespace genetics {

template < class RNG, class PositionType >
class position_generator {
public:

    typedef position_generator< RNG, PositionType > self_type;

    typedef RNG                 random_engine_type;
    typedef PositionType        position_type;

    typedef typename position_distribution_helper< PositionType >::type distribution_type;

    position_generator( random_engine_type * rng ) :
        m_rand( rng )
    {}

    position_generator( random_engine_type * rng, boost::property_tree::ptree & config ) :
        m_rand( rng )
    {  }

    position_generator( const self_type & other ) :
        m_rand( other.m_rand )
    {}

    position_type   operator()() {
        return m_dist( *m_rand );
    }

    virtual ~position_generator() {}
protected:
    random_engine_type  * m_rand;
    distribution_type   m_dist;
};

template < class PositionType >
class position_generator2 {
public:
    typedef position_generator2< PositionType > self_type;
    typedef PositionType        position_type;
    typedef typename position_distribution_helper< PositionType >::type distribution_type;

    position_generator2() {}

    template < class Engine >
    position_type operator()( Engine & eng ) {
        return m_dist( eng );
    }

    virtual ~position_generator2() {}

protected:
    distribution_type   m_dist;
};

}   // namespace genetics
}   // namespace clotho

#endif  // CLOTHO_POSITION_GENERATOR_HPP_


#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <numeric>
#include <chrono>
#include <limits>
#include <float.h>
#include <lamure/mesh/bvh.h>
#include <memory>

#include <CGAL/Polygon_mesh_processing/measure.h>

#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/trim.hpp>

#include "Utils.h"
#include "OBJ_printer.h"
#include "SymMat.h"
#include "cluster_settings.h"
#include "ErrorQuadric.h"
#include "Chart.h"
#include "JoinOperation.h"
#include "polyhedron_builder.h"
#include "eig.h"
#include "ClusterCreator.h"
#include "GridClusterCreator.h"
#include "ParallelClusterCreator.h"

#include "CGAL_typedefs.h"

int main( int argc, char** argv )
{

  std::string obj_filename = "dino.obj";
  if (Utils::cmdOptionExists(argv, argv+argc, "-f")) {
    obj_filename = std::string(Utils::getCmdOption(argv, argv + argc, "-f"));
  }
  else {
    std::cout << "Please provide an obj filename using -f <filename.obj>" << std::endl;
    std::cout << "Optional: -of specifies outfile name" << std::endl;

    std::cout << "Optional: -ch specifies chart threshold (=0)" << std::endl;
    std::cout << "Optional: -co specifies cost threshold (=double max)" << std::endl;

    std::cout << "Optional: -ef specifies error fit coefficient (=1)" << std::endl;
    std::cout << "Optional: -eo specifies error orientation coefficient (=1)" << std::endl;
    std::cout << "Optional: -es specifies error shape coefficient (=1)" << std::endl;

    std::cout << "Optional: -cc specifies cell resolution for grid splitting, along longest axis. When 0, no cell splitting is used. (=0)" << std::endl;
    std::cout << "Optional: -ct specifies threshold for grid chart splitting by normal variance (=0.001)" << std::endl;

    std::cout << "Optional: -debug writes charts to obj file, as colours that override texture coordinates (=false)" << std::endl;
    return 1;
  }

  std::string out_filename = obj_filename.substr(0,obj_filename.size()-4) + "_charts.obj";
  if (Utils::cmdOptionExists(argv, argv+argc, "-of")) {
    out_filename = Utils::getCmdOption(argv, argv + argc, "-of");
  }

  double cost_threshold = std::numeric_limits<double>::max();
  if (Utils::cmdOptionExists(argv, argv+argc, "-co")) {
    cost_threshold = atof(Utils::getCmdOption(argv, argv + argc, "-co"));
  }
  uint32_t chart_threshold = 0;
  if (Utils::cmdOptionExists(argv, argv+argc, "-ch")) {
    chart_threshold = atoi(Utils::getCmdOption(argv, argv + argc, "-ch"));
  }
  double e_fit_cf = 1.0;
  if (Utils::cmdOptionExists(argv, argv+argc, "-ef")) {
    e_fit_cf = atof(Utils::getCmdOption(argv, argv + argc, "-ef"));
  }
  double e_ori_cf = 1.0;
  if (Utils::cmdOptionExists(argv, argv+argc, "-eo")) {
    e_ori_cf = atof(Utils::getCmdOption(argv, argv + argc, "-eo"));
  }
  double e_shape_cf = 1.0;
  if (Utils::cmdOptionExists(argv, argv+argc, "-es")) {
    e_shape_cf = atof(Utils::getCmdOption(argv, argv + argc, "-es"));
  }
  double cst = 0.001;
  if (Utils::cmdOptionExists(argv, argv+argc, "-ct")) {
    cst = atof(Utils::getCmdOption(argv, argv + argc, "-ct"));
  }

  CLUSTER_SETTINGS cluster_settings (e_fit_cf, e_ori_cf, e_shape_cf, cst);

  int cell_resolution = 0;
  if (Utils::cmdOptionExists(argv, argv+argc, "-cc")) {
    cell_resolution = atoi(Utils::getCmdOption(argv, argv + argc, "-cc"));
  }

  if (Utils::cmdOptionExists(argv, argv+argc, "-debug")) {
    cluster_settings.write_charts_as_textures = true;
  }

  std::vector<std::string> materials;

    //load OBJ into arrays
  std::cout << "loading obj..." << std::endl;
  std::vector<double> vertices;
  std::vector<int> tris;
  std::vector<double> t_coords;
  std::vector<int> tindices;
  BoundingBoxLimits limits = Utils::load_obj( obj_filename, vertices, tris, t_coords, tindices, materials);

  if (vertices.size() == 0 ) {
    std::cout << "didnt find any vertices" << std::endl;
    return 1;
  }
  // std::cout << "Mesh loaded (" << triangles.size() << " triangles, " << vertices.size() / 3 << " vertices, " << tris.size() / 3 << " faces, " << t_coords.size() / 2 << " tex coords)" << std::endl;
  std::cout << "Mesh loaded (" << vertices.size() / 3 << " vertices, " << tris.size() / 3 << " faces, " << t_coords.size() / 2 << " tex coords)" << std::endl;

  std::cout << "loading mtl..." << std::endl;
  std::string mtl_filename = obj_filename.substr(0, obj_filename.size()-4)+".mtl";
  std::map<std::string, std::pair<std::string, int> > material_map;
  bool load_mtl_success = Utils::load_mtl(mtl_filename, material_map);

  //create face to texture id vector (implicit id = face, value = texture id)
  if (materials.size() != 0 && materials.size() != tris.size() / 3)
  {
    std::cout << "WARNING: incorrect amount of materials found, given number of incoming triangles.\nTriangles will be rendered from first texture by default\n";
  }
  std::vector<int> face_textures;
  if (load_mtl_success)
  {
    for (uint32_t i = 0; i < tris.size() / 3; ++i)
    {
      // todo find the texture id for this face and add to list
      std::string mat = materials[i];
      int mat_index = material_map[mat].second;
      face_textures.push_back(mat_index);
    }
  }

  //START chart creation ====================================================================================================================
  auto start_time = std::chrono::system_clock::now();

  // build a polyhedron from the loaded arrays
  Polyhedron polyMesh;
  bool check_vertices = true;
  polyhedron_builder<HalfedgeDS> builder( vertices, tris, t_coords, tindices, check_vertices, face_textures );
  polyMesh.delegate( builder );

  if (polyMesh.is_valid(false)){
    std::cout << "mesh valid\n";
  }

  if (!CGAL::is_triangle_mesh(polyMesh)){
    std::cerr << "Input geometry is not triangulated." << std::endl;
    return EXIT_FAILURE;
  }
  else {
    std::cout << "mesh is triangulated\n";
  }

  //key: face_id, value: chart_id
  std::map<uint32_t, uint32_t> chart_id_map;
  uint32_t active_charts;

  if (cell_resolution > 0)
  {
    //do grid clustering

    //creates clusters, starting using an arbitrary grid
    active_charts = GridClusterCreator::create_grid_clusters(polyMesh,chart_id_map, limits,cell_resolution, cluster_settings);

    std::cout << "Grid clusters: " << active_charts << std::endl;

  }

  active_charts = ParallelClusterCreator::create_charts(chart_id_map, polyMesh, cost_threshold, chart_threshold, cluster_settings);
  std::cout << "After creating chart clusters: " << active_charts << std::endl;

  //END chart creation ====================================================================================================================

  std::string chart_filename = out_filename.substr(0,out_filename.size()-4) + ".chart";
  std::ofstream ofs( out_filename );
  OBJ_printer::print_polyhedron_wavefront_with_charts( ofs, polyMesh,chart_id_map, active_charts, !cluster_settings.write_charts_as_textures, chart_filename);
  ofs.close();
  std::cout << "mesh was written to " << out_filename << std::endl;

  //Logging
  auto time = std::chrono::system_clock::now();
  std::chrono::duration<double> diff = time - start_time;
  std::time_t now_c = std::chrono::system_clock::to_time_t(time);
  std::string log_path = "../../data/logs/chart_creation_log.txt";
  ofs.open (log_path, std::ofstream::out | std::ofstream::app);
  ofs << "\n-------------------------------------\n";
  ofs << "Executed at " << std::put_time(std::localtime(&now_c), "%F %T") << std::endl;
  ofs << "Ran for " << (int)diff.count() / 60 << " m "<< (int)diff.count() % 60 << " s" << std::endl;
  ofs << "Input file: " << obj_filename << "\nOutput file: " << out_filename << std::endl;
  ofs << "Vertices: " << vertices.size()/3 << " , faces: " << tris.size()/3 << std::endl;
  ofs << "Desired Charts: " << chart_threshold << ", active charts: " << active_charts << std::endl;
  ofs << "Cost threshold: " << cost_threshold << std::endl;
  ofs << "Cluster settings: e_fit: " << cluster_settings.e_fit_cf << ", e_ori: " << cluster_settings.e_ori_cf << ", e_shape: " << cluster_settings.e_shape_cf << std::endl;

  ofs.close();
  std::cout << "Log written to " << log_path << std::endl;

  //write tex id file if necessary
  if (face_textures.size() > 0){
    std::string tex_file_name = out_filename.substr(0,out_filename.size()-4) + ".texid";
    Utils::write_tex_id_file(polyMesh, tex_file_name);
  }

  return EXIT_SUCCESS ;
}