/* === S Y N F I G ========================================================= */
/*!	\file centerlinetostrokes.cpp
**	\brief centerlinetostrokes
**
**	$Id$
**
**	\legal
**	This file uses code from OpenToonz open-source animation software  (https://github.com/opentoonz/opentoonz/), which is developed from Toonz, a software originally created by Digital Video, S.p.A., Rome Italy Digital Video, S.p.A., Rome Italy.
**
**	Copyright (c) 2016 - 2019, DWANGO Co., Ltd.
**	Copyright (c) 2016 Toshihiro Shimizu - https://github.com/meso
**	Copyright (c) 2016 Shinya Kitaoka - https://github.com/skitaoka
**	Copyright (c) 2016 shun-iwasawa - https://github.com/shun-iwasawa
**	Copyright (c) 2016 Campbell Barton - https://github.com/ideasman42
**	Copyright (c) 2019 luzpaz - https://github.com/luzpaz
**
**	Copyright (c) 2019 - 2020, Ankit Kumar Dwivedi - https://github.com/ankit-kumar-dwivedi
**
**	LICENSE
** 
**	BSD 3-Clause "New" or "Revised" License
** 
**	Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
**
**	1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
**
**	2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
**
**	3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
**
**	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**	\endlegal
*/
/* ========================================================================= */

/* === H E A D E R S ======================================================= */

#include "polygonizerclasses.h"
#include <synfig/valuenodes/valuenode_bline.h>
#include <synfig/surface.h>
#include <synfig/rendering/software/surfacesw.h>

#include <synfig/blinepoint.h>
#include <synfig/layer.h>
#include <synfig/canvas.h>
#include <synfig/valuenode.h>


/* === U S I N G =========================================================== */

using namespace etl;
using namespace studio;

/* === M A C R O S ========================================================= */

/* === G L O B A L S ======================================================= */
const double Polyg_eps_max = 1;     // Sequence simplification max error
const double Polyg_eps_mul = 0.75;  // Sequence simple thickness-multiplier error
const double Quad_eps_max =  infinity;  // As above, for sequence conversion into strokes
synfig::Point bottomleft(0,0);
bool max_thickness_zero = false;
synfig::Canvas::Handle canvas;
float unit_size;
float h_factor = 1;
float w_factor = 1;
/* === P R O C E D U R E S ================================================= */

// this function will be responsible for unit conversion and height, width transformation
void PreProcessSegment(studio::PointList &segment)
{
  int size = segment.size();
  // unit_size is for pixel to synfig unit conversion 
  // w_factor and h_factor is scaling factors due to image layer TL and BR movement 
  // multiplier is for handling custom canvas settings
  // bottomleft[0] and bottomleft[1] is used to shift the image from only positive to negative - positive
  float multiplier = unit_size/60.0;
  for (int i = 0; i < size; ++i)
  {
    segment[i][0] = w_factor *( multiplier * segment[i][0]/unit_size ) + bottomleft[0];
    segment[i][1] = h_factor *( multiplier * segment[i][1]/unit_size ) + bottomleft[1];
    segment[i][2] = (segment[i][2]/2.5)*std::max(w_factor,h_factor);
  }
  
}


etl::handle<synfig::Layer> BezierToOutline(studio::PointList segment)
{
  int segment_size = segment.size();
  synfig::Layer::Handle layer(synfig::Layer::create("outline"));
  std::vector<synfig::BLinePoint> bline_point_list; 
  
  PreProcessSegment(segment);

  if(max_thickness_zero)
    for(int i = 0; i < segment_size; ++i)
      segment[i][2] = 1.0;
     
  int size, flag, num, point;

  switch (segment_size) // in any case size>=3
  {
    case 3:{ //std::cout<<"This is case 3\n";
      size = 2;
      flag = 0;
    }break;

    case 4:{
      size = 3;
      flag = 0;
    }break;

    default:{ /*Odd : 1 2 3 , 3 4 5, 5 6 7, 7 8 9
                  Even : 1 2 3 4, 4 5 6, 6 7 8 */
      num = 0, point = 0;
      if (segment_size & 1)
      {
        size = 2;
        flag = 1;
        num = 2; // represent segment index used
      }
      else
      {
        size = 3;
        flag = 1;
        num = 3; // represent segment index used
      }
    }break;

  }

  bline_point_list.push_back(synfig::BLinePoint());
  bline_point_list.push_back(synfig::BLinePoint());
  bline_point_list[0].set_vertex(segment[0].to_2d());
  bline_point_list[1].set_vertex(segment[size].to_2d());
  bline_point_list[0].set_tangent((segment[1].to_2d() - segment[0].to_2d()) * 2);

  if (flag == 0)
    bline_point_list[1].set_tangent((segment[size].to_2d() - segment[size - 1].to_2d()) * 2);
  else if(flag == 1)
    bline_point_list[1].set_tangent1((segment[size].to_2d() - segment[size - 1].to_2d()) * 2);

  bline_point_list[0].set_width(segment[0][2]);
  bline_point_list[1].set_width(segment[size][2]);

  if(flag == 1)
  {
    for (/*num, */point = 2; num < segment_size - 3; point++, num += 2)
    {
      bline_point_list.push_back(synfig::BLinePoint());
      bline_point_list[point].set_vertex(segment[num + 2].to_2d()); // last point
      bline_point_list[point].set_width(segment[num + 2][2]);       // last point
      bline_point_list[point - 1].set_tangent2((segment[num + 1].to_2d() - segment[num].to_2d()) * 2);
      bline_point_list[point].set_tangent1((segment[num + 2].to_2d() - segment[num + 1].to_2d()) * 2);
    }

    // add last blinepoint and set it's param
    bline_point_list.push_back(synfig::BLinePoint());
    bline_point_list.back().set_vertex(segment[segment_size - 1].to_2d());
    bline_point_list.back().set_width(segment[segment_size - 1][2]);
    bline_point_list.back().set_tangent((segment[segment_size - 1].to_2d() - segment[segment_size - 2].to_2d()) * 2);
  }
  
  etl::handle<synfig::ValueNode_BLine> bline_value_node; 
  etl::handle<synfig::ValueNode_DynamicList> value_node;
  etl::handle<synfig::ValueNode> vn;

	vn=value_node=bline_value_node=synfig::ValueNode_BLine::create(bline_point_list, canvas);
  layer->connect_dynamic_param("bline",vn);
  layer->set_param("width",5.0/unit_size);
  return layer;
}

/* === M E T H O D S ======================================================= */
inline double distance2(const synfig::Point3 &P, const synfig::Point3 &v, const synfig::Point3 &B) 
{
  double t    = P * v - B * v;
  synfig::Point3 Q = B + (v * t) - P;

  return Q * Q;
}

inline double tdistance(synfig::Point3 P, synfig::Point3 v, synfig::Point3 B) {
  double vv = v.mag_squared();
  if (vv < 0.01) return -1;

  double t = (P * v - B * v) / vv;
  synfig::Point3 Q = B + v * t - P;

  return Q.mag();
}

/* === E N T R Y P O I N T ================================================= */
class SequenceSimplifier 
{
  const Sequence *m_s;
  const SkeletonGraph *m_graph;

private:
  class Length 
  {
  public:
    int n;
    double l;
    UINT firstNode, secondNode;

    Length() : n(0), l(0) {}
    Length(int n_, double l_) : n(n_), l(l_) {}

    inline void infty(void) 
    {
      n = infinity;
      l = infinity;
    }
    inline bool operator<(Length sl) 
    {
      return n < sl.n ? 1 : n > sl.n ? 0 : l < sl.l ? 1 : 0;
    }
    inline Length operator+(Length sl) { return Length(n + sl.n, l + sl.l); }
  };

  Length lengthOf(UINT a, UINT aLink, UINT b);

public:
  // Methods
  SequenceSimplifier(const Sequence *s) : m_s(s), m_graph(m_s->m_graphHolder) {}

  void simplify(std::vector<unsigned int> &result);
};

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

// Bellman algorithm for Sequences
// NOTE: Circular Sequences are dealt.
void SequenceSimplifier::simplify(std::vector<unsigned int> &result) {
  // Initialize variables
  unsigned int n;
  unsigned int i, j, iLink, jLink;

  // NOTE: If s is circular, we have to protect

  i     = m_s->m_head;
  iLink = m_s->m_headLink;
  // NOTE: If m_head==m_tail then we have to force the first step by "|| n==1"
  for (n = 1; i != m_s->m_tail || n == 1; ++n, m_s->next(i, iLink))
    ;

  Length L_att, L_min, l_min, l_ji;
  unsigned int p_i, a, b;

  std::vector<Length> M(n);
  std::vector<Length> K(n);
  std::vector<unsigned int> P(n);

  // Search for minimal path
  i     = m_s->m_head;
  iLink = m_s->m_headLink;
  for (a = 1; i != m_s->m_tail || a == 1; m_s->next(i, iLink), ++a) 
  {
    L_min.infty();
    l_min.infty();
    p_i = 0;

    j                  = m_s->m_head;
    jLink              = m_s->m_headLink;
    unsigned int iNext = m_graph->getNode(i).getLink(iLink).getNext();
    for (b = 0; j != iNext || b == 0; m_s->next(j, jLink), ++b) 
    {
      if ((L_att = M[b] + (l_ji = lengthOf(j, jLink, iNext))) < L_min) 
      {
        L_min = L_att;
        p_i   = b;
        l_min = l_ji;
      }
    }
    M[a] = L_min;
    K[a] = l_min;
    P[a] = p_i;
  }

  // Copies minimal path found to the output reducedIndices vector
  // NOTE: size() is added due to circular sequences case handling
  unsigned int redSize = result.size();

  result.resize(redSize + M[n - 1].n + 1);

  result[redSize + M[n - 1].n] = K[n - 1].secondNode;
  for (b = n - 1, a = redSize + M[n - 1].n - 1; b > 0; b = P[b], --a)
    result[a] = K[b].firstNode;
}

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

// Length between two sequence points
SequenceSimplifier::Length SequenceSimplifier::lengthOf(UINT a, UINT aLink, UINT b) 
{
  UINT curr, old;
  synfig::Point3 v;
  double d, vv;
  Length res;

  res.n          = 1;
  res.firstNode  = a;
  res.secondNode = b;

  v  = *m_graph->getNode(b) - *m_graph->getNode(a);
  vv = v.mag();

  curr = m_graph->getNode(a).getLink(aLink).getNext();
  old  = a;

  // If the distance between extremities is small, check if the same holds
  // for internal points; if so, ok - otherwise set infty().
  if (vv < 0.1) 
  {
    for (; curr != b; m_s->advance(old, curr)) 
    {
      d = (*m_graph->getNode(curr) - *m_graph->getNode(a)).mag();// distance btw points
      if (d > 0.1) res.infty();
    }
    return res;
  }

  // Otherwise, check distances from line passing from a and b
  v = v * (1 / vv);

  for (; curr != b; m_s->advance(old, curr)) 
  {
    d = distance2(*m_graph->getNode(curr), v, *m_graph->getNode(a));
    if (d > std::min(m_graph->getNode(curr)->operator[](2) * Polyg_eps_mul, Polyg_eps_max)) 
    {
      res.infty();
      return res;
    } 
    else
      res.l += d;
  }

  return res;
}

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

//===============================
//      Sequence conversion
//===============================

// EXPLANATION: Sequences convert into Outline by applying a SequenceConverter
//  class. A graph minimal-path algorithm is run by using a
//  lexicographic-ordered
//  (number of quadratics, error) length.

class SequenceConverter 
{
  const Sequence *m_s;
  const SkeletonGraph *m_graph;

  double m_penalty;

public:
  // Length construction globals (see 'lengthOf' method)
  unsigned int middle;
  std::vector<double> pars;

  class Length 
  {
  public:
    int n;
    double l;
    std::vector<synfig::Point3> CPs;

    Length() : n(0), l(0) {}
    Length(int n_, double l_) : n(n_), l(l_) {}

    inline void infty(void) 
    {
      n = infinity;
      l = infinity;
    }

    inline bool operator<(Length sl) 
    {
      return n < sl.n ? 1 : n > sl.n ? 0 : l < sl.l ? 1 : 0;
    }

    inline Length operator+(Length sl) { return Length(n + sl.n, l + sl.l); }

    void set_CPs(const synfig::Point3 &a, const synfig::Point3 &b, const synfig::Point3 &c) 
    {
      CPs.resize(3);
      CPs[0] = a;
      CPs[1] = b;
      CPs[2] = c;
    }

    void set_CPs(const synfig::Point3 &a, const synfig::Point3 &b, const synfig::Point3 &c,
                 const synfig::Point3 &d, const synfig::Point3 &e) 
    {
      CPs.resize(5);
      CPs[0] = a;
      CPs[1] = b;
      CPs[2] = c;
      CPs[3] = d;
      CPs[4] = e;
    }
  };

  // Intermediate Sequence form
  std::vector<synfig::Point3> middleAddedSequence;
  std::vector<unsigned int> *inputIndices;

  // Methods
  SequenceConverter(const Sequence *s, double penalty)
      : m_s(s), m_graph(m_s->m_graphHolder), m_penalty(penalty) {}

  Length lengthOf(unsigned int a, unsigned int b);
  void addMiddlePoints();
  etl::handle<synfig::Layer> operator()(std::vector<unsigned int> *indices);

  // Length construction methods
  bool parametrize(unsigned int a, unsigned int b);
  void lengthOfTriplet(unsigned int i, Length &len);
  bool calculateCPs(unsigned int i, unsigned int j, Length &len);
  bool penalty(unsigned int a, unsigned int b, Length &len);
};

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

// Changes in stroke thickness are considered more penalizating
inline double ellProd(const synfig::Point3 &a, const synfig::Point3 &b) {
  return a[0] * b[0] + a[1] * b[1] + 5 * a[2] * b[2];
}

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

// EXPLANATION:  After simplification, we receive a vector<UINT> of indices
// corresponding to the vertices of the simplified current sequence.
// Before beginning conversion, we need to add middle points between the
// above vertex points.

inline void SequenceConverter::addMiddlePoints() 
{
  unsigned int i, j, n;

  n = inputIndices->size();
  middleAddedSequence.clear();

  if (n == 2) 
  {
    middleAddedSequence.resize(3);
    middleAddedSequence[0] = *m_graph->getNode((*inputIndices)[0]);
    middleAddedSequence[1] = (*m_graph->getNode((*inputIndices)[0]) +
                              *m_graph->getNode((*inputIndices)[1])) * 0.5;
    middleAddedSequence[2] = *m_graph->getNode((*inputIndices)[1]);
  } 
  else 
  {
    middleAddedSequence.resize(2 * n - 3);
    middleAddedSequence[0] = *m_graph->getNode((*inputIndices)[0]);
    
    for (i = j = 1; i < n - 2; ++i, j += 2) 
    {
      middleAddedSequence[j]     = *m_graph->getNode((*inputIndices)[i]);
      middleAddedSequence[j + 1] = (*m_graph->getNode((*inputIndices)[i]) +
                                    *m_graph->getNode((*inputIndices)[i + 1])) * 0.5;
    }
    middleAddedSequence[j]     = *m_graph->getNode((*inputIndices)[n - 2]);
    middleAddedSequence[j + 1] = *m_graph->getNode((*inputIndices)[n - 1]);
  }
}

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

etl::handle<synfig::Layer> SequenceConverter::operator()(std::vector<unsigned int> *indices) {
  // Prepare Sequence
  inputIndices = indices;
  addMiddlePoints();

  // Initialize local variables
  unsigned int n = (middleAddedSequence.size() + 1) / 2;  // Number of middle points

  unsigned int i;
  int j;

  Length L_att, L_min, l_min, l_ji;
  unsigned int p_i, a, b;

  std::vector<Length> M(n);
  std::vector<Length> K(n);
  std::vector<unsigned int> P(n);

  // Bellman algorithm
  for (i = 2, a = 1; i < middleAddedSequence.size(); i += 2, ++a) 
  {
    L_min.infty();
    l_min.infty();
    p_i = 0;

    for (j = i - 2, b = j / 2; j >= 0; j -= 2, --b) 
    {
      if ((L_att = M[b] + (l_ji = lengthOf(j, i))) < L_min) 
      {
        L_min = L_att;
        p_i   = b;
        l_min = l_ji;
      }
      // NOTE: The following else may be taken out to perform a deeper
      // search for optimal result. However, it prevents quadratic complexities
      // on large-scale images.
      else if (l_ji.n == infinity)
        break;  // Stops searching for current i
    }
    M[a] = L_min;
    K[a] = l_min;
    P[a] = p_i;
  }

  // Read off the output
  std::vector<synfig::Point3> controlPoints(2 * M[n - 1].n + 1);

  for (b = n - 1, a = 2 * M[n - 1].n; b > 0; b = P[b]) 
  {
    for (i = K[b].CPs.size() - 1; i > 0; --i, --a)
      controlPoints[a] = K[b].CPs[i];
  }
  controlPoints[0] = middleAddedSequence[0];
  
  etl::handle<synfig::Layer> res = BezierToOutline(controlPoints);

  return res;
}

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

//--------------------------------------
//      Conversion Length build-up
//--------------------------------------

SequenceConverter::Length SequenceConverter::lengthOf(unsigned int a, unsigned int b) 
{
  Length l;

  // If we have a triplet, apply a specific procedure
  if (b == a + 2) 
  {
    lengthOfTriplet(a, l);
    return l;
  }
  // otherwise
  if (!parametrize(a, b) || !calculateCPs(a, b, l) || !penalty(a, b, l))
    l.infty();
  return l;
}

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

void SequenceConverter::lengthOfTriplet(unsigned int i, Length &len) 
{
  synfig::Point3 A = middleAddedSequence[i];
  synfig::Point3 B = middleAddedSequence[i + 1];
  synfig::Point3 C = middleAddedSequence[i + 2];

  // We assume that this conversion is faithful, avoiding length penalty
  len.l = 0;
  double d = tdistance(B, C - A, A);
  
  if (d <= 2) 
  {
    len.n = 1;
    len.set_CPs(A, B, C);
  } 
  else if (d <= 6) 
  {
    len.n       = 2;
    d           = (d - 1) / d;
    synfig::Point3 U = A + (B - A) * d, V = C + (B - C) * d;
    len.set_CPs(A, U, (U + V) * 0.5, V, C);
  } 
  else 
  {
    len.n = 2;
    len.set_CPs(A, (A + B) * 0.5, B, (B + C) * 0.5, C);
  }
}

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

bool SequenceConverter::parametrize(unsigned int a, unsigned int b) 
{
  unsigned int curr, old;
  unsigned int i;
  double w, t;
  double den;

  pars.clear();
  pars.push_back(0);

  for (old = a, curr = a + 1, den = 0; curr < b; old = curr, curr += 2) 
  {
    w = (middleAddedSequence[curr] - middleAddedSequence[old]).mag();
    den += w;
    pars.push_back(w);
  }
  w = (middleAddedSequence[b] - middleAddedSequence[old]).mag();
  den += w;
  pars.push_back(w);

  if (den < 0.1) return 0;

  for (i = 1, t = 0; i < pars.size(); ++i) 
  {
    t += 2 * pars[i] / den;
    pars[i] = t;
  }

  // Seek the interval which holds 1 - the middle interval
  for (middle = 0; middle < pars.size() && pars[middle + 1] <= 1; ++middle)
    ;

  return 1;
}

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

//------------------------
//    CP construction
//------------------------

// NOTE: Check my thesis for variable meanings (int_ stands for 'integral').

// Some integrals (int_) for the CP linear system resolution

inline synfig::Point3 int_H(const synfig::Point3 &A, const synfig::Point3 &B, double t1, double t2) 
{
  return  B * -(0.375 * (pow(t2, 4) - pow(t1, 4))) +
         (B * 0.6667 - A * 0.5) * (pow(t2, 3) - pow(t1, 3)) +
         A * (pow(t2, 2) - pow(t1, 2));
}

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

inline synfig::Point3 int_K(const synfig::Point3 &A, const synfig::Point3 &B, double t1, double t2) 
{
  return (B * 0.125) * (pow(t2, 4) - pow(t1, 4)) +
         (A * 0.1667) * (pow(t2, 3) - pow(t1, 3));
}

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

bool SequenceConverter::calculateCPs(unsigned int i, unsigned int j, Length &len) 
{
  unsigned int curr, old;

  synfig::Matrix M;
  synfig::Point l;
  synfig::Point3 a, e, x, y, A, B;
  synfig::Point3 IH, IK, IM, IN_;  //"IN" seems to be reserved word
  double HxL, KyL, MxO, NyO;
  unsigned int k;
  a = middleAddedSequence[i];
  e = middleAddedSequence[j];
  x = middleAddedSequence[i + 1] - a;
  y = middleAddedSequence[j - 1] - e;

  // Build TAffine M
  double par = ellProd(x, y) / 5;
  M = synfig::Matrix( synfig::Point(ellProd(x, x)/3, par), synfig::Point(par, ellProd(y, y)/3),synfig::Point() );

   // Integral from 0.0 to 1.0
  for (k = 0, old = i, curr = i + 1; k < middle; ++k, old = curr, curr += 2) 
  {
    B = (middleAddedSequence[curr] - middleAddedSequence[old]) *
        (1 / (pars[k + 1] - pars[k]));
    A = middleAddedSequence[old] - B * pars[k];
    IH += int_H(A, B, pars[k], pars[k + 1]);
    IK += int_K(A, B, pars[k], pars[k + 1]);
  }

  if (curr == j + 1) curr = j;
  B = (middleAddedSequence[curr] - middleAddedSequence[old]) *
      (1 / (pars[k + 1] - pars[k]));
  A = middleAddedSequence[old] - B * pars[k];
  IH += int_H(A, B, pars[k], 1.0);
  IK += int_K(A, B, pars[k], 1.0);

  // Integral from 1.0 to 2.0
  for (k = pars.size() - 1, old = j, curr = j - 1; k > middle + 1;
       --k, old = curr, curr -= 2) {
    B = (middleAddedSequence[curr] - middleAddedSequence[old]) *
        (1 / (pars[k] - pars[k - 1]));
    A = middleAddedSequence[curr] - B * (2 - pars[k - 1]);
    IM += int_K(A, B, 2 - pars[k], 2 - pars[k - 1]);
    IN_ += int_H(A, B, 2 - pars[k], 2 - pars[k - 1]);
  }

  if (old == i + 1) curr = i;
  B = (middleAddedSequence[curr] - middleAddedSequence[old]) *
      (1 / (pars[k] - pars[k - 1]));
  A = middleAddedSequence[curr] - B * (2 - pars[k - 1]);
  IM += int_K(A, B, 2 - pars[k], 1.0);
  IN_ += int_H(A, B, 2 - pars[k], 1.0);

  // Polygonal-free integrals
  synfig::Point3 f = (a + e) * 0.5;
  HxL         = (ellProd(a, x) * 0.3) + (ellProd(f, x) / 5.0);
  NyO         = (ellProd(e, y) * 0.3) + (ellProd(f, y) / 5.0);
  KyL         = (ellProd(a, y) / 15.0) + (ellProd(f, y) / 10.0);
  MxO         = ((e * x) / 15.0) + (ellProd(f, x) / 10.0);

  // Infine, ho il termine noto
  l = synfig::Point(ellProd(IH, x) - HxL + ellProd(IM, x) - MxO,
              ellProd(IK, y) - KyL + ellProd(IN_, y) - NyO);
  M.m20 = -l[0];
  M.m21 = -l[1];

  // Check validity conditions:
  //  a) System is not singular
  if (fabs(M.det()) < 0.01) return 0;

  M.invert();

  //  b) Shift (solution) is positive
  if (M.m20 < 0 || M.m21 < 0) return 0;
  synfig::Point3 b = a + x * M.m20;
  synfig::Point3 d = e + y * M.m21;

  //  c) The height of every CP must be >=0
  if (b[2] < 0 || d[2] < 0) return 0;
  len.set_CPs(a, b, (b + d) * 0.5, d, e);

  return 1;
}

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

//------------------------
//      Penalties
//------------------------

inline synfig::Point3 int_B0a(const synfig::Point3 &A, const synfig::Point3 &B, double t1,
                         double t2) {
  return B * (0.25 * (pow(t2, 4) - pow(t1, 4))) +
         (A - B * 2.0) * ((pow(t2, 3) - pow(t1, 3)) / 3.0) +
         (B - A * 2.0) * (0.5 * (pow(t2, 2) - pow(t1, 2))) + A * (t2 - t1);
}

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

inline synfig::Point3 int_B1a(const synfig::Point3 &A, const synfig::Point3 &B, double t1,
                         double t2) {
return B * -(0.5 * (pow(t2, 4) - pow(t1, 4))) + 
          ((B - A) * 2.0 * ((pow(t2, 3) - pow(t1, 3)) / 3.0) +
          A * (pow(t2, 2) - pow(t1, 2)));
}

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

inline synfig::Point3 int_B2a(const synfig::Point3 &A, const synfig::Point3 &B, double t1,
                         double t2) {
  return B * (0.25 * (pow(t2, 4) - pow(t1, 4))) +
         A * ((pow(t2, 3) - pow(t1, 3)) / 3.0);
}

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

inline double int_a2(const synfig::Point3 &A, const synfig::Point3 &B, double t1,
                     double t2) {
  return ellProd(A, A) * (t2 - t1) + ellProd(A, B) * (pow(t2, 2) - pow(t1, 2)) +
         (ellProd(B, B) * (pow(t2, 3) - pow(t1, 3)) / 3.0);
}

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

// Penalty is the integral of the square norm of differences between polygonal
// and quadratics.
bool SequenceConverter::penalty(unsigned int a, unsigned int b, Length &len) 
{
  unsigned int curr, old;

  const std::vector<synfig::Point3> &CPs = len.CPs;
  synfig::Point3 A, B, P0, P1, P2;
  double p, p_max;
  unsigned int k;

  len.n = 2;  // A couple of arcs

  // Prepare max penalty p_max
  p_max = 0;
  for (curr = a + 1, old = a, k = 0; curr < b; ++k, old = curr, curr += 2) 
  {
    p_max += (middleAddedSequence[curr][2] + middleAddedSequence[old][2]) *
             (pars[k + 1] - pars[k]) / 2;
  }
  p_max += (middleAddedSequence[b][2] + middleAddedSequence[old][2]) *
           (pars[k + 1] - pars[k]) / 2;

  // Confronting 4th power of error with mean polygonal thickness
  // - can be changed
  p_max = std::min(sqrt(p_max) * m_penalty, Quad_eps_max);

  // CP only integral
  p = (ellProd(CPs[0], CPs[0]) + 2 * ellProd(CPs[2], CPs[2]) +
       ellProd(CPs[4], CPs[4]) + ellProd(CPs[0], CPs[1]) +
       ellProd(CPs[1], CPs[2]) + ellProd(CPs[2], CPs[3]) +
       ellProd(CPs[3], CPs[4])) /
          5.0 +
      (2 * (ellProd(CPs[1], CPs[1]) + ellProd(CPs[3], CPs[3])) +
       ellProd(CPs[0], CPs[2]) + ellProd(CPs[2], CPs[4])) /
          15.0;

  // Penalty from 0.0 to 1.0
  P0 = P1 = P2 = synfig::Point3();
  for (k = 0, old = a, curr = a + 1; k < middle; ++k, old = curr, curr += 2) 
  {
    B = (middleAddedSequence[curr] - middleAddedSequence[old]) *
        (1 / (pars[k + 1] - pars[k]));
    A = middleAddedSequence[old] - B * pars[k];

    // Mixed integral
    P0 += int_B0a(A, B, pars[k], pars[k + 1]);
    P1 += int_B1a(A, B, pars[k], pars[k + 1]);
    P2 += int_B2a(A, B, pars[k], pars[k + 1]);

    // Sequence integral
    p += int_a2(A, B, pars[k], pars[k + 1]);
  }

  if (curr == b + 1) curr = b;
  B = (middleAddedSequence[curr] - middleAddedSequence[old]) *
      (1 / (pars[k + 1] - pars[k]));
  A = middleAddedSequence[old] - B * pars[k];

  // Mixed integral
  P0 += int_B0a(A, B, pars[k], 1.0);
  P1 += int_B1a(A, B, pars[k], 1.0);
  P2 += int_B2a(A, B, pars[k], 1.0);

  // Sequence integral
  p += int_a2(A, B, pars[k], 1.0);

  p -= 2 * (ellProd(P0, CPs[0]) + ellProd(P1, CPs[1]) + ellProd(P2, CPs[2]));

  // Penalty from 1.0 to 2.0
  P0 = P1 = P2 = synfig::Point3();
  for (k = pars.size() - 1, old = b, curr = b - 1; k > middle + 1; --k, old = curr, curr -= 2) 
  {
    B = (middleAddedSequence[curr] - middleAddedSequence[old]) *
        (1 / (pars[k] - pars[k - 1]));
    A = middleAddedSequence[curr] - B * (2 - pars[k - 1]);

    // Mixed integral
    P0 += int_B0a(A, B, 2 - pars[k], 2 - pars[k - 1]);
    P1 += int_B1a(A, B, 2 - pars[k], 2 - pars[k - 1]);
    P2 += int_B2a(A, B, 2 - pars[k], 2 - pars[k - 1]);

    // Sequence integral
    p += int_a2(A, B, 2 - pars[k], 2 - pars[k - 1]);
  }
  
  if (old == a + 1) curr = a;
  B = (middleAddedSequence[curr] - middleAddedSequence[old]) *
      (1 / (pars[k] - pars[k - 1]));
  A = middleAddedSequence[curr] - B * (2 - pars[k - 1]);

  // Mixed integral
  P0 += int_B0a(A, B, 2 - pars[k], 1.0);
  P1 += int_B1a(A, B, 2 - pars[k], 1.0);
  P2 += int_B2a(A, B, 2 - pars[k], 1.0);

  // Sequence integral
  p += int_a2(A, B, 2 - pars[k], 1.0);

  p -= 2 * (ellProd(P0, CPs[4]) + ellProd(P1, CPs[3]) + ellProd(P2, CPs[2]));

  // OCCHIO! Ho visto ancora qualche p<0! Da rivedere - non dovrebbe...
  if (p > p_max || p < 0)
    return 0;
  else
    len.l = p;

  return 1;
}

//--------------------------------------------------------------------------
inline etl::handle<synfig::Layer> convert(const Sequence &s, double penalty) 
{
  SkeletonGraph *graph = s.m_graphHolder;

  etl::handle<synfig::Layer> result;

  // First, we simplify the skeleton sequences found
  std::vector<unsigned int> reducedIndices;

  // NOTE: If s is circular, we have to protect head==tail 's adjacent nodes.
  // We then move away s tail and head, and insert them in the reducedIndices
  // apart from simplification.
  if (s.m_head == s.m_tail && graph->getNode(s.m_head).degree() == 2) 
  {
    Sequence t = s;

    SequenceSimplifier simplifier(&t);
    reducedIndices.push_back(s.m_head);

    t.m_head     = graph->getNode(s.m_head).getLink(0).getNext();
    t.m_headLink = !graph->getNode(t.m_head).linkOfNode(s.m_head);
    t.m_tail     = graph->getNode(s.m_tail).getLink(1).getNext();
    t.m_tailLink = !graph->getNode(t.m_tail).linkOfNode(s.m_tail);

    simplifier.simplify(reducedIndices);
    reducedIndices.push_back(s.m_tail);
  } 
  else 
  {
    SequenceSimplifier simplifier(&s);
    simplifier.simplify(reducedIndices);
  }

  // For segments, apply this immediate conversion
  if (reducedIndices.size() == 2) 
  {
    std::vector<synfig::Point3> segment(3);
    segment[0] = *graph->getNode(s.m_head);
    segment[1] = (*graph->getNode(s.m_head) + *graph->getNode(s.m_tail)) * 0.5;
    segment[2] = *graph->getNode(s.m_tail);
    
    return BezierToOutline(segment);
  }
  // when calculating sequence with 3 thick points where x,y are coordinates and z is thickness of stroke
  // it then build quadratic chunk using the three control points

  // Then, we convert the sequence in a quadratic stroke
  SequenceConverter converter(&s, penalty);
  result = converter(&reducedIndices);

  return result;
}

