summaryrefslogtreecommitdiffstats
path: root/src/sp-mesh-array.cpp
diff options
context:
space:
mode:
authorJabier Arraiza Cenoz <jabier.arraiza@marker.es>2015-03-18 18:07:14 +0000
committerJabiertxof <jtx@jtx.marker.es>2015-03-18 18:07:14 +0000
commite77956b4dbd029c9f6949f81fe083606f995c624 (patch)
tree74adda4df8986d65f70efb341c6235277361fd35 /src/sp-mesh-array.cpp
parentupdated code to work on 0.92 code (diff)
parentLatvian translation update (diff)
downloadinkscape-e77956b4dbd029c9f6949f81fe083606f995c624.tar.gz
inkscape-e77956b4dbd029c9f6949f81fe083606f995c624.zip
update to trunk
(bzr r12588.1.39)
Diffstat (limited to 'src/sp-mesh-array.cpp')
-rw-r--r--src/sp-mesh-array.cpp419
1 files changed, 400 insertions, 19 deletions
diff --git a/src/sp-mesh-array.cpp b/src/sp-mesh-array.cpp
index 8bfe23656..445c9a8f6 100644
--- a/src/sp-mesh-array.cpp
+++ b/src/sp-mesh-array.cpp
@@ -32,7 +32,7 @@
* Authors:
* Tavmjong Bah <tavmjong@free.fr>
*
- * Copyrigt (C) 2012 Tavmjong Bah
+ * Copyright (C) 2012, 2015 Tavmjong Bah
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
@@ -46,8 +46,8 @@
#include "document.h"
#include "sp-root.h"
+#include "sp-mesh.h"
#include "sp-mesh-array.h"
-#include "sp-mesh-gradient.h"
#include "sp-mesh-row.h"
#include "sp-mesh-patch.h"
#include "sp-stop.h"
@@ -289,7 +289,7 @@ bool SPMeshPatchI::tensorIsSet() {
/**
Return if tensor control point for "corner" i is set.
*/
-bool SPMeshPatchI::tensorIsSet( guint i ) {
+bool SPMeshPatchI::tensorIsSet( unsigned int i ) {
assert( i < 4 );
@@ -574,13 +574,54 @@ void SPMeshPatchI::setOpacity( guint i, gdouble opacity ) {
};
-SPMeshNodeArray::SPMeshNodeArray( SPMeshGradient *mg ) {
+SPMeshNodeArray::SPMeshNodeArray( SPMesh *mg ) {
read( mg );
};
-void SPMeshNodeArray::read( SPMeshGradient *mg_in ) {
+
+// Copy constructor
+SPMeshNodeArray::SPMeshNodeArray( const SPMeshNodeArray& rhs ) {
+
+ built = false;
+ mg = NULL;
+ drag_valid = false;
+
+ nodes = rhs.nodes; // This only copies the pointers but it does size the vector of vectors.
+
+ for( unsigned i=0; i < nodes.size(); ++i ) {
+ for( unsigned j=0; j < nodes[i].size(); ++j ) {
+ nodes[i][j] = new SPMeshNode( *rhs.nodes[i][j] ); // Copy data.
+ }
+ }
+};
+
+
+// Copy assignment operator
+SPMeshNodeArray& SPMeshNodeArray::operator=( const SPMeshNodeArray& rhs ) {
+
+ if( this == &rhs ) return *this;
+
+ clear(); // Clear any existing array.
+
+ built = false;
+ mg = NULL;
+ drag_valid = false;
+
+ nodes = rhs.nodes; // This only copies the pointers but it does size the vector of vectors.
+
+ for( unsigned i=0; i < nodes.size(); ++i ) {
+ for( unsigned j=0; j < nodes[i].size(); ++j ) {
+ nodes[i][j] = new SPMeshNode( *rhs.nodes[i][j] ); // Copy data.
+ }
+ }
+
+ return *this;
+};
+
+
+void SPMeshNodeArray::read( SPMesh *mg_in ) {
mg = mg_in;
@@ -600,7 +641,7 @@ void SPMeshNodeArray::read( SPMeshGradient *mg_in ) {
if (SP_IS_MESHPATCH(po)) {
- SPMeshPatch *patch = SP_MESHPATCH(po);
+ SPMeshpatch *patch = SP_MESHPATCH(po);
// std::cout << "SPMeshNodeArray::read: row size: " << nodes.size() << std::endl;
SPMeshPatchI new_patch( &nodes, irow, icolumn ); // Adds new nodes.
@@ -797,7 +838,7 @@ void SPMeshNodeArray::read( SPMeshGradient *mg_in ) {
/**
Write repr using our array.
*/
-void SPMeshNodeArray::write( SPMeshGradient *mg ) {
+void SPMeshNodeArray::write( SPMesh *mg ) {
// std::cout << "SPMeshNodeArray::write: entrance:" << std::endl;
// print();
@@ -848,14 +889,14 @@ void SPMeshNodeArray::write( SPMeshGradient *mg ) {
for( guint i = 0; i < rows; ++i ) {
// Write row
- Inkscape::XML::Node *row = xml_doc->createElement("svg:meshRow");
+ Inkscape::XML::Node *row = xml_doc->createElement("svg:meshrow");
mesh->appendChild( row ); // No attributes
guint columns = array->patch_columns();
for( guint j = 0; j < columns; ++j ) {
// Write patch
- Inkscape::XML::Node *patch = xml_doc->createElement("svg:meshPatch");
+ Inkscape::XML::Node *patch = xml_doc->createElement("svg:meshpatch");
SPMeshPatchI patchi( &(array->nodes), i, j );
@@ -926,10 +967,10 @@ void SPMeshNodeArray::write( SPMeshGradient *mg ) {
break;
case 'z':
case 'Z':
- std::cout << "sp_meshgradient_repr_write: bad path type" << path_type << std::endl;
+ std::cout << "sp_mesh_repr_write: bad path type" << path_type << std::endl;
break;
default:
- std::cout << "sp_meshgradient_repr_write: unhandled path type" << path_type << std::endl;
+ std::cout << "sp_mesh_repr_write: unhandled path type" << path_type << std::endl;
}
stop->setAttribute("path", is.str().c_str());
// std::cout << "SPMeshNodeArray::write: path: " << is.str().c_str() << std::endl;
@@ -998,7 +1039,7 @@ static SPColor default_color( SPItem *item ) {
/**
Create a default mesh.
*/
-void SPMeshNodeArray::create( SPMeshGradient *mg, SPItem *item, Geom::OptRect bbox ) {
+void SPMeshNodeArray::create( SPMesh *mg, SPItem *item, Geom::OptRect bbox ) {
// std::cout << "SPMeshNodeArray::create: Entrance" << std::endl;
@@ -1036,10 +1077,10 @@ void SPMeshNodeArray::create( SPMeshGradient *mg, SPItem *item, Geom::OptRect bb
guint prows = prefs->getInt("/tools/mesh/mesh_rows", 1);
guint pcols = prefs->getInt("/tools/mesh/mesh_cols", 1);
- SPGradientMeshType mesh_type =
- (SPGradientMeshType) prefs->getInt("/tools/mesh/mesh_type", SP_GRADIENT_MESH_TYPE_NORMAL);
+ SPMeshGeometry mesh_type =
+ (SPMeshGeometry) prefs->getInt("/tools/mesh/mesh_geometry", SP_MESH_GEOMETRY_NORMAL);
- if( mesh_type == SP_GRADIENT_MESH_TYPE_CONICAL ) {
+ if( mesh_type == SP_MESH_GEOMETRY_CONICAL ) {
// Conical gradient.. for any shape/path using geometric bounding box.
@@ -1382,6 +1423,346 @@ void SPMeshNodeArray::print() {
};
+
+/*
+double hermite( const double p0, const double p1, const double m0, const double m1, const double t ) {
+ double t2 = t*t;
+ double t3 = t2*t;
+
+ double result = (2.0*t3 - 3.0*t2 +1.0) * p0
+ + (t3 - 2.0*t2 + t) * m0
+ + (-2.0*t3 + 3.0*t2) * p1
+ + (t3 -t2) * m1;
+
+ return result;
+}
+*/
+
+class SPMeshSmoothCorner {
+
+ enum {
+ AMP,
+ DX_LEFT,
+ DX_RIGHT,
+ DY_TOP,
+ DY_BOTTOM,
+ DXY_LT,
+ DXY_RT,
+ DXY_LB,
+ DXY_RB
+ };
+
+public:
+ SPMeshSmoothCorner() {
+ for( unsigned i = 0; i < 3; ++i ) {
+ for( unsigned j = 0; j < 4; ++j ) {
+ g[i][j] = 0;
+ }
+ }
+ }
+
+ double g[3][8]; // 3 colors, 8 parameters: see enum.
+ Geom::Point p; // Location of point
+};
+
+// Find slope at point 1 given values at previous and next points
+// Return value is slope in user space
+double find_slope1( const double &p0, const double &p1, const double &p2,
+ const double &d01, const double &d12 ) {
+
+ double slope = 0;
+
+ if( d01 > 0 && d12 > 0 ) {
+ slope = 0.5 * ( (p1 - p0)/d01 + (p2 - p1)/d12 );
+
+ if( ( p0 > p1 && p1 < p2 ) ||
+ ( p0 < p1 && p1 > p2 ) ) {
+ // At minimum or maximum, use slope of zero
+ slope = 0;
+ } else {
+ // Ensure we don't overshoot
+ if( fabs(slope) > fabs(3*(p1-p0)/d01) ) {
+ slope = 3*(p1-p0)/d01;
+ }
+ if( fabs(slope) > fabs(3*(p2-p1)/d12) ) {
+ slope = 3*(p2-p1)/d12;
+ }
+ }
+ } else {
+ // Do something clever
+ }
+ return slope;
+};
+
+
+/*
+// Find slope at point 0 given values at previous and next points
+// TO DO: TAKE DISTANCE BETWEEN POINTS INTO ACCOUNT
+double find_slope2( double pmm, double ppm, double pmp, double ppp, double p0 ) {
+
+ // pmm == d[i-1][j-1], ... 'm' is minus, 'p' is plus
+ double slope = (ppp - ppm - pmp + pmm)/2.0;
+ if( (ppp > p0 && ppm > p0 && pmp > p0 && pmm > 0) ||
+ (ppp < p0 && ppm < p0 && pmp < p0 && pmm < 0) ) {
+ // At minimum or maximum, use slope of zero
+ slope = 0;
+ } else {
+ // Don't really know what to do here
+ if( fabs(slope) > fabs(3*(ppp-p0)) ) {
+ slope = 3*(ppp-p0);
+ }
+ if( fabs(slope) > fabs(3*(pmp-p0)) ) {
+ slope = 3*(pmp-p0);
+ }
+ if( fabs(slope) > fabs(3*(ppm-p0)) ) {
+ slope = 3*(ppm-p0);
+ }
+ if( fabs(slope) > fabs(3*(pmm-p0)) ) {
+ slope = 3*(pmm-p0);
+ }
+ }
+ return slope;
+}
+*/
+
+// https://en.wikipedia.org/wiki/Bicubic_interpolation
+void invert( const double v[16], double alpha[16] ) {
+
+ const double A[16][16] = {
+
+ { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ {-3, 3, 0, 0, -2,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 2,-2, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, -3, 3, 0, 0, -2,-1, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 2,-2, 0, 0, 1, 1, 0, 0 },
+ {-3, 0, 3, 0, 0, 0, 0, 0, -2, 0,-1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, -3, 0, 3, 0, 0, 0, 0, 0, -2, 0,-1, 0 },
+ { 9,-9,-9, 9, 6, 3,-6,-3, 6,-6, 3,-3, 4, 2, 2, 1 },
+ {-6, 6, 6,-6, -3,-3, 3, 3, -4, 4,-2, 2, -2,-2,-1,-1 },
+ { 2, 0,-2, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 2, 0,-2, 0, 0, 0, 0, 0, 1, 0, 1, 0 },
+ {-6, 6, 6,-6, -4,-2, 4, 2, -3, 3,-3, 3, -2,-1,-2,-1 },
+ { 4,-4,-4, 4, 2, 2,-2,-2, 2,-2, 2,-2, 1, 1, 1, 1 }
+ };
+
+ for( unsigned i = 0; i < 16; ++i ) {
+ alpha[i] = 0;
+ for( unsigned j = 0; j < 16; ++j ) {
+ alpha[i] += A[i][j]*v[j];
+ }
+ }
+}
+
+double sum( const double alpha[16], const double& x, const double& y ) {
+
+ double result = 0;
+
+ double xx = x*x;
+ double xxx = xx * x;
+ double yy = y*y;
+ double yyy = yy * y;
+
+ result += alpha[ 0 ];
+ result += alpha[ 1 ] * x;
+ result += alpha[ 2 ] * xx;
+ result += alpha[ 3 ] * xxx;
+ result += alpha[ 4 ] * y;
+ result += alpha[ 5 ] * y * x;
+ result += alpha[ 6 ] * y * xx;
+ result += alpha[ 7 ] * y * xxx;
+ result += alpha[ 8 ] * yy;
+ result += alpha[ 9 ] * yy * x;
+ result += alpha[ 10 ] * yy * xx;
+ result += alpha[ 11 ] * yy * xxx;
+ result += alpha[ 12 ] * yyy;
+ result += alpha[ 13 ] * yyy * x;
+ result += alpha[ 14 ] * yyy * xx;
+ result += alpha[ 15 ] * yyy * xxx;
+
+ return result;
+}
+
+/**
+ Fill 'smooth' with a smoothed version of the array by subdividing each patch into smaller patches.
+*/
+void SPMeshNodeArray::bicubic( SPMeshNodeArray* smooth, SPMeshType type ) {
+
+
+ *smooth = *this; // Deep copy via copy assignment constructor, smooth cleared before copy
+ // std::cout << "SPMeshNodeArray::smooth2(): " << this->patch_rows() << " " << smooth->patch_columns() << std::endl;
+ // std::cout << " " << smooth << " " << this << std::endl;
+
+ // Find derivatives at corners
+
+ // Create array of corner points
+ std::vector< std::vector <SPMeshSmoothCorner> > d;
+ d.resize( smooth->patch_rows() + 1 );
+ for( unsigned i = 0; i < d.size(); ++i ) {
+ d[i].resize( smooth->patch_columns() + 1 );
+ for( unsigned j = 0; j < d[i].size(); ++j ) {
+ float rgb_color[3];
+ sp_color_get_rgb_floatv( &this->nodes[ i*3 ][ j*3 ]->color, rgb_color );
+ d[i][j].g[0][0] = rgb_color[ 0 ];
+ d[i][j].g[1][0] = rgb_color[ 1 ];
+ d[i][j].g[2][0] = rgb_color[ 2 ];
+ d[i][j].p = this->nodes[ i*3 ][ j*3 ]->p;
+ }
+ }
+
+ // Calculate interior derivatives
+ for( unsigned i = 0; i < d.size(); ++i ) {
+ for( unsigned j = 0; j < d[i].size(); ++j ) {
+ for( unsigned k = 0; k < 3; ++k ) { // Loop over colors
+
+ // dx
+
+ if( i != 0 && i != d.size()-1 ) {
+ double lm = Geom::distance( d[i-1][j].p, d[i][j].p );
+ double lp = Geom::distance( d[i+1][j].p, d[i][j].p );
+ d[i][j].g[k][1] = find_slope1( d[i-1][j].g[k][0], d[i][j].g[k][0], d[i+1][j].g[k][0], lm, lp );
+ }
+
+ // dy
+ if( j != 0 && j != d[i].size()-1 ) {
+ double lm = Geom::distance( d[i][j-1].p, d[i][j].p );
+ double lp = Geom::distance( d[i][j+1].p, d[i][j].p );
+ d[i][j].g[k][2] = find_slope1( d[i][j-1].g[k][0], d[i][j].g[k][0], d[i][j+1].g[k][0], lm, lp );
+ }
+
+ // dxdy if needed, need to take lengths into account
+ // if( i != 0 && i != d.size()-1 && j != 0 && j != d[i].size()-1 ) {
+ // d[i][j].g[k][3] = find_slope2( d[i-1][j-1].g[k][0], d[i+1][j-1].g[k][0],
+ // d[i-1][j+1].g[k][0], d[i-1][j-1].g[k][0],
+ // d[i][j].g[k][0] );
+ // }
+
+ }
+ }
+ }
+
+ // Calculate exterior derivatives
+ // We need to do this after calculating interior derivatives as we need to already
+ // have the non-exterior derivative calculated for finding the parabola.
+ for( unsigned j = 0; j< d[0].size(); ++j ) {
+ for( unsigned k = 0; k < 3; ++k ) { // Loop over colors
+
+ // Parabolic
+ double d0 = Geom::distance( d[1][j].p, d[0 ][j].p );
+ if( d0 > 0 ) {
+ d[0][j].g[k][1] = 2.0*(d[1][j].g[k][0] - d[0 ][j].g[k][0])/d0 - d[1][j].g[k][1];
+ } else {
+ d[0][j].g[k][1] = 0;
+ }
+
+ unsigned z = d.size()-1;
+ double dz = Geom::distance( d[z][j].p, d[z-1][j].p );
+ if( dz > 0 ) {
+ d[z][j].g[k][1] = 2.0*(d[z][j].g[k][0] - d[z-1][j].g[k][0])/dz - d[z-1][j].g[k][1];
+ } else {
+ d[z][j].g[k][1] = 0;
+ }
+ }
+ }
+
+ for( unsigned i = 0; i< d.size(); ++i ) {
+ for( unsigned k = 0; k < 3; ++k ) { // Loop over colors
+
+ // Parabolic
+ double d0 = Geom::distance( d[i][1].p, d[i][0 ].p );
+ if( d0 > 0 ) {
+ d[i][0].g[k][2] = 2.0*(d[i][1].g[k][0] - d[i][0 ].g[k][0])/d0 - d[i][1].g[k][2];
+ } else {
+ d[i][0].g[k][2] = 0;
+ }
+
+ unsigned z = d[0].size()-1;
+ double dz = Geom::distance( d[i][z].p, d[i][z-1].p );
+ if( dz > 0 ) {
+ d[i][z].g[k][2] = 2.0*(d[i][z].g[k][0] - d[i][z-1].g[k][0])/dz - d[i][z-1].g[k][2];
+ } else {
+ d[i][z].g[k][2] = 0;
+ }
+ }
+ }
+
+ // Leave outside corner cross-derivatives at zero.
+
+ // Next split each patch into 8x8 smaller patches.
+
+ // Split each row into eight rows.
+ // Must do it from end so inserted rows don't mess up indexing
+ for( int i = smooth->patch_rows() - 1; i >= 0; --i ) {
+ smooth->split_row( i, unsigned(8) );
+ }
+
+ // Split each column into eight columns.
+ // Must do it from end so inserted columns don't mess up indexing
+ for( int i = smooth->patch_columns() - 1; i >= 0; --i ) {
+ smooth->split_column( i, (unsigned)8 );
+ }
+
+ // Fill new patches
+ for( unsigned i = 0; i < this->patch_rows(); ++i ) {
+ for( unsigned j = 0; j < this->patch_columns(); ++j ) {
+
+ double dx0 = Geom::distance( d[i ][j ].p, d[i+1][j ].p );
+ double dx1 = Geom::distance( d[i ][j+1].p, d[i+1][j+1].p );
+ double dy0 = Geom::distance( d[i ][j ].p, d[i ][j+1].p );
+ double dy1 = Geom::distance( d[i+1][j ].p, d[i+1][j+1].p );
+
+ // Temp loop over 0..8 to get last column/row edges
+ float r[3][9][9]; // result
+ for( unsigned m = 0; m < 3; ++m ) {
+
+ double v[16];
+ v[ 0] = d[i ][j ].g[m][0];
+ v[ 1] = d[i+1][j ].g[m][0];
+ v[ 2] = d[i ][j+1].g[m][0];
+ v[ 3] = d[i+1][j+1].g[m][0];
+ v[ 4] = d[i ][j ].g[m][1]*dx0;
+ v[ 5] = d[i+1][j ].g[m][1]*dx0;
+ v[ 6] = d[i ][j+1].g[m][1]*dx1;
+ v[ 7] = d[i+1][j+1].g[m][1]*dx1;
+ v[ 8] = d[i ][j ].g[m][2]*dy0;
+ v[ 9] = d[i+1][j ].g[m][2]*dy1;
+ v[10] = d[i ][j+1].g[m][2]*dy0;
+ v[11] = d[i+1][j+1].g[m][2]*dy1;
+ v[12] = d[i ][j ].g[m][3];
+ v[13] = d[i+1][j ].g[m][3];
+ v[14] = d[i ][j+1].g[m][3];
+ v[15] = d[i+1][j+1].g[m][3];
+
+ double alpha[16];
+ invert( v, alpha );
+
+ for( unsigned k = 0; k < 9; ++k ) {
+ for( unsigned l = 0; l < 9; ++l ) {
+ double x = k/8.0;
+ double y = l/8.0;
+ r[m][k][l] = sum( alpha, x, y );
+ // Clamp to allowed values
+ if( r[m][k][l] > 1.0 )
+ r[m][k][l] = 1.0;
+ if( r[m][k][l] < 0.0 )
+ r[m][k][l] = 0.0;
+ }
+ }
+
+ } // Loop over colors
+
+ for( unsigned k = 0; k < 9; ++k ) {
+ for( unsigned l = 0; l < 9; ++l ) {
+ // Every third node is a corner node
+ smooth->nodes[ (i*8+k)*3 ][(j*8+l)*3 ]->color.set( r[0][k][l], r[1][k][l], r[2][k][l] );
+ }
+ }
+ }
+ }
+}
+
/**
Number of patch rows.
*/
@@ -2239,7 +2620,7 @@ guint32 average_color(guint32 c1, guint32 c2, gdouble p);
/**
Split a row into n equal parts.
*/
-void SPMeshNodeArray::split_row( guint row, guint n ) {
+void SPMeshNodeArray::split_row( unsigned int row, unsigned int n ) {
double nn = n;
if( n > 1 ) split_row( row, (nn-1)/nn );
@@ -2249,7 +2630,7 @@ void SPMeshNodeArray::split_row( guint row, guint n ) {
/**
Split a column into n equal parts.
*/
-void SPMeshNodeArray::split_column( guint col, guint n ) {
+void SPMeshNodeArray::split_column( unsigned int col, unsigned int n ) {
double nn = n;
if( n > 1 ) split_column( col, (nn-1)/nn );
@@ -2259,7 +2640,7 @@ void SPMeshNodeArray::split_column( guint col, guint n ) {
/**
Split a row into two rows at coord (fraction of row height).
*/
-void SPMeshNodeArray::split_row( guint row, double coord ) {
+void SPMeshNodeArray::split_row( unsigned int row, double coord ) {
// std::cout << "Splitting row: " << row << " at " << coord << std::endl;
// print();
@@ -2382,7 +2763,7 @@ void SPMeshNodeArray::split_row( guint row, double coord ) {
/**
Split a column into two columns at coord (fraction of column width).
*/
-void SPMeshNodeArray::split_column( guint col, double coord ) {
+void SPMeshNodeArray::split_column( unsigned int col, double coord ) {
// std::cout << "Splitting column: " << col << " at " << coord << std::endl;
// print();