// SPDX-License-Identifier: GPL-2.0-or-later
/** \file
 * LPE "Ellipse through 5 points" implementation
 */

/*
 * Authors:
 *   Theodore Janeczko
 *
 * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#include "live_effects/lpe-ellipse_5pts.h"
#include <2geom/circle.h>
#include <2geom/ellipse.h>
#include <2geom/path-sink.h>
#include "inkscape.h"
#include "desktop.h"
#include "message-stack.h"
// TODO due to internal breakage in glibmm headers, this must be last:
#include <glibmm/i18n.h>

namespace Inkscape {
namespace LivePathEffect {

LPEEllipse5Pts::LPEEllipse5Pts(LivePathEffectObject *lpeobject) :
    Effect(lpeobject)
{
    //perceived_path = true;
}

LPEEllipse5Pts::~LPEEllipse5Pts()
= default;

static double _det3(double (*mat)[3])
{
    for (int i = 0; i < 2; i++)
    {
        for (int j = i + 1; j < 3; j++)
        {
            for (int k = i + 1; k < 3; k++)
            {
                mat[j][k] = (mat[j][k] * mat[i][i] - mat[j][i] * mat[i][k]);
                if (i) mat[j][k] /= mat[i-1][i-1];
            }
        }
    }
    return mat[2][2];
}
static double _det5(double (*mat)[5])
{
    for (int i = 0; i < 4; i++)
    {
        for (int j = i + 1; j < 5; j++)
        {
            for (int k = i + 1; k < 5; k++)
            {
                mat[j][k] = (mat[j][k] * mat[i][i] - mat[j][i] * mat[i][k]);
                if (i) mat[j][k] /= mat[i-1][i-1];
            }
        }
    }
    return mat[4][4];
}

Geom::PathVector
LPEEllipse5Pts::doEffect_path (Geom::PathVector const & path_in)
{
    Geom::PathVector path_out = Geom::PathVector();

    if (path_in[0].size() < 4) {
        
        SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Five points required for constructing an ellipse"));
        return path_in;
    }
    // we assume that the path has >= 3 nodes
    Geom::Point A = path_in[0].initialPoint();
    Geom::Point B = path_in[0].pointAt(1);
    Geom::Point C = path_in[0].pointAt(2);
    Geom::Point D = path_in[0].pointAt(3);
    Geom::Point E = path_in[0].pointAt(4);

    using namespace Geom;

    double rowmajor_matrix[5][6] =
    {
        {A.x()*A.x(), A.x()*A.y(), A.y()*A.y(), A.x(), A.y(), 1},
        {B.x()*B.x(), B.x()*B.y(), B.y()*B.y(), B.x(), B.y(), 1},
        {C.x()*C.x(), C.x()*C.y(), C.y()*C.y(), C.x(), C.y(), 1},
        {D.x()*D.x(), D.x()*D.y(), D.y()*D.y(), D.x(), D.y(), 1},
        {E.x()*E.x(), E.x()*E.y(), E.y()*E.y(), E.x(), E.y(), 1}
    };
    
    double mat_a[5][5] =
    {
        {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]},
        {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]},
        {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]},
        {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]},
        {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]}
    };
    double mat_b[5][5] =
    {
        {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]},
        {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]},
        {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]},
        {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]},
        {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]}
    };
    double mat_c[5][5] =
    {
        {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]},
        {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]},
        {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]},
        {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]},
        {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]}
    };
    double mat_d[5][5] =
    {
        {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]},
        {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]},
        {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]},
        {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]},
        {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]}
    };
    double mat_e[5][5] =
    {
        {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]},
        {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]},
        {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]},
        {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]},
        {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]}
    };
    double mat_f[5][5] =
    {
        {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]},
        {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]},
        {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]},
        {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]},
        {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]}
    };
    
    double a1 = _det5(mat_a);
    double b1 = -_det5(mat_b);
    double c1 = _det5(mat_c);
    double d1 = -_det5(mat_d);
    double e1 = _det5(mat_e);
    double f1 = -_det5(mat_f);
    
    double mat_check[][3] = 
    {
        {a1, b1/2, d1/2},
        {b1/2, c1, e1/2},
        {d1/2, e1/2, f1}
    };
    
    if (_det3(mat_check) == 0 || a1*c1 - b1*b1/4 <= 0) {
        SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No ellipse found for specified points"));
        return path_in;
    }
    
    Geom::Ellipse el(a1, b1, c1, d1, e1, f1);
    
    double s, e;
    double x0, y0, x1, y1, x2, y2, x3, y3;
    double len;

    // figure out if we have a slice, guarding against rounding errors

    Geom::Path p(Geom::Point(cos(0), sin(0)));

    double end = 2 * M_PI;
    for (s = 0; s < end; s += M_PI_2) {
        e = s + M_PI_2;
        if (e > end)
            e = end;
        len = 4*tan((e - s)/4)/3;
        x0 = cos(s);
        y0 = sin(s);
        x1 = x0 + len * cos(s + M_PI_2);
        y1 = y0 + len * sin(s + M_PI_2);
        x3 = cos(e);
        y3 = sin(e);
        x2 = x3 + len * cos(e - M_PI_2);
        y2 = y3 + len * sin(e - M_PI_2);
        p.appendNew<Geom::CubicBezier>(Geom::Point(x1,y1), Geom::Point(x2,y2), Geom::Point(x3,y3));
    }
    
    Geom::Affine aff = Geom::Scale(el.rays()) * Geom::Rotate(el.rotationAngle()) * Geom::Translate(el.center());
    
    path_out.push_back(p * aff);

    return path_out;
}

/* ######################## */

} //namespace LivePathEffect
} /* namespace Inkscape */

/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