// Converts each forward or single Sequence of the image in its corresponding
// Stroke. 
// In synfig we will be using outline layer instead of TStroke  

void studio::conversionToStrokes(std::vector< etl::handle<synfig::Layer> > &strokes, VectorizerCoreGlobals &g,const etl::handle<synfig::Layer_Bitmap> &image) 
{
  SequenceList &singleSequences           = g.singleSequences;
  JointSequenceGraphList &organizedGraphs = g.organizedGraphs;
  double penalty                          = g.currConfig->m_penalty;
  max_thickness_zero                      = !g.currConfig->m_maxThickness; // if any value then false otherwise 0 then true
  unsigned int i, j, k;

  synfig::Point topleft = image->param_tl.get(synfig::Point());
  synfig::Point bottomright = image->param_br.get(synfig::Point());
  bottomleft[0] = topleft[0];
  bottomleft[1] = bottomright[1];

  canvas = image->get_canvas();
  synfig::rendering::SurfaceResource::LockRead<synfig::rendering::SurfaceSW> lock( image->rendering_surface );
	const synfig::Surface &surface = lock->get_surface(); 
  
  synfig::Point q = canvas->rend_desc().get_br() - canvas->rend_desc().get_tl();
  float p = canvas->rend_desc().get_w();
  // 1 unit = p/q[0] pixels
  unit_size = p/q[0];

  // here scaling factors are calculated
  h_factor = ((topleft[1] - bottomright[1]) * unit_size)/(surface.get_h());
  w_factor = ((bottomright[0] - topleft[0]) * unit_size)/(surface.get_w());


  for (i = 0; i < singleSequences.size(); ++i) 
  {
    if (singleSequences[i].m_head == singleSequences[i].m_tail) 
    {
      // If the sequence is circular, move your endpoints to an edge middle, in
      // order to allow a soft junction
      SkeletonGraph *currGraph = singleSequences[i].m_graphHolder;

      unsigned int head     = singleSequences[i].m_head;
      unsigned int headLink = singleSequences[i].m_headLink;
      unsigned int next = currGraph->getNode(head).getLink(headLink).getNext();
      unsigned int nextLink = currGraph->getNode(next).linkOfNode(head);

      unsigned int addedNode = singleSequences[i].m_graphHolder->newNode(
          (*currGraph->getNode(head) + *currGraph->getNode(next)) * 0.5);

      singleSequences[i].m_graphHolder->insert(addedNode, head, headLink);
      *singleSequences[i].m_graphHolder->node(addedNode).link(0) =
          *singleSequences[i].m_graphHolder->node(head).link(headLink);

      singleSequences[i].m_graphHolder->insert(addedNode, next, nextLink);
      *singleSequences[i].m_graphHolder->node(addedNode).link(1) =
          *singleSequences[i].m_graphHolder->node(next).link(nextLink);

      singleSequences[i].m_head     = addedNode;
      singleSequences[i].m_headLink = 0;
      singleSequences[i].m_tail     = addedNode;
      singleSequences[i].m_tailLink = 1;
    }

    strokes.push_back(convert(singleSequences[i], penalty));
  }

  // Convert graph sequences
  for (i = 0; i < organizedGraphs.size(); ++i)
    for (j = 0; j < organizedGraphs[i].getNodesCount(); ++j)
      if (!organizedGraphs[i].getNode(j).hasAttribute(
              JointSequenceGraph::ELIMINATED))
        // Otherwise eliminated by junction recovery
        for (k = 0; k < organizedGraphs[i].getNode(j).getLinksCount(); ++k) {
          // A sequence is taken at both extremities in our organized graphs
          if (organizedGraphs[i].getNode(j).getLink(k)->isForward())
            strokes.push_back(
                convert(*organizedGraphs[i].getNode(j).getLink(k), penalty));
        }
}
