summaryrefslogtreecommitdiffstats
path: root/kig/misc/cubic-common.cc
diff options
context:
space:
mode:
Diffstat (limited to 'kig/misc/cubic-common.cc')
-rw-r--r--kig/misc/cubic-common.cc527
1 files changed, 527 insertions, 0 deletions
diff --git a/kig/misc/cubic-common.cc b/kig/misc/cubic-common.cc
new file mode 100644
index 00000000..029f1194
--- /dev/null
+++ b/kig/misc/cubic-common.cc
@@ -0,0 +1,527 @@
+// Copyright (C) 2003 Dominique Devriese <devriese@kde.org>
+
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301, USA.
+
+#include <config.h>
+
+#include "cubic-common.h"
+#include "kignumerics.h"
+#include "kigtransform.h"
+
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+
+/*
+ * coefficients of the cartesian equation for cubics
+ */
+
+CubicCartesianData::CubicCartesianData()
+{
+ std::fill( coeffs, coeffs + 10, 0 );
+}
+
+CubicCartesianData::CubicCartesianData(
+ const double incoeffs[10] )
+{
+ std::copy( incoeffs, incoeffs + 10, coeffs );
+}
+
+const CubicCartesianData calcCubicThroughPoints (
+ const std::vector<Coordinate>& points )
+{
+ // points is a vector of at most 9 points through which the cubic is
+ // constrained.
+ // this routine should compute the coefficients in the cartesian equation
+ // they are defined up to a multiplicative factor.
+ // since we don't know (in advance) which one of them is nonzero, we
+ // simply keep all 10 parameters, obtaining a 9x10 linear system which
+ // we solve using gaussian elimination with complete pivoting
+ // If there are too few, then we choose some cool way to fill in the
+ // empty parts in the matrix according to the LinearConstraints
+ // given..
+
+ // 9 rows, 10 columns..
+ double row0[10];
+ double row1[10];
+ double row2[10];
+ double row3[10];
+ double row4[10];
+ double row5[10];
+ double row6[10];
+ double row7[10];
+ double row8[10];
+ double *matrix[9] = {row0, row1, row2, row3, row4, row5, row6, row7, row8};
+ double solution[10];
+ int scambio[10];
+
+ int numpoints = points.size();
+ int numconstraints = 9;
+
+ // fill in the matrix elements
+ for ( int i = 0; i < numpoints; ++i )
+ {
+ double xi = points[i].x;
+ double yi = points[i].y;
+ matrix[i][0] = 1.0;
+ matrix[i][1] = xi;
+ matrix[i][2] = yi;
+ matrix[i][3] = xi*xi;
+ matrix[i][4] = xi*yi;
+ matrix[i][5] = yi*yi;
+ matrix[i][6] = xi*xi*xi;
+ matrix[i][7] = xi*xi*yi;
+ matrix[i][8] = xi*yi*yi;
+ matrix[i][9] = yi*yi*yi;
+ }
+
+ for ( int i = 0; i < numconstraints; i++ )
+ {
+ if (numpoints >= 9) break; // don't add constraints if we have enough
+ for (int j = 0; j < 10; ++j) matrix[numpoints][j] = 0.0;
+ bool addedconstraint = true;
+ switch (i)
+ {
+ case 0:
+ matrix[numpoints][7] = 1.0;
+ matrix[numpoints][8] = -1.0;
+ break;
+ case 1:
+ matrix[numpoints][7] = 1.0;
+ break;
+ case 2:
+ matrix[numpoints][9] = 1.0;
+ break;
+ case 3:
+ matrix[numpoints][4] = 1.0;
+ break;
+ case 4:
+ matrix[numpoints][5] = 1.0;
+ break;
+ case 5:
+ matrix[numpoints][3] = 1.0;
+ break;
+ case 6:
+ matrix[numpoints][1] = 1.0;
+ break;
+
+ default:
+ addedconstraint = false;
+ break;
+ }
+
+ if (addedconstraint) ++numpoints;
+ }
+
+ if ( ! GaussianElimination( matrix, numpoints, 10, scambio ) )
+ return CubicCartesianData::invalidData();
+ // fine della fase di eliminazione
+ BackwardSubstitution( matrix, numpoints, 10, scambio, solution );
+
+ // now solution should contain the correct coefficients..
+ return CubicCartesianData( solution );
+}
+
+const CubicCartesianData calcCubicCuspThroughPoints (
+ const std::vector<Coordinate>& points )
+{
+ // points is a vector of at most 4 points through which the cubic is
+ // constrained. Moreover the cubic is required to have a cusp at the
+ // origin.
+
+ // 9 rows, 10 columns..
+ double row0[10];
+ double row1[10];
+ double row2[10];
+ double row3[10];
+ double row4[10];
+ double row5[10];
+ double row6[10];
+ double row7[10];
+ double row8[10];
+ double *matrix[9] = {row0, row1, row2, row3, row4, row5, row6, row7, row8};
+ double solution[10];
+ int scambio[10];
+
+ int numpoints = points.size();
+ int numconstraints = 9;
+
+ // fill in the matrix elements
+ for ( int i = 0; i < numpoints; ++i )
+ {
+ double xi = points[i].x;
+ double yi = points[i].y;
+ matrix[i][0] = 1.0;
+ matrix[i][1] = xi;
+ matrix[i][2] = yi;
+ matrix[i][3] = xi*xi;
+ matrix[i][4] = xi*yi;
+ matrix[i][5] = yi*yi;
+ matrix[i][6] = xi*xi*xi;
+ matrix[i][7] = xi*xi*yi;
+ matrix[i][8] = xi*yi*yi;
+ matrix[i][9] = yi*yi*yi;
+ }
+
+ for ( int i = 0; i < numconstraints; i++ )
+ {
+ if (numpoints >= 9) break; // don't add constraints if we have enough
+ for (int j = 0; j < 10; ++j) matrix[numpoints][j] = 0.0;
+ bool addedconstraint = true;
+ switch (i)
+ {
+ case 0:
+ matrix[numpoints][0] = 1.0; // through the origin
+ break;
+ case 1:
+ matrix[numpoints][1] = 1.0;
+ break;
+ case 2:
+ matrix[numpoints][2] = 1.0; // no first degree term
+ break;
+ case 3:
+ matrix[numpoints][3] = 1.0; // a011 (x^2 coeff) = 0
+ break;
+ case 4:
+ matrix[numpoints][4] = 1.0; // a012 (xy coeff) = 0
+ break;
+ case 5:
+ matrix[numpoints][7] = 1.0;
+ matrix[numpoints][8] = -1.0;
+ break;
+ case 6:
+ matrix[numpoints][7] = 1.0;
+ break;
+ case 7:
+ matrix[numpoints][9] = 1.0;
+ break;
+ case 8:
+ matrix[numpoints][6] = 1.0;
+ break;
+
+ default:
+ addedconstraint = false;
+ break;
+ }
+
+ if (addedconstraint) ++numpoints;
+ }
+
+ if ( ! GaussianElimination( matrix, numpoints, 10, scambio ) )
+ return CubicCartesianData::invalidData();
+ // fine della fase di eliminazione
+ BackwardSubstitution( matrix, numpoints, 10, scambio, solution );
+
+ // now solution should contain the correct coefficients..
+ return CubicCartesianData( solution );
+}
+
+const CubicCartesianData calcCubicNodeThroughPoints (
+ const std::vector<Coordinate>& points )
+{
+ // points is a vector of at most 6 points through which the cubic is
+ // constrained. Moreover the cubic is required to have a node at the
+ // origin.
+
+ // 9 rows, 10 columns..
+ double row0[10];
+ double row1[10];
+ double row2[10];
+ double row3[10];
+ double row4[10];
+ double row5[10];
+ double row6[10];
+ double row7[10];
+ double row8[10];
+ double *matrix[9] = {row0, row1, row2, row3, row4, row5, row6, row7, row8};
+ double solution[10];
+ int scambio[10];
+
+ int numpoints = points.size();
+ int numconstraints = 9;
+
+ // fill in the matrix elements
+ for ( int i = 0; i < numpoints; ++i )
+ {
+ double xi = points[i].x;
+ double yi = points[i].y;
+ matrix[i][0] = 1.0;
+ matrix[i][1] = xi;
+ matrix[i][2] = yi;
+ matrix[i][3] = xi*xi;
+ matrix[i][4] = xi*yi;
+ matrix[i][5] = yi*yi;
+ matrix[i][6] = xi*xi*xi;
+ matrix[i][7] = xi*xi*yi;
+ matrix[i][8] = xi*yi*yi;
+ matrix[i][9] = yi*yi*yi;
+ }
+
+ for ( int i = 0; i < numconstraints; i++ )
+ {
+ if (numpoints >= 9) break; // don't add constraints if we have enough
+ for (int j = 0; j < 10; ++j) matrix[numpoints][j] = 0.0;
+ bool addedconstraint = true;
+ switch (i)
+ {
+ case 0:
+ matrix[numpoints][0] = 1.0;
+ break;
+ case 1:
+ matrix[numpoints][1] = 1.0;
+ break;
+ case 2:
+ matrix[numpoints][2] = 1.0;
+ break;
+ case 3:
+ matrix[numpoints][7] = 1.0;
+ matrix[numpoints][8] = -1.0;
+ break;
+ case 4:
+ matrix[numpoints][7] = 1.0;
+ break;
+ case 5:
+ matrix[numpoints][9] = 1.0;
+ break;
+ case 6:
+ matrix[numpoints][4] = 1.0;
+ break;
+ case 7:
+ matrix[numpoints][5] = 1.0;
+ break;
+ case 8:
+ matrix[numpoints][3] = 1.0;
+ break;
+
+ default:
+ addedconstraint = false;
+ break;
+ }
+
+ if (addedconstraint) ++numpoints;
+ }
+
+ if ( ! GaussianElimination( matrix, numpoints, 10, scambio ) )
+ return CubicCartesianData::invalidData();
+ // fine della fase di eliminazione
+ BackwardSubstitution( matrix, numpoints, 10, scambio, solution );
+
+ // now solution should contain the correct coefficients..
+ return CubicCartesianData( solution );
+}
+
+/*
+ * computation of the y value corresponding to some x value
+ */
+
+double calcCubicYvalue ( double x, double ymin, double ymax, int root,
+ CubicCartesianData data, bool& valid,
+ int &numroots )
+{
+ valid = true;
+
+ // compute the third degree polinomial:
+ double a000 = data.coeffs[0];
+ double a001 = data.coeffs[1];
+ double a002 = data.coeffs[2];
+ double a011 = data.coeffs[3];
+ double a012 = data.coeffs[4];
+ double a022 = data.coeffs[5];
+ double a111 = data.coeffs[6];
+ double a112 = data.coeffs[7];
+ double a122 = data.coeffs[8];
+ double a222 = data.coeffs[9];
+
+ // first the y^3 coefficient, it coming only from a222:
+ double a = a222;
+ // next the y^2 coefficient (from a122 and a022):
+ double b = a122*x + a022;
+ // next the y coefficient (from a112, a012 and a002):
+ double c = a112*x*x + a012*x + a002;
+ // finally the constant coefficient (from a111, a011, a001 and a000):
+ double d = a111*x*x*x + a011*x*x + a001*x + a000;
+
+ return calcCubicRoot ( ymin, ymax, a, b, c, d, root, valid, numroots );
+}
+
+const Coordinate calcCubicLineIntersect( const CubicCartesianData& cu,
+ const LineData& l,
+ int root, bool& valid )
+{
+ assert( root == 1 || root == 2 || root == 3 );
+
+ double a, b, c, d;
+ calcCubicLineRestriction ( cu, l.a, l.b-l.a, a, b, c, d );
+ int numroots;
+ double param =
+ calcCubicRoot ( -1e10, 1e10, a, b, c, d, root, valid, numroots );
+ return l.a + param*(l.b - l.a);
+}
+
+/*
+ * calculate the cubic polynomial resulting from the restriction
+ * of a cubic to a line (defined by two "Coordinates": a point and a
+ * direction)
+ */
+
+void calcCubicLineRestriction ( CubicCartesianData data,
+ Coordinate p, Coordinate v,
+ double& a, double& b, double& c, double& d )
+{
+ a = b = c = d = 0;
+
+ double a000 = data.coeffs[0];
+ double a001 = data.coeffs[1];
+ double a002 = data.coeffs[2];
+ double a011 = data.coeffs[3];
+ double a012 = data.coeffs[4];
+ double a022 = data.coeffs[5];
+ double a111 = data.coeffs[6];
+ double a112 = data.coeffs[7];
+ double a122 = data.coeffs[8];
+ double a222 = data.coeffs[9];
+
+ // zero degree term
+ d += a000;
+
+ // first degree terms
+ d += a001*p.x + a002*p.y;
+ c += a001*v.x + a002*v.y;
+
+ // second degree terms
+ d += a011*p.x*p.x + a012*p.x*p.y + a022*p.y*p.y;
+ c += 2*a011*p.x*v.x + a012*(p.x*v.y + v.x*p.y) + 2*a022*p.y*v.y;
+ b += a011*v.x*v.x + a012*v.x*v.y + a022*v.y*v.y;
+
+ // third degree terms: a111 x^3 + a222 y^3
+ d += a111*p.x*p.x*p.x + a222*p.y*p.y*p.y;
+ c += 3*(a111*p.x*p.x*v.x + a222*p.y*p.y*v.y);
+ b += 3*(a111*p.x*v.x*v.x + a222*p.y*v.y*v.y);
+ a += a111*v.x*v.x*v.x + a222*v.y*v.y*v.y;
+
+ // third degree terms: a112 x^2 y + a122 x y^2
+ d += a112*p.x*p.x*p.y + a122*p.x*p.y*p.y;
+ c += a112*(p.x*p.x*v.y + 2*p.x*v.x*p.y) + a122*(v.x*p.y*p.y + 2*p.x*p.y*v.y);
+ b += a112*(v.x*v.x*p.y + 2*v.x*p.x*v.y) + a122*(p.x*v.y*v.y + 2*v.x*v.y*p.y);
+ a += a112*v.x*v.x*v.y + a122*v.x*v.y*v.y;
+}
+
+
+const CubicCartesianData calcCubicTransformation (
+ const CubicCartesianData& data,
+ const Transformation& t, bool& valid )
+{
+ double a[3][3][3];
+ double b[3][3][3];
+ CubicCartesianData dataout;
+
+ int icount = 0;
+ for (int i=0; i < 3; i++)
+ {
+ for (int j=i; j < 3; j++)
+ {
+ for (int k=j; k < 3; k++)
+ {
+ a[i][j][k] = data.coeffs[icount++];
+ if ( i < k )
+ {
+ if ( i == j ) // case aiik
+ {
+ a[i][i][k] /= 3.;
+ a[i][k][i] = a[k][i][i] = a[i][i][k];
+ }
+ else if ( j == k ) // case aijj
+ {
+ a[i][j][j] /= 3.;
+ a[j][i][j] = a[j][j][i] = a[i][j][j];
+ }
+ else // case aijk (i<j<k)
+ {
+ a[i][j][k] /= 6.;
+ a[i][k][j] = a[j][i][k] = a[j][k][i] =
+ a[k][i][j] = a[k][j][i] = a[i][j][k];
+ }
+ }
+ }
+ }
+ }
+
+ Transformation ti = t.inverse( valid );
+ if ( ! valid ) return dataout;
+
+ for (int i = 0; i < 3; i++)
+ {
+ for (int j = 0; j < 3; j++)
+ {
+ for (int k = 0; k < 3; k++)
+ {
+ b[i][j][k] = 0.;
+ for (int ii = 0; ii < 3; ii++)
+ {
+ for (int jj = 0; jj < 3; jj++)
+ {
+ for (int kk = 0; kk < 3; kk++)
+ {
+ b[i][j][k] += a[ii][jj][kk]*ti.data( ii, i )*ti.data( jj, j )*ti.data( kk, k );
+ }
+ }
+ }
+ }
+ }
+ }
+
+// assert (fabs(b[0][1][2] - b[1][2][0]) < 1e-8); // test a couple of cases
+// assert (fabs(b[0][1][1] - b[1][1][0]) < 1e-8);
+
+ // apparently, the above assertions are wrong ( due to rounding
+ // errors, Maurizio and I hope :) ), so since the symmetry is not
+ // present, we just take the sum of the parts of the matrix elements
+ // that should be symmetric, instead of taking one of them, and
+ // multiplying it..
+
+ dataout.coeffs[0] = b[0][0][0];
+ dataout.coeffs[1] = b[0][0][1] + b[0][1][0] + b[1][0][0];
+ dataout.coeffs[2] = b[0][0][2] + b[0][2][0] + b[2][0][0];
+ dataout.coeffs[3] = b[0][1][1] + b[1][0][1] + b[1][1][0];
+ dataout.coeffs[4] = b[0][1][2] + b[0][2][1] + b[1][2][0] + b[1][0][2] + b[2][1][0] + b[2][0][1];
+ dataout.coeffs[5] = b[0][2][2] + b[2][0][2] + b[2][2][0];
+ dataout.coeffs[6] = b[1][1][1];
+ dataout.coeffs[7] = b[1][1][2] + b[1][2][1] + b[2][1][1];
+ dataout.coeffs[8] = b[1][2][2] + b[2][1][2] + b[2][2][1];
+ dataout.coeffs[9] = b[2][2][2];
+
+ return dataout;
+}
+
+bool operator==( const CubicCartesianData& lhs, const CubicCartesianData& rhs )
+{
+ for ( int i = 0; i < 10; ++i )
+ if ( lhs.coeffs[i] != rhs.coeffs[i] )
+ return false;
+ return true;
+}
+
+CubicCartesianData CubicCartesianData::invalidData()
+{
+ CubicCartesianData ret;
+ ret.coeffs[0] = double_inf;
+ return ret;
+}
+
+bool CubicCartesianData::valid() const
+{
+ return finite( coeffs[0] );
+}