// Copyright (C) 2003 Dominique Devriese // 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 #include "cubic-common.h" #include "kignumerics.h" #include "kigtransform.h" #ifdef HAVE_IEEEFP_H #include #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& 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& 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& 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