diff options
Diffstat (limited to 'src/sp-mesh-array.cpp')
| -rw-r--r-- | src/sp-mesh-array.cpp | 3102 |
1 files changed, 0 insertions, 3102 deletions
diff --git a/src/sp-mesh-array.cpp b/src/sp-mesh-array.cpp deleted file mode 100644 index d958427f2..000000000 --- a/src/sp-mesh-array.cpp +++ /dev/null @@ -1,3102 +0,0 @@ -/** \file - A group of classes and functions for manipulating mesh gradients. - - A mesh is made up of an array of patches. Each patch has four sides and four corners. The sides can - be shared between two patches and the corners between up to four. - - The order of the points for each side always goes from left to right or top to bottom. - For sides 2 and 3 the points must be reversed when used (as in calls to cairo functions). - - Two patches: (C=corner, S=side, H=handle, T=tensor) - - C0 H1 H2 C1 C0 H1 H2 C1 - + ---------- + ---------- + - | S0 | S0 | - H1 | T0 T1 |H1 T0 T1 | H1 - |S3 S1|S3 S1| - H2 | T3 T2 |H2 T3 T2 | H2 - | S2 | S2 | - + ---------- + ---------- + - C3 H1 H2 C2 C3 H1 H2 C2 - - The mesh is stored internally as an array of nodes that includes the tensor nodes. - - Note: This code uses tensor points which are not part of the SVG2 plan at the moment. - Including tensor points was motivated by a desire to experiment with their usefulness - in smoothing color transitions. There doesn't seem to be much advantage for that - purpose. However including them internally allows for storing all the points in - an array which simplifies things like inserting new rows or columns. -*/ - -/* - * Authors: - * Tavmjong Bah <tavmjong@free.fr> - * - * Copyright (C) 2012, 2015 Tavmjong Bah - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include <glibmm.h> -#include <set> - -// For color picking -#include "display/drawing.h" -#include "display/drawing-context.h" -#include "display/cairo-utils.h" -#include "document.h" -#include "sp-root.h" - -#include "sp-mesh-gradient.h" -#include "sp-mesh-array.h" -#include "sp-mesh-row.h" -#include "sp-mesh-patch.h" -#include "sp-stop.h" -#include "display/curve.h" - -// For new mesh creation -#include "preferences.h" -#include "sp-ellipse.h" -#include "sp-star.h" - -// For writing color/opacity to style -#include "svg/css-ostringstream.h" - -// For default color -#include "style.h" -#include "svg/svg-color.h" - - -// Includes bezier-curve.h, ray.h, crossing.h -#include "2geom/line.h" - -#include "xml/repr.h" -#include <cmath> -#include <algorithm> - -enum { ROW, COL }; - -SPMeshPatchI::SPMeshPatchI( std::vector<std::vector< SPMeshNode* > > * n, int r, int c ) { - - nodes = n; - row = r*3; // Convert from patch array to node array - col = c*3; - - guint i = 0; - if( row != 0 ) i = 1; - for( ; i < 4; ++i ) { - if( nodes->size() < row+i+1 ) { - std::vector< SPMeshNode* > row; - nodes->push_back( row ); - } - - guint j = 0; - if( col != 0 ) j = 1; - for( ; j < 4; ++j ) { - if( (*nodes)[row+i].size() < col+j+1 ){ - SPMeshNode* node = new SPMeshNode; - // Ensure all nodes know their type. - node->node_type = MG_NODE_TYPE_HANDLE; - if( (i == 0 || i == 3) && (j == 0 || j == 3 ) ) node->node_type = MG_NODE_TYPE_CORNER; - if( (i == 1 || i == 2) && (j == 1 || j == 2 ) ) node->node_type = MG_NODE_TYPE_TENSOR; - (*nodes)[row+i].push_back( node ); - } - } - } -} - -/** - Returns point for side in proper order for patch -*/ -Geom::Point SPMeshPatchI::getPoint( guint s, guint pt ) { - - assert( s < 4 ); - assert( pt < 4 ); - - Geom::Point p; - switch ( s ) { - case 0: - p = (*nodes)[ row ][ col+pt ]->p; - break; - case 1: - p = (*nodes)[ row+pt ][ col+3 ]->p; - break; - case 2: - p = (*nodes)[ row+3 ][ col+3-pt ]->p; - break; - case 3: - p = (*nodes)[ row+3-pt ][ col ]->p; - break; - } - return p; - -}; - -/** - Returns vector of points for a side in proper order for a patch (clockwise order). -*/ -std::vector< Geom::Point > SPMeshPatchI::getPointsForSide( guint i ) { - - assert( i < 4 ); - - std::vector< Geom::Point> points; - points.push_back( getPoint( i, 0 ) ); - points.push_back( getPoint( i, 1 ) ); - points.push_back( getPoint( i, 2 ) ); - points.push_back( getPoint( i, 3 ) ); - return points; -}; - - -/** - Set point for side in proper order for patch -*/ -void SPMeshPatchI::setPoint( guint s, guint pt, Geom::Point p, bool set ) { - - assert( s < 4 ); - assert( pt < 4 ); - - NodeType node_type = MG_NODE_TYPE_CORNER; - if( pt == 1 || pt == 2 ) node_type = MG_NODE_TYPE_HANDLE; - - // std::cout << "SPMeshPatchI::setPoint: s: " << s - // << " pt: " << pt - // << " p: " << p - // << " node_type: " << node_type - // << " set: " << set - // << " row: " << row - // << " col: " << col << std::endl; - switch ( s ) { - case 0: - (*nodes)[ row ][ col+pt ]->p = p; - (*nodes)[ row ][ col+pt ]->set = set; - (*nodes)[ row ][ col+pt ]->node_type = node_type; - break; - case 1: - (*nodes)[ row+pt ][ col+3 ]->p = p; - (*nodes)[ row+pt ][ col+3 ]->set = set; - (*nodes)[ row+pt ][ col+3 ]->node_type = node_type; - break; - case 2: - (*nodes)[ row+3 ][ col+3-pt ]->p = p; - (*nodes)[ row+3 ][ col+3-pt ]->set = set; - (*nodes)[ row+3 ][ col+3-pt ]->node_type = node_type; - break; - case 3: - (*nodes)[ row+3-pt ][ col ]->p = p; - (*nodes)[ row+3-pt ][ col ]->set = set; - (*nodes)[ row+3-pt ][ col ]->node_type = node_type; - break; - } - -}; - -/** - Get path type for side (stored in handle nodes). -*/ -gchar SPMeshPatchI::getPathType( guint s ) { - - assert( s < 4 ); - - gchar type = 'x'; - - switch ( s ) { - case 0: - type = (*nodes)[ row ][ col+1 ]->path_type; - break; - case 1: - type = (*nodes)[ row+1 ][ col+3 ]->path_type; - break; - case 2: - type = (*nodes)[ row+3 ][ col+2 ]->path_type; - break; - case 3: - type = (*nodes)[ row+2 ][ col ]->path_type; - break; - } - - return type; -}; - -/** - Set path type for side (stored in handle nodes). -*/ -void SPMeshPatchI::setPathType( guint s, gchar t ) { - - assert( s < 4 ); - - switch ( s ) { - case 0: - (*nodes)[ row ][ col+1 ]->path_type = t; - (*nodes)[ row ][ col+2 ]->path_type = t; - break; - case 1: - (*nodes)[ row+1 ][ col+3 ]->path_type = t; - (*nodes)[ row+2 ][ col+3 ]->path_type = t; - break; - case 2: - (*nodes)[ row+3 ][ col+1 ]->path_type = t; - (*nodes)[ row+3 ][ col+2 ]->path_type = t; - break; - case 3: - (*nodes)[ row+1 ][ col ]->path_type = t; - (*nodes)[ row+2 ][ col ]->path_type = t; - break; - } - -}; - -/** - Set tensor control point for "corner" i. - */ -void SPMeshPatchI::setTensorPoint( guint i, Geom::Point p ) { - - assert( i < 4 ); - switch ( i ) { - case 0: - (*nodes)[ row + 1 ][ col + 1 ]->p = p; - (*nodes)[ row + 1 ][ col + 1 ]->set = true; - (*nodes)[ row + 1 ][ col + 1 ]->node_type = MG_NODE_TYPE_TENSOR; - break; - case 1: - (*nodes)[ row + 1 ][ col + 2 ]->p = p; - (*nodes)[ row + 1 ][ col + 2 ]->set = true; - (*nodes)[ row + 1 ][ col + 2 ]->node_type = MG_NODE_TYPE_TENSOR; - break; - case 2: - (*nodes)[ row + 2 ][ col + 2 ]->p = p; - (*nodes)[ row + 2 ][ col + 2 ]->set = true; - (*nodes)[ row + 2 ][ col + 2 ]->node_type = MG_NODE_TYPE_TENSOR; - break; - case 3: - (*nodes)[ row + 2 ][ col + 1 ]->p = p; - (*nodes)[ row + 2 ][ col + 1 ]->set = true; - (*nodes)[ row + 2 ][ col + 1 ]->node_type = MG_NODE_TYPE_TENSOR; - break; - } -} - -/** - Return if any tensor control point is set. - */ -bool SPMeshPatchI::tensorIsSet() { - for( guint i = 0; i < 4; ++i ) { - if( tensorIsSet( i ) ) { - return true; - } - } - return false; -} - -/** - Return if tensor control point for "corner" i is set. - */ -bool SPMeshPatchI::tensorIsSet( unsigned int i ) { - - assert( i < 4 ); - - bool set = false; - switch ( i ) { - case 0: - set = (*nodes)[ row + 1 ][ col + 1 ]->set; - break; - case 1: - set = (*nodes)[ row + 1 ][ col + 2 ]->set; - break; - case 2: - set = (*nodes)[ row + 2 ][ col + 2 ]->set; - break; - case 3: - set = (*nodes)[ row + 2 ][ col + 1 ]->set; - break; - } - return set; -} - -/** - Return tensor control point for "corner" i. - If not set, returns calculated (Coons) point. - */ -Geom::Point SPMeshPatchI::getTensorPoint( guint k ) { - - assert( k < 4 ); - - guint i = 0; - guint j = 0; - - - switch ( k ) { - case 0: - i = 1; - j = 1; - break; - case 1: - i = 1; - j = 2; - break; - case 2: - i = 2; - j = 2; - break; - case 3: - i = 2; - j = 1; - break; - } - - Geom::Point p; - if( (*nodes)[ row + i ][ col + j ]->set ) { - p = (*nodes)[ row + i ][ col + j ]->p; - } else { - p = coonsTensorPoint( k ); - } - return p; -} - -/** - Find default tensor point (equivalent point to Coons Patch). - Formulas defined in PDF spec. - Equivalent to 1/3 of side length from corner for square patch. - */ -Geom::Point SPMeshPatchI::coonsTensorPoint( guint i ) { - - Geom::Point t; - Geom::Point p[4][4]; // Points in PDF notation - - p[0][0] = getPoint( 0, 0 ); - p[0][1] = getPoint( 0, 1 ); - p[0][2] = getPoint( 0, 2 ); - p[0][3] = getPoint( 0, 3 ); - p[1][0] = getPoint( 3, 2 ); - p[1][3] = getPoint( 1, 1 ); - p[2][0] = getPoint( 3, 1 ); - p[2][3] = getPoint( 1, 2 ); - p[3][0] = getPoint( 2, 3 ); - p[3][1] = getPoint( 2, 2 ); - p[3][2] = getPoint( 2, 1 ); - p[3][3] = getPoint( 2, 0 ); - - switch ( i ) { - case 0: - t = ( -4.0 * p[0][0] + - 6.0 * ( p[0][1] + p[1][0] ) + - -2.0 * ( p[0][3] + p[3][0] ) + - 3.0 * ( p[3][1] + p[1][3] ) + - -1.0 * p[3][3] ) / 9.0; - break; - - case 1: - t = ( -4.0 * p[0][3] + - 6.0 * ( p[0][2] + p[1][3] ) + - -2.0 * ( p[0][0] + p[3][3] ) + - 3.0 * ( p[3][2] + p[1][0] ) + - -1.0 * p[3][0] ) / 9.0; - break; - - case 2: - t = ( -4.0 * p[3][3] + - 6.0 * ( p[3][2] + p[2][3] ) + - -2.0 * ( p[3][0] + p[0][3] ) + - 3.0 * ( p[0][2] + p[2][0] ) + - -1.0 * p[0][0] ) / 9.0; - break; - - case 3: - t = ( -4.0 * p[3][0] + - 6.0 * ( p[3][1] + p[2][0] ) + - -2.0 * ( p[3][3] + p[0][0] ) + - 3.0 * ( p[0][1] + p[2][3] ) + - -1.0 * p[0][3] ) / 9.0; - break; - - default: - - g_warning( "Impossible!" ); - - } - return t; -} - -/** - Update default values for handle and tensor nodes. -*/ -void SPMeshPatchI::updateNodes() { - - // std::cout << "SPMeshPatchI::updateNodes: " << row << "," << col << std::endl; - // Handles first (tensors require update handles). - for( guint i = 0; i < 4; ++i ) { - for( guint j = 0; j < 4; ++j ) { - if( (*nodes)[ row + i ][ col + j ]->set == false ) { - - if( (*nodes)[ row + i ][ col + j ]->node_type == MG_NODE_TYPE_HANDLE ) { - - // If a handle is not set it is because the side is a line. - // Set node points 1/3 of the way between corners. - - if( i == 0 || i == 3 ) { - Geom::Point p0 = ( (*nodes)[ row + i ][ col ]->p ); - Geom::Point p3 = ( (*nodes)[ row + i ][ col + 3 ]->p ); - Geom::Point dp = (p3 - p0)/3.0; - if( j == 2 ) dp *= 2.0; - (*nodes)[ row + i ][ col + j ]->p = p0 + dp; - } - - if( j == 0 || j == 3 ) { - Geom::Point p0 = ( (*nodes)[ row ][ col + j ]->p ); - Geom::Point p3 = ( (*nodes)[ row + 3 ][ col + j ]->p ); - Geom::Point dp = (p3 - p0)/3.0; - if( i == 2 ) dp *= 2.0; - (*nodes)[ row + i ][ col + j ]->p = p0 + dp; - } - } - } - } - } - - // Update tensor nodes - for( guint i = 1; i < 3; ++i ) { - for( guint j = 1; j < 3; ++j ) { - if( (*nodes)[ row + i ][ col + j ]->set == false ) { - - (*nodes)[ row + i ][ col + j ]->node_type = MG_NODE_TYPE_TENSOR; - - guint t = 0; - if( i == 1 && j == 2 ) t = 1; - if( i == 2 && j == 2 ) t = 2; - if( i == 2 && j == 1 ) t = 3; - (*nodes)[ row + i ][ col + j ]->p = coonsTensorPoint( t ); - // std::cout << "Update node: " << i << ", " << j << " " << coonsTensorPoint( t ) << std::endl; - - } - } - } -} - -/** - Return color for corner of patch. -*/ -SPColor SPMeshPatchI::getColor( guint i ) { - - assert( i < 4 ); - - SPColor color; - switch ( i ) { - case 0: - color = (*nodes)[ row ][ col ]->color; - break; - case 1: - color = (*nodes)[ row ][ col+3 ]->color; - break; - case 2: - color = (*nodes)[ row+3 ][ col+3 ]->color; - break; - case 3: - color = (*nodes)[ row+3 ][ col ]->color; - break; - - } - - return color; - -}; - -/** - Set color for corner of patch. -*/ -void SPMeshPatchI::setColor( guint i, SPColor color ) { - - assert( i < 4 ); - - switch ( i ) { - case 0: - (*nodes)[ row ][ col ]->color = color; - break; - case 1: - (*nodes)[ row ][ col+3 ]->color = color; - break; - case 2: - (*nodes)[ row+3 ][ col+3 ]->color = color; - break; - case 3: - (*nodes)[ row+3 ][ col ]->color = color; - break; - } -}; - -/** - Return opacity for corner of patch. -*/ -gdouble SPMeshPatchI::getOpacity( guint i ) { - - assert( i < 4 ); - - gdouble opacity = 0.0; - switch ( i ) { - case 0: - opacity = (*nodes)[ row ][ col ]->opacity; - break; - case 1: - opacity = (*nodes)[ row ][ col+3 ]->opacity; - break; - case 2: - opacity = (*nodes)[ row+3 ][ col+3 ]->opacity; - break; - case 3: - opacity = (*nodes)[ row+3 ][ col ]->opacity; - break; - } - - return opacity; -}; - - -/** - Set opacity for corner of patch. -*/ -void SPMeshPatchI::setOpacity( guint i, gdouble opacity ) { - - assert( i < 4 ); - - switch ( i ) { - case 0: - (*nodes)[ row ][ col ]->opacity = opacity; - break; - case 1: - (*nodes)[ row ][ col+3 ]->opacity = opacity; - break; - case 2: - (*nodes)[ row+3 ][ col+3 ]->opacity = opacity; - break; - case 3: - (*nodes)[ row+3 ][ col ]->opacity = opacity; - break; - - } - -}; - - -/** - Return stop pointer for corner of patch. -*/ -SPStop* SPMeshPatchI::getStopPtr( guint i ) { - - assert( i < 4 ); - - SPStop* stop = nullptr; - switch ( i ) { - case 0: - stop = (*nodes)[ row ][ col ]->stop; - break; - case 1: - stop = (*nodes)[ row ][ col+3 ]->stop; - break; - case 2: - stop = (*nodes)[ row+3 ][ col+3 ]->stop; - break; - case 3: - stop = (*nodes)[ row+3 ][ col ]->stop; - break; - } - - return stop; -}; - - -/** - Set stop pointer for corner of patch. -*/ -void SPMeshPatchI::setStopPtr( guint i, SPStop* stop ) { - - assert( i < 4 ); - - switch ( i ) { - case 0: - (*nodes)[ row ][ col ]->stop = stop; - break; - case 1: - (*nodes)[ row ][ col+3 ]->stop = stop; - break; - case 2: - (*nodes)[ row+3 ][ col+3 ]->stop = stop; - break; - case 3: - (*nodes)[ row+3 ][ col ]->stop = stop; - break; - - } - -}; - - -SPMeshNodeArray::SPMeshNodeArray( SPMeshGradient *mg ) { - - read( mg ); - -}; - - -// Copy constructor -SPMeshNodeArray::SPMeshNodeArray( const SPMeshNodeArray& rhs ) { - - built = false; - mg = NULL; - draggers_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; - draggers_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; -}; - -// Fill array with data from mesh objects. -// Returns true of array's dimensions unchanged. -bool SPMeshNodeArray::read( SPMeshGradient *mg_in ) { - - mg = mg_in; - SPMeshGradient* mg_array = dynamic_cast<SPMeshGradient*>(mg->getArray()); - if (!mg_array) { - std::cerr << "SPMeshNodeArray::read: No mesh array!" << std::endl; - return false; - } - // std::cout << "SPMeshNodeArray::read: " << mg_in << " array: " << mg_array << std::endl; - - // Count rows and columns, if unchanged reuse array to keep draggers valid. - unsigned cols = 0; - unsigned rows = 0; - for (auto& ro: mg_array->children) { - if (SP_IS_MESHROW(&ro)) { - ++rows; - if (rows == 1 ) { - for (auto& po: ro.children) { - if (SP_IS_MESHPATCH(&po)) { - ++cols; - } - } - } - } - } - bool same_size = true; - if (cols != patch_columns() || rows != patch_rows() ) { - // Draggers will be invalidated. - same_size = false; - clear(); - draggers_valid = false; - } - - Geom::Point current_p( mg->x.computed, mg->y.computed ); - // std::cout << "SPMeshNodeArray::read: p: " << current_p << std::endl; - - guint max_column = 0; - guint irow = 0; // Corresponds to top of patch being read in. - for (auto& ro: mg_array->children) { - - if (SP_IS_MESHROW(&ro)) { - - guint icolumn = 0; // Corresponds to left of patch being read in. - for (auto& po: ro.children) { - - if (SP_IS_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. - // std::cout << " after: " << nodes.size() << std::endl; - - gint istop = 0; - - // Only 'top' side defined for first row. - if( irow != 0 ) ++istop; - - for (auto& so: po.children) { - if (SP_IS_STOP(&so)) { - - if( istop > 3 ) { - // std::cout << " Mesh Gradient: Too many stops: " << istop << std::endl; - break; - } - - SPStop *stop = SP_STOP(&so); - - // Handle top of first row. - if( istop == 0 && icolumn == 0 ) { - // First patch in mesh. - new_patch.setPoint( 0, 0, current_p ); - } - // First point is always already defined by previous side (stop). - current_p = new_patch.getPoint( istop, 0 ); - - // If side closes patch, then we read one less point. - bool closed = false; - if( icolumn == 0 && istop == 3 ) closed = true; - if( icolumn > 0 && istop == 2 ) closed = true; - - - // Copy path and then replace commas by spaces so we can use stringstream to parse - std::string path_string = *(stop->path_string); - std::replace(path_string.begin(),path_string.end(),',',' '); - - // std::cout << " path_string: " << path_string << std::endl; - // std::cout << " current_p: " << current_p << std::endl; - - std::stringstream os( path_string ); - - // Determine type of path - char path_type; - os >> path_type; - new_patch.setPathType( istop, path_type ); - - gdouble x, y; - Geom::Point p, dp; - guint max; - switch ( path_type ) { - case 'l': - if( !closed ) { - os >> x >> y; - if( !os.fail() ) { - dp = Geom::Point( x, y ); - new_patch.setPoint( istop, 3, current_p + dp ); - } else { - std::cerr << "Failed to read l" << std::endl; - } - } - // To facilitate some side operations, set handles to 1/3 and - // 2/3 distance between corner points but flag as unset. - p = new_patch.getPoint( istop, 3 ); - dp = (p - current_p)/3.0; // Calculate since may not be set if closed. - // std::cout << " istop: " << istop - // << " dp: " << dp - // << " p: " << p - // << " current_p: " << current_p - // << std::endl; - new_patch.setPoint( istop, 1, current_p + dp, false ); - new_patch.setPoint( istop, 2, current_p + 2.0 * dp, false ); - break; - case 'L': - if( !closed ) { - os >> x >> y; - if( !os.fail() ) { - p = Geom::Point( x, y ); - new_patch.setPoint( istop, 3, p ); - } else { - std::cerr << "Failed to read L" << std::endl; - } - } - // To facilitate some side operations, set handles to 1/3 and - // 2/3 distance between corner points but flag as unset. - p = new_patch.getPoint( istop, 3 ); - dp = (p - current_p)/3.0; - new_patch.setPoint( istop, 1, current_p + dp, false ); - new_patch.setPoint( istop, 2, current_p + 2.0 * dp, false ); - break; - case 'c': - max = 4; - if( closed ) max = 3; - for( guint i = 1; i < max; ++i ) { - os >> x >> y; - if( !os.fail() ) { - p = Geom::Point( x, y ); - p += current_p; - new_patch.setPoint( istop, i, p ); - } else { - std::cerr << "Failed to read c: " << i << std::endl; - } - } - break; - case 'C': - max = 4; - if( closed ) max = 3; - for( guint i = 1; i < max; ++i ) { - os >> x >> y; - if( !os.fail() ) { - p = Geom::Point( x, y ); - new_patch.setPoint( istop, i, p ); - } else { - std::cerr << "Failed to read C: " << i << std::endl; - } - } - break; - default: - // should not reach - std::cerr << "Path Error: unhandled path type: " << path_type << std::endl; - } - current_p = new_patch.getPoint( istop, 3 ); - - // Color - if( (istop == 0 && irow == 0 && icolumn > 0) || (istop == 1 && irow > 0 ) ) { - // skip - } else { - SPColor color = stop->getEffectiveColor(); - double opacity = stop->opacity; - new_patch.setColor( istop, color ); - new_patch.setOpacity( istop, opacity ); - new_patch.setStopPtr( istop, stop ); - } - - } - ++istop; - } // Loop over stops - - // Read in tensor string after stops since tensor nodes defined relative to corner nodes. - - // Copy string and then replace commas by spaces so we can use stringstream to parse XXXX - if( patch->tensor_string ) { - std::string tensor_string = *(patch->tensor_string); - std::replace(tensor_string.begin(),tensor_string.end(),',',' '); - - // std::cout << " tensor_string: " << tensor_string << std::endl; - - std::stringstream os( tensor_string ); - for( guint i = 0; i < 4; ++i ) { - double x = 0.0; - double y = 0.0; - os >> x >> y; - if( !os.fail() ) { - new_patch.setTensorPoint( i, new_patch.getPoint( i, 0 ) + Geom::Point( x, y ) ); - } else { - std::cerr << "Failed to read p: " << i << std::endl; - break; - } - } - } - } - - ++icolumn; - if( max_column < icolumn ) max_column = icolumn; - } - } - ++irow; - } - - // Insure we have a true array. - for( guint i = 0; i < nodes.size(); ++i ) { - nodes[ i ].resize( max_column * 3 + 1 ); - } - - // Set node edge. - for( guint i = 0; i < nodes.size(); ++i ) { - for( guint j = 0; j < nodes[i].size(); ++j ) { - nodes[i][j]->node_edge = MG_NODE_EDGE_NONE; - if( i == 0 ) nodes[i][j]->node_edge |= MG_NODE_EDGE_TOP; - if( i == nodes.size() - 1 ) nodes[i][j]->node_edge |= MG_NODE_EDGE_BOTTOM; - if( j == 0 ) nodes[i][j]->node_edge |= MG_NODE_EDGE_RIGHT; - if( j == nodes[i].size() - 1 ) nodes[i][j]->node_edge |= MG_NODE_EDGE_LEFT; - } - } - - // std::cout << "SPMeshNodeArray::Read: result:" << std::endl; - // print(); - - built = true; - - return same_size; -}; - -/** - Write repr using our array. -*/ -void SPMeshNodeArray::write( SPMeshGradient *mg ) { - - // std::cout << "SPMeshNodeArray::write: entrance:" << std::endl; - // print(); - using Geom::X; - using Geom::Y; - - SPMeshGradient* mg_array = dynamic_cast<SPMeshGradient*>(mg->getArray()); - if (!mg_array) { - // std::cerr << "SPMeshNodeArray::write: missing patches!" << std::endl; - mg_array = mg; - } - - // First we must delete reprs for old mesh rows and patches. We only need to call the - // deleteObject() method, which in turn calls sp_repr_unparent. Since iterators do not play - // well with boost::intrusive::list (which ChildrenList derive from) we need to iterate over a - // copy of the pointers to the objects. - std::vector<SPObject*> children_pointers; - for (auto& row : mg_array->children) { - children_pointers.push_back(&row); - } - - for (auto i : children_pointers) { - i->deleteObject(); - } - - // Now we build new reprs - Inkscape::XML::Node *mesh = mg->getRepr(); - Inkscape::XML::Node *mesh_array = mg_array->getRepr(); - - SPMeshNodeArray* array = &(mg_array->array); - SPMeshPatchI patch0( &(array->nodes), 0, 0 ); - Geom::Point current_p = patch0.getPoint( 0, 0 ); // Side 0, point 0 - - sp_repr_set_svg_double( mesh, "x", current_p[X] ); - sp_repr_set_svg_double( mesh, "y", current_p[Y] ); - - Geom::Point current_p2( mg->x.computed, mg->y.computed ); - - Inkscape::XML::Document *xml_doc = mesh->document(); - guint rows = array->patch_rows(); - for( guint i = 0; i < rows; ++i ) { - - // Write row - Inkscape::XML::Node *row = xml_doc->createElement("svg:meshrow"); - mesh_array->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"); - - SPMeshPatchI patchi( &(array->nodes), i, j ); - - // Add tensor - if( patchi.tensorIsSet() ) { - - std::stringstream is; - - for( guint k = 0; k < 4; ++k ) { - Geom::Point p = patchi.getTensorPoint( k ) - patchi.getPoint( k, 0 ); - is << p[X] << "," << p[Y]; - if( k < 3 ) is << " "; - } - - patch->setAttribute("tensor", is.str().c_str()); - // std::cout << " SPMeshNodeArray::write: tensor: " << is.str() << std::endl; - } - - row->appendChild( patch ); - - // Write sides - for( guint k = 0; k < 4; ++k ) { - - // Only first row has top stop - if( k == 0 && i != 0 ) continue; - - // Only first column has left stop - if( k == 3 && j != 0 ) continue; - - Inkscape::XML::Node *stop = xml_doc->createElement("svg:stop"); - - // Add path - std::stringstream is; - char path_type = patchi.getPathType( k ); - is << path_type; - - std::vector< Geom::Point> p = patchi.getPointsForSide( k ); - current_p = patchi.getPoint( k, 0 ); - - switch ( path_type ) { - case 'l': - is << " " - << ( p[3][X] - current_p[X] ) << "," - << ( p[3][Y] - current_p[Y] ); - break; - case 'L': - is << " " - << p[3][X] << "," - << p[3][Y]; - break; - case 'c': - is << " " - << ( p[1][X] - current_p[X] ) << "," - << ( p[1][Y] - current_p[Y] ) << " " - << ( p[2][X] - current_p[X] ) << "," - << ( p[2][Y] - current_p[Y] ) << " " - << ( p[3][X] - current_p[X] ) << "," - << ( p[3][Y] - current_p[Y] ); - break; - case 'C': - is << " " - << p[1][X] << "," - << p[1][Y] << " " - << p[2][X] << "," - << p[2][Y] << " " - << p[3][X] << "," - << p[3][Y]; - break; - case 'z': - case 'Z': - std::cerr << "SPMeshNodeArray::write(): bad path type" << path_type << std::endl; - break; - default: - std::cerr << "SPMeshNodeArray::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; - // Add stop-color - if( ( k == 0 && i == 0 && j == 0 ) || - ( k == 1 && i == 0 ) || - ( k == 2 ) || - ( k == 3 && j == 0 ) ) { - - // Why are we setting attribute and not style? - //stop->setAttribute("stop-color", patchi.getColor(k).toString().c_str() ); - //stop->setAttribute("stop-opacity", patchi.getOpacity(k) ); - - Inkscape::CSSOStringStream os; - os << "stop-color:" << patchi.getColor(k).toString() << ";stop-opacity:" << patchi.getOpacity(k); - stop->setAttribute("style", os.str().c_str()); - } - patch->appendChild( stop ); - } - } - } -} - -/** - * Find default color based on colors in existing fill. - */ -static SPColor default_color( SPItem *item ) { - - SPColor color( 0.5, 0.0, 0.5 ); - - if ( item->style ) { - SPIPaint const &paint = ( item->style->fill ); // Could pick between style.fill/style.stroke - if ( paint.isColor() ) { - color = paint.value.color; - } else if ( paint.isPaintserver() ) { - SPObject const *server = item->style->getFillPaintServer(); - if ( SP_IS_GRADIENT(server) && SP_GRADIENT(server)->getVector() ) { - SPStop *firstStop = SP_GRADIENT(server)->getVector()->getFirstStop(); - if ( firstStop ) { - color = firstStop->getEffectiveColor(); - } - } - } - } else { - std::cerr << " SPMeshNodeArray: default_color(): No style" << std::endl; - } - - return color; -} - -/** - Create a default mesh. -*/ -void SPMeshNodeArray::create( SPMeshGradient *mg, SPItem *item, Geom::OptRect bbox ) { - - // std::cout << "SPMeshNodeArray::create: Entrance" << std::endl; - - if( !bbox ) { - // Set default size to bounding box if size not given. - std::cerr << "SPMeshNodeArray::create(): bbox empty" << std::endl; - bbox = item->geometricBounds(); - - if( !bbox ) { - std::cerr << "SPMeshNodeArray::create: ERROR: No bounding box!" << std::endl; - return; - } - } - - Geom::Coord const width = bbox->dimensions()[Geom::X]; - Geom::Coord const height = bbox->dimensions()[Geom::Y]; - Geom::Point center = bbox->midpoint(); - - // Must keep repr and array in sync. We have two choices: - // Build the repr first and then "read" it. - // Construct the array and then "write" it. - // We'll do the second. - - // Remove any existing mesh. We could choose to simply scale an existing mesh... - //clear(); - - // We get called twice when a new mesh is created...WHY? - // return if we've already constructed the mesh. - if( !nodes.empty() ) return; - - // Set 'gradientUnits'. Our calculations assume "userSpaceOnUse". - Inkscape::XML::Node *repr = mg->getRepr(); - repr->setAttribute("gradientUnits", "userSpaceOnUse"); - - // Get default color - SPColor color = default_color( item ); - - // Set some corners to white so we can see the mesh. - SPColor white( 1.0, 1.0, 1.0 ); - if (color == white) { - // If default color is white, set other color to black. - white = SPColor( 0.0, 0.0, 0.0 ); - } - - // Get preferences - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - guint prows = prefs->getInt("/tools/mesh/mesh_rows", 1); - guint pcols = prefs->getInt("/tools/mesh/mesh_cols", 1); - - SPMeshGeometry mesh_type = - (SPMeshGeometry) prefs->getInt("/tools/mesh/mesh_geometry", SP_MESH_GEOMETRY_NORMAL); - - if( mesh_type == SP_MESH_GEOMETRY_CONICAL ) { - - // Conical gradient.. for any shape/path using geometric bounding box. - - gdouble rx = width/2.0; - gdouble ry = height/2.0; - - // Start and end angles - gdouble start = 0.0; - gdouble end = 2.0 * M_PI; - - if ( SP_IS_STAR( item ) ) { - // But if it is a star... use star parameters! - SPStar* star = SP_STAR( item ); - center = star->center; - rx = star->r[0]; - ry = star->r[0]; - start = star->arg[0]; - end = start + 2.0 * M_PI; - } - - if ( SP_IS_GENERICELLIPSE( item ) ) { - // For arcs use set start/stop - SPGenericEllipse* arc = SP_GENERICELLIPSE( item ); - center[Geom::X] = arc->cx.computed; - center[Geom::Y] = arc->cy.computed; - rx = arc->rx.computed; - ry = arc->ry.computed; - start = arc->start; - end = arc->end; - if( end <= start ) { - end += 2.0 * M_PI; - } - } - - // std::cout << " start: " << start << " end: " << end << std::endl; - - // IS THIS NECESSARY? - sp_repr_set_svg_double( repr, "x", center[Geom::X] + rx * cos(start) ); - sp_repr_set_svg_double( repr, "y", center[Geom::Y] + ry * sin(start) ); - - guint sections = pcols; - - // If less sections, arc approximation error too great. (Check!) - if( sections < 4 ) sections = 4; - - double arc = (end - start) / (double)sections; - - // See: http://en.wikipedia.org/wiki/B%C3%A9zier_curve - gdouble kappa = 4.0/3.0 * tan(arc/4.0); - gdouble lenx = rx * kappa; - gdouble leny = ry * kappa; - - gdouble s = start; - for( guint i = 0; i < sections; ++i ) { - - SPMeshPatchI patch( &nodes, 0, i ); - - gdouble x0 = center[Geom::X] + rx * cos(s); - gdouble y0 = center[Geom::Y] + ry * sin(s); - gdouble x1 = x0 - lenx * sin(s); - gdouble y1 = y0 + leny * cos(s); - - s += arc; - gdouble x3 = center[Geom::X] + rx * cos(s); - gdouble y3 = center[Geom::Y] + ry * sin(s); - gdouble x2 = x3 + lenx * sin(s); - gdouble y2 = y3 - leny * cos(s); - - patch.setPoint( 0, 0, Geom::Point( x0, y0 ) ); - patch.setPoint( 0, 1, Geom::Point( x1, y1 ) ); - patch.setPoint( 0, 2, Geom::Point( x2, y2 ) ); - patch.setPoint( 0, 3, Geom::Point( x3, y3 ) ); - - patch.setPoint( 2, 0, center ); - patch.setPoint( 3, 0, center ); - - for( guint k = 0; k < 4; ++k ) { - patch.setPathType( k, 'l' ); - patch.setColor( k, (i+k)%2 ? color : white ); - patch.setOpacity( k, 1.0 ); - } - patch.setPathType( 0, 'c' ); - - // Set handle and tensor nodes. - patch.updateNodes(); - - } - - split_row( 0, prows ); - - } else { - - // Normal grid meshes - - if( SP_IS_GENERICELLIPSE( item ) ) { - - // std::cout << "We've got ourselves an arc!" << std::endl; - - SPGenericEllipse* arc = SP_GENERICELLIPSE( item ); - center[Geom::X] = arc->cx.computed; - center[Geom::Y] = arc->cy.computed; - gdouble rx = arc->rx.computed; - gdouble ry = arc->ry.computed; - - gdouble s = -3.0/2.0 * M_PI_2; - - sp_repr_set_svg_double( repr, "x", center[Geom::X] + rx * cos(s) ); - sp_repr_set_svg_double( repr, "y", center[Geom::Y] + ry * sin(s) ); - - gdouble lenx = rx * 4*tan(M_PI_2/4)/3; - gdouble leny = ry * 4*tan(M_PI_2/4)/3; - - SPMeshPatchI patch( &nodes, 0, 0 ); - for( guint i = 0; i < 4; ++i ) { - - gdouble x0 = center[Geom::X] + rx * cos(s); - gdouble y0 = center[Geom::Y] + ry * sin(s); - gdouble x1 = x0 + lenx * cos(s + M_PI_2); - gdouble y1 = y0 + leny * sin(s + M_PI_2); - - s += M_PI_2; - gdouble x3 = center[Geom::X] + rx * cos(s); - gdouble y3 = center[Geom::Y] + ry * sin(s); - gdouble x2 = x3 + lenx * cos(s - M_PI_2); - gdouble y2 = y3 + leny * sin(s - M_PI_2); - - Geom::Point p1( x1, y1 ); - Geom::Point p2( x2, y2 ); - Geom::Point p3( x3, y3 ); - patch.setPoint( i, 1, p1 ); - patch.setPoint( i, 2, p2 ); - patch.setPoint( i, 3, p3 ); - - patch.setPathType( i, 'c' ); - - patch.setColor( i, i%2 ? color : white ); - patch.setOpacity( i, 1.0 ); - } - - // Fill out tensor points - patch.updateNodes(); - - split_row( 0, prows ); - split_column( 0, pcols ); - - // END Arc - - } else if ( SP_IS_STAR( item ) ) { - - // Do simplest thing... assume star is not rounded or randomized. - // (It should be easy to handle the rounded/randomized cases by making - // the appropriate star class function public.) - SPStar* star = SP_STAR( item ); - guint sides = star->sides; - - // std::cout << "We've got ourselves an star! Sides: " << sides << std::endl; - - Geom::Point p0 = sp_star_get_xy( star, SP_STAR_POINT_KNOT1, 0 ); - sp_repr_set_svg_double( repr, "x", p0[Geom::X] ); - sp_repr_set_svg_double( repr, "y", p0[Geom::Y] ); - - for( guint i = 0; i < sides; ++i ) { - - if( star->flatsided ) { - - SPMeshPatchI patch( &nodes, 0, i ); - - patch.setPoint( 0, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT1, i ) ); - guint ii = i+1; - if( ii == sides ) ii = 0; - patch.setPoint( 1, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT1, ii ) ); - patch.setPoint( 2, 0, star->center ); - patch.setPoint( 3, 0, star->center ); - - for( guint s = 0; s < 4; ++s ) { - patch.setPathType( s, 'l' ); - patch.setColor( s, (i+s)%2 ? color : white ); - patch.setOpacity( s, 1.0 ); - } - - // Set handle and tensor nodes. - patch.updateNodes(); - - } else { - - SPMeshPatchI patch0( &nodes, 0, 2*i ); - - patch0.setPoint( 0, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT1, i ) ); - patch0.setPoint( 1, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT2, i ) ); - patch0.setPoint( 2, 0, star->center ); - patch0.setPoint( 3, 0, star->center ); - - guint ii = i+1; - if( ii == sides ) ii = 0; - - SPMeshPatchI patch1( &nodes, 0, 2*i+1 ); - - patch1.setPoint( 0, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT2, i ) ); - patch1.setPoint( 1, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT1, ii ) ); - patch1.setPoint( 2, 0, star->center ); - patch1.setPoint( 3, 0, star->center ); - - for( guint s = 0; s < 4; ++s ) { - patch0.setPathType( s, 'l' ); - patch0.setColor( s, s%2 ? color : white ); - patch0.setOpacity( s, 1.0 ); - patch1.setPathType( s, 'l' ); - patch1.setColor( s, s%2 ? white : color ); - patch1.setOpacity( s, 1.0 ); - } - - // Set handle and tensor nodes. - patch0.updateNodes(); - patch1.updateNodes(); - - } - } - - //print(); - - split_row( 0, prows ); - //split_column( 0, pcols ); - - } else { - - // Generic - - sp_repr_set_svg_double(repr, "x", bbox->min()[Geom::X]); - sp_repr_set_svg_double(repr, "y", bbox->min()[Geom::Y]); - - // Get node array size - guint nrows = prows * 3 + 1; - guint ncols = pcols * 3 + 1; - - gdouble dx = width / (gdouble)(ncols-1.0); - gdouble dy = height / (gdouble)(nrows-1.0); - - Geom::Point p0( mg->x.computed, mg->y.computed ); - - for( guint i = 0; i < nrows; ++i ) { - std::vector< SPMeshNode* > row; - for( guint j = 0; j < ncols; ++j ) { - SPMeshNode* node = new SPMeshNode; - node->p = p0 + Geom::Point( j * dx, i * dy ); - - node->node_edge = MG_NODE_EDGE_NONE; - if( i == 0 ) node->node_edge |= MG_NODE_EDGE_TOP; - if( i == nrows -1 ) node->node_edge |= MG_NODE_EDGE_BOTTOM; - if( j == 0 ) node->node_edge |= MG_NODE_EDGE_LEFT; - if( j == ncols -1 ) node->node_edge |= MG_NODE_EDGE_RIGHT; - - if( i%3 == 0 ) { - - if( j%3 == 0) { - // Corner - node->node_type = MG_NODE_TYPE_CORNER; - node->set = true; - node->color = (i+j)%2 ? color : white; - node->opacity = 1.0; - - } else { - // Side - node->node_type = MG_NODE_TYPE_HANDLE; - node->set = true; - node->path_type = 'c'; - } - - } else { - - if( j%3 == 0) { - // Side - node->node_type = MG_NODE_TYPE_HANDLE; - node->set = true; - node->path_type = 'c'; - } else { - // Tensor - node->node_type = MG_NODE_TYPE_TENSOR; - node->set = false; - } - - } - - row.push_back( node ); - } - nodes.push_back( row ); - } - // End normal - } - - } // If conical - - //print(); - - // Write repr - write( mg ); -} - - -/** - Clear mesh gradient. -*/ -void SPMeshNodeArray::clear() { - - for( guint i = 0; i < nodes.size(); ++i ) { - for( guint j = 0; j < nodes[i].size(); ++j ) { - if( nodes[i][j] ) { - delete nodes[i][j]; - } - } - } - nodes.clear(); -}; - - -/** - Print mesh gradient (for debugging). -*/ -void SPMeshNodeArray::print() { - for( guint i = 0; i < nodes.size(); ++i ) { - std::cout << "New node row:" << std::endl; - for( guint j = 0; j < nodes[i].size(); ++j ) { - if( nodes[i][j] ) { - std::cout.width(4); - std::cout << " Node: " << i << "," << j << ": " - << nodes[i][j]->p - << " Node type: " << nodes[i][j]->node_type - << " Node edge: " << nodes[i][j]->node_edge - << " Set: " << nodes[i][j]->set - << " Path type: " << nodes[i][j]->path_type - << " Stop: " << nodes[i][j]->stop - << std::endl; - } else { - std::cout << "Error: missing mesh node." << std::endl; - } - } // Loop over patches - } // Loop over rows -}; - - - -/* -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 { - -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. -*/ -guint SPMeshNodeArray::patch_rows() { - - return nodes.size()/3; -} - -/** - Number of patch columns. -*/ -guint SPMeshNodeArray::patch_columns() { - if (nodes.empty()) { - return 0; - } - return nodes[0].size()/3; -} - -/** - Inputs: - i, j: Corner draggable indices. - Returns: - true if corners adjacent. - n[] is array of nodes in top/bottom or left/right order. -*/ -bool SPMeshNodeArray::adjacent_corners( guint i, guint j, SPMeshNode* n[4] ) { - - // This works as all corners have indices and they - // are numbered in order by row and column (and - // the node array is rectangular). - - bool adjacent = false; - - guint c1 = i; - guint c2 = j; - if( j < i ) { - c1 = j; - c2 = i; - } - - // Number of corners in a row of patches. - guint ncorners = patch_columns() + 1; - - guint crow1 = c1 / ncorners; - guint crow2 = c2 / ncorners; - guint ccol1 = c1 % ncorners; - guint ccol2 = c2 % ncorners; - - guint nrow = crow1 * 3; - guint ncol = ccol1 * 3; - - // std::cout << " i: " << i - // << " j: " << j - // << " ncorners: " << ncorners - // << " c1: " << c1 - // << " crow1: " << crow1 - // << " ccol1: " << ccol1 - // << " c2: " << c2 - // << " crow2: " << crow2 - // << " ccol2: " << ccol2 - // << " nrow: " << nrow - // << " ncol: " << ncol - // << std::endl; - - // Check for horizontal neighbors - if ( crow1 == crow2 && (ccol2 - ccol1) == 1 ) { - adjacent = true; - for( guint k = 0; k < 4; ++k ) { - n[k] = nodes[nrow][ncol+k]; - } - } - - // Check for vertical neighbors - if ( ccol1 == ccol2 && (crow2 - crow1) == 1 ) { - adjacent = true; - for( guint k = 0; k < 4; ++k ) { - n[k] = nodes[nrow+k][ncol]; - } - } - - return adjacent; -} - -/** - Toggle sides between lineto and curve to if both corners selected. - Input is a list of selected corner draggable indices. -*/ -guint SPMeshNodeArray::side_toggle( std::vector<guint> corners ) { - - guint toggled = 0; - - if( corners.size() < 2 ) return 0; - - for( guint i = 0; i < corners.size()-1; ++i ) { - for( guint j = i+1; j < corners.size(); ++j ) { - - SPMeshNode* n[4]; - if( adjacent_corners( corners[i], corners[j], n ) ) { - - gchar path_type = n[1]->path_type; - switch (path_type) - { - case 'L': - n[1]->path_type = 'C'; - n[2]->path_type = 'C'; - n[1]->set = true; - n[2]->set = true; - break; - - case 'l': - n[1]->path_type = 'c'; - n[2]->path_type = 'c'; - n[1]->set = true; - n[2]->set = true; - break; - - case 'C': { - n[1]->path_type = 'L'; - n[2]->path_type = 'L'; - n[1]->set = false; - n[2]->set = false; - // 'L' acts as if handles are 1/3 of path length from corners. - Geom::Point dp = (n[3]->p - n[0]->p)/3.0; - n[1]->p = n[0]->p + dp; - n[2]->p = n[3]->p - dp; - break; - } - case 'c': { - n[1]->path_type = 'l'; - n[2]->path_type = 'l'; - n[1]->set = false; - n[2]->set = false; - // 'l' acts as if handles are 1/3 of path length from corners. - Geom::Point dp = (n[3]->p - n[0]->p)/3.0; - n[1]->p = n[0]->p + dp; - n[2]->p = n[3]->p - dp; - // std::cout << "Toggle sides: " - // << n[0]->p << " " - // << n[1]->p << " " - // << n[2]->p << " " - // << n[3]->p << " " - // << dp << std::endl; - break; - } - default: - std::cout << "Toggle sides: Invalid path type: " << path_type << std::endl; - } - ++toggled; - } - } - } - if( toggled > 0 ) built = false; - return toggled; -} - -/** - * Converts generic Beziers to Beziers approximating elliptical arcs, preserving handle direction. - * There are infinite possible solutions. The solution chosen here is to generate a section of an - * ellipse that is centered on the intersection of the two lines passing through the two nodes but - * parallel to the other node's handle direction. This is the section of an ellipse that - * corresponds to a quarter of a circle squished and then skewed. - */ -guint SPMeshNodeArray::side_arc( std::vector<guint> corners ) { - - if( corners.size() < 2 ) return 0; - - guint arced = 0; - for( guint i = 0; i < corners.size()-1; ++i ) { - for( guint j = i+1; j < corners.size(); ++j ) { - - SPMeshNode* n[4]; - if( adjacent_corners( corners[i], corners[j], n ) ) { - - gchar path_type = n[1]->path_type; - switch (path_type) - { - case 'L': - case 'l': - std::cerr << "SPMeshNodeArray::side_arc: Can't convert straight lines to arcs." << std::endl; - break; - - case 'C': - case 'c': { - - Geom::Ray ray1( n[0]->p, n[1]->p ); - Geom::Ray ray2( n[3]->p, n[2]->p ); - if( !are_parallel( (Geom::Line)ray1, (Geom::Line)ray2 ) ) { - - Geom::OptCrossing crossing = intersection( ray1, ray2 ); - - if( crossing ) { - - Geom::Point intersection = ray1.pointAt( (*crossing).ta ); - - const double f = 4.0/3.0 * tan( M_PI/2.0/4.0 ); - - Geom::Point h1 = intersection - n[0]->p; - Geom::Point h2 = intersection - n[3]->p; - - n[1]->p = n[0]->p + f*h1; - n[2]->p = n[3]->p + f*h2; - ++arced; - - } else { - std::cerr << "SPMeshNodeArray::side_arc: No crossing, can't turn into arc." << std::endl; - } - } else { - std::cerr << "SPMeshNodeArray::side_arc: Handles parallel, can't turn into arc." << std::endl; - } - break; - } - default: - std::cerr << "SPMeshNodeArray::side_arc: Invalid path type: " << n[1]->path_type << std::endl; - } - } - } - } - if( arced > 0 ) built = false; - return arced; -} - -/** - Toggle sides between lineto and curve to if both corners selected. - Input is a list of selected corner draggable indices. -*/ -guint SPMeshNodeArray::tensor_toggle( std::vector<guint> corners ) { - - // std::cout << "SPMeshNodeArray::tensor_toggle" << std::endl; - - if( corners.size() < 4 ) return 0; - - guint toggled = 0; - - // Number of corners in a row of patches. - guint ncorners = patch_columns() + 1; - - for( guint i = 0; i < corners.size()-3; ++i ) { - for( guint j = i+1; j < corners.size()-2; ++j ) { - for( guint k = j+1; k < corners.size()-1; ++k ) { - for( guint l = k+1; l < corners.size(); ++l ) { - - guint c[4]; - c[0] = corners[i]; - c[1] = corners[j]; - c[2] = corners[k]; - c[3] = corners[l]; - std::sort( c, c+4 ); - - // Check we have four corners of one patch selected - if( c[1]-c[0] == 1 && - c[3]-c[2] == 1 && - c[2]-c[0] == ncorners && - c[3]-c[1] == ncorners && - c[0] % ncorners < ncorners - 1 ) { - - // Patch - guint prow = c[0] / ncorners; - guint pcol = c[0] % ncorners; - - // Upper left node of patch - guint irow = prow * 3; - guint jcol = pcol * 3; - - // std::cout << "tensor::toggle: " - // << c[0] << ", " - // << c[1] << ", " - // << c[2] << ", " - // << c[3] << std::endl; - - // std::cout << "tensor::toggle: " - // << " irow: " << irow - // << " jcol: " << jcol - // << " prow: " << prow - // << " pcol: " << pcol - // << std::endl; - - SPMeshPatchI patch( &nodes, prow, pcol ); - patch.updateNodes(); - - if( patch.tensorIsSet() ) { - // Unset tensor points - nodes[irow+1][jcol+1]->set = false; - nodes[irow+1][jcol+2]->set = false; - nodes[irow+2][jcol+1]->set = false; - nodes[irow+2][jcol+2]->set = false; - } else { - // Set tensor points - nodes[irow+1][jcol+1]->set = true; - nodes[irow+1][jcol+2]->set = true; - nodes[irow+2][jcol+1]->set = true; - nodes[irow+2][jcol+2]->set = true; - } - - ++toggled; - } - } - } - } - } - if( toggled > 0 ) built = false; - return toggled; -} - -/** - Atempts to smooth color transitions across corners. - Input is a list of selected corner draggable indices. -*/ -guint SPMeshNodeArray::color_smooth( std::vector<guint> corners ) { - - // std::cout << "SPMeshNodeArray::color_smooth" << std::endl; - - guint smoothed = 0; - - // Number of corners in a row of patches. - guint ncorners = patch_columns() + 1; - - // Number of node rows and columns - guint ncols = patch_columns() * 3 + 1; - guint nrows = patch_rows() * 3 + 1; - - for( guint i = 0; i < corners.size(); ++i ) { - - guint corner = corners[i]; - // std::cout << "SPMeshNodeArray::color_smooth: " << i << " " << corner << std::endl; - - // Node row & col - guint nrow = (corner / ncorners) * 3; - guint ncol = (corner % ncorners) * 3; - - SPMeshNode* n[7]; - for( guint s = 0; s < 2; ++s ) { - - bool smooth = false; - - // Find neighboring nodes - if( s == 0 ) { - - // Horizontal - if( ncol > 2 && ncol+3 < ncols) { - for( guint j = 0; j < 7; ++j ) { - n[j] = nodes[ nrow ][ ncol - 3 + j ]; - } - smooth = true; - } - - } else { - - // Vertical - if( nrow > 2 && nrow+3 < nrows) { - for( guint j = 0; j < 7; ++j ) { - n[j] = nodes[ nrow - 3 + j ][ ncol ]; - } - smooth = true; - } - } - - if( smooth ) { - - // Let the smoothing begin - // std::cout << " checking: " << ncol << " " << nrow << std::endl; - - // Get initial slopes using closest handles. - double slope[2][3]; - double slope_ave[3]; - double slope_diff[3]; - - // Color of corners - SPColor color0 = n[0]->color; - SPColor color3 = n[3]->color; - SPColor color6 = n[6]->color; - - // Distance nodes from selected corner - Geom::Point d[7]; - for( guint k = 0; k < 7; ++k ) { - d[k]= n[k]->p - n[3]->p; - // std::cout << " d[" << k << "]: " << d[k].length() << std::endl; - } - - double sdm = -1.0; // Slope Diff Max - guint cdm = 0; // Color Diff Max (Which color has the maximum difference in slopes) - for( guint c = 0; c < 3; ++c ) { - if( d[2].length() != 0.0 ) { - slope[0][c] = (color3.v.c[c] - color0.v.c[c]) / d[2].length(); - } - if( d[4].length() != 0.0 ) { - slope[1][c] = (color6.v.c[c] - color3.v.c[c]) / d[4].length(); - } - slope_ave[c] = (slope[0][c]+slope[1][c]) / 2.0; - slope_diff[c] = (slope[0][c]-slope[1][c]); - // std::cout << " color: " << c << " :" - // << color0.v.c[c] << " " - // << color3.v.c[c] << " " - // << color6.v.c[c] - // << " slope: " - // << slope[0][c] << " " - // << slope[1][c] - // << " slope_ave: " << slope_ave[c] - // << " slope_diff: " << slope_diff[c] - // << std::endl; - - // Find color with maximum difference - if( std::abs( slope_diff[c] ) > sdm ) { - sdm = std::abs( slope_diff[c] ); - cdm = c; - } - } - // std::cout << " cdm: " << cdm << std::endl; - - // Find new handle positions: - double length_left = d[0].length(); - double length_right = d[6].length(); - if( slope_ave[ cdm ] != 0.0 ) { - length_left = std::abs( (color3.v.c[cdm] - color0.v.c[cdm]) / slope_ave[ cdm ] ); - length_right = std::abs( (color6.v.c[cdm] - color3.v.c[cdm]) / slope_ave[ cdm ] ); - } - - // Move closest handle a maximum of mid point... but don't shorten - double max = 0.8; - if( length_left > max * d[0].length() && length_left > d[2].length() ) { - std::cout << " Can't smooth left side" << std::endl; - length_left = std::max( max * d[0].length(), d[2].length() ); - } - if( length_right > max * d[6].length() && length_right > d[4].length() ) { - std::cout << " Can't smooth right side" << std::endl; - length_right = std::max( max * d[6].length(), d[4].length() ); - } - - if( d[2].length() != 0.0 ) d[2] *= length_left/d[2].length(); - if( d[4].length() != 0.0 ) d[4] *= length_right/d[4].length(); - - // std::cout << " length_left: " << length_left - // << " d[0]: " << d[0].length() - // << " length_right: " << length_right - // << " d[6]: " << d[6].length() - // << std::endl; - - n[2]->p = n[3]->p + d[2]; - n[4]->p = n[3]->p + d[4]; - - ++smoothed; - } - } - - } - - if( smoothed > 0 ) built = false; - return smoothed; -} - -/** - Pick color from background for selected corners. -*/ -guint SPMeshNodeArray::color_pick( std::vector<guint> icorners, SPItem* item ) { - - // std::cout << "SPMeshNodeArray::color_pick" << std::endl; - - guint picked = 0; - - // Code inspired from clone tracing - - // Setup... - - // We need a copy of the drawing so we can hide the mesh. - Inkscape::Drawing *pick_drawing = new Inkscape::Drawing(); - unsigned pick_visionkey = SPItem::display_key_new(1); - - SPDocument *pick_doc = mg->document; - - pick_drawing->setRoot(pick_doc->getRoot()->invoke_show(*pick_drawing, pick_visionkey, SP_ITEM_SHOW_DISPLAY)); - - item->invoke_hide(pick_visionkey); - - pick_doc->getRoot()->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); - pick_doc->ensureUpToDate(); - - //gdouble pick_zoom = 1.0; // zoom; - //pick_drawing->root()->setTransform(Geom::Scale(pick_zoom)); - pick_drawing->update(); - - // std::cout << " transform: " << std::endl; - // std::cout << item->transform << std::endl; - // std::cout << " i2doc: " << std::endl; - // std::cout << item->i2doc_affine() << std::endl; - // std::cout << " i2dt: " << std::endl; - // std::cout << item->i2dt_affine() << std::endl; - // std::cout << " dt2i: " << std::endl; - // std::cout << item->dt2i_affine() << std::endl; - SPGradient* gr = SP_GRADIENT( mg ); - // if( gr->gradientTransform_set ) { - // std::cout << " gradient transform set: " << std::endl; - // std::cout << gr->gradientTransform << std::endl; - // } else { - // std::cout << " gradient transform not set! " << std::endl; - // } - - // Do picking - for( guint i = 0; i < icorners.size(); ++i ) { - - guint corner = icorners[i]; - - SPMeshNode* n = corners[ corner ]; - - // Region to average over - Geom::Point p = n->p; - // std::cout << " before transform: p: " << p << std::endl; - p *= gr->gradientTransform; - // std::cout << " after transform: p: " << p << std::endl; - p *= item->i2doc_affine(); - // std::cout << " after transform: p: " << p << std::endl; - - // If on edge, move inward - guint cols = patch_columns()+1; - guint rows = patch_rows()+1; - guint col = corner % cols; - guint row = corner / cols; - guint ncol = col * 3; - guint nrow = row * 3; - - const double size = 3.0; - - // Top edge - if( row == 0 ) { - Geom::Point dp = nodes[nrow+1][ncol]->p - p; - p += unit_vector( dp ) * size; - } - // Right edge - if( col == cols-1 ) { - Geom::Point dp = nodes[nrow][ncol-1]->p - p; - p += unit_vector( dp ) * size; - } - // Bottom edge - if( row == rows-1 ) { - Geom::Point dp = nodes[nrow-1][ncol]->p - p; - p += unit_vector( dp ) * size; - } - // Left edge - if( col == 0 ) { - Geom::Point dp = nodes[nrow][ncol+1]->p - p; - p += unit_vector( dp ) * size; - } - - Geom::Rect box( p[Geom::X]-size/2.0, p[Geom::Y]-size/2.0, - p[Geom::X]+size/2.0, p[Geom::Y]+size/2.0 ); - - /* Item integer bbox in points */ - Geom::IntRect ibox = box.roundOutwards(); - - /* Find visible area */ - cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ibox.width(), ibox.height()); - Inkscape::DrawingContext dc(s, ibox.min()); - - /* Render copy and pick color */ - pick_drawing->render(dc, ibox); - double R = 0, G = 0, B = 0, A = 0; - ink_cairo_surface_average_color(s, R, G, B, A); - cairo_surface_destroy(s); - - // std::cout << " p: " << p - // << " box: " << ibox - // << " R: " << R - // << " G: " << G - // << " B: " << B - // << std::endl; - n->color.set( R, G, B ); - } - - pick_doc->getRoot()->invoke_hide(pick_visionkey); - delete pick_drawing; - - picked = 1; // Picking always happens - if( picked > 0 ) built = false; - return picked; -} - -/** - Splits selected rows and/or columns in half (according to the path 't' parameter). - Input is a list of selected corner draggable indices. -*/ -guint SPMeshNodeArray::insert( std::vector<guint> corners ) { - - guint inserted = 0; - - if( corners.size() < 2 ) return 0; - - std::set<guint> columns; - std::set<guint> rows; - - for( guint i = 0; i < corners.size()-1; ++i ) { - for( guint j = i+1; j < corners.size(); ++j ) { - - // This works as all corners have indices and they - // are numbered in order by row and column (and - // the node array is rectangular). - - guint c1 = corners[i]; - guint c2 = corners[j]; - if (c2 < c1) { - c1 = corners[j]; - c2 = corners[i]; - } - - // Number of corners in a row of patches. - guint ncorners = patch_columns() + 1; - - guint crow1 = c1 / ncorners; - guint crow2 = c2 / ncorners; - guint ccol1 = c1 % ncorners; - guint ccol2 = c2 % ncorners; - - // Check for horizontal neighbors - if ( crow1 == crow2 && (ccol2 - ccol1) == 1 ) { - columns.insert( ccol1 ); - } - - // Check for vertical neighbors - if ( ccol1 == ccol2 && (crow2 - crow1) == 1 ) { - rows.insert( crow1 ); - } - } - } - - // Iterate backwards so column/row numbers are not invalidated. - std::set<guint>::reverse_iterator rit; - for (rit=columns.rbegin(); rit != columns.rend(); ++rit) { - split_column( *rit, 0.5); - ++inserted; - } - for (rit=rows.rbegin(); rit != rows.rend(); ++rit) { - split_row( *rit, 0.5); - ++inserted; - } - - if( inserted > 0 ) built = false; - return inserted; -} - -/** - Moves handles in response to a corner node move. - p_old: original position of moved corner node. - corner: the corner node moved (draggable index, i.e. point_i). - selected: list of all corners selected (draggable indices). - op: how other corners should be moved. - Corner node must already have been moved! -*/ -void SPMeshNodeArray::update_handles( guint corner, std::vector< guint > /*selected*/, Geom::Point p_old, MeshNodeOperation /*op*/ ) -{ - if (!draggers_valid) { - std::cerr << "SPMeshNodeArray::update_handles: Draggers not valid!" << std::endl; - return; - } - // assert( draggers_valid ); - - // std::cout << "SPMeshNodeArray::update_handles: " - // << " corner: " << corner - // << " op: " << op - // << std::endl; - - // Find number of patch rows and columns - guint mrow = patch_rows(); - guint mcol = patch_columns(); - - // Number of corners in a row of patches. - guint ncorners = mcol + 1; - - // Find corner row/column - guint crow = corner / ncorners; - guint ccol = corner % ncorners; - - // Find node row/column - guint nrow = crow * 3; - guint ncol = ccol * 3; - - // std::cout << " mrow: " << mrow - // << " mcol: " << mcol - // << " crow: " << crow - // << " ccol: " << ccol - // << " ncorners: " << ncorners - // << " nrow: " << nrow - // << " ncol: " << ncol - // << std::endl; - - // New corner mesh coordinate. - Geom::Point p_new = nodes[nrow][ncol]->p; - - // Corner point move dpg in mesh coordinate system. - Geom::Point dp = p_new - p_old; - - // std::cout << " p_old: " << p_old << std::endl; - // std::cout << " p_new: " << p_new << std::endl; - // std::cout << " dp: " << dp << std::endl; - - // STEP 1: ONLY DO DIRECT MOVE - bool patch[4]; - patch[0] = patch[1] = patch[2] = patch[3] = false; - if( ccol > 0 && crow > 0 ) patch[0] = true; - if( ccol < mcol && crow > 0 ) patch[1] = true; - if( ccol < mcol && crow < mrow ) patch[2] = true; - if( ccol > 0 && crow < mrow ) patch[3] = true; - - // std::cout << patch[0] << " " - // << patch[1] << " " - // << patch[2] << " " - // << patch[3] << std::endl; - - // Move handles - if( patch[0] || patch[1] ) { - if( nodes[nrow-1][ncol]->path_type == 'l' || - nodes[nrow-1][ncol]->path_type == 'L' ) { - Geom::Point s = (nodes[nrow-3][ncol]->p - nodes[nrow][ncol]->p)/3.0; - nodes[nrow-1][ncol ]->p = nodes[nrow][ncol]->p + s; - nodes[nrow-2][ncol ]->p = nodes[nrow-3][ncol]->p - s; - } else { - nodes[nrow-1][ncol ]->p += dp; - } - } - - if( patch[1] || patch[2] ) { - if( nodes[nrow ][ncol+1]->path_type == 'l' || - nodes[nrow ][ncol+1]->path_type == 'L' ) { - Geom::Point s = (nodes[nrow][ncol+3]->p - nodes[nrow][ncol]->p)/3.0; - nodes[nrow ][ncol+1]->p = nodes[nrow][ncol]->p + s; - nodes[nrow ][ncol+2]->p = nodes[nrow][ncol+3]->p - s; - } else { - nodes[nrow ][ncol+1]->p += dp; - } - } - - if( patch[2] || patch[3] ) { - if( nodes[nrow+1][ncol ]->path_type == 'l' || - nodes[nrow+1][ncol ]->path_type == 'L' ) { - Geom::Point s = (nodes[nrow+3][ncol]->p - nodes[nrow][ncol]->p)/3.0; - nodes[nrow+1][ncol ]->p = nodes[nrow][ncol]->p + s; - nodes[nrow+2][ncol ]->p = nodes[nrow+3][ncol]->p - s; - } else { - nodes[nrow+1][ncol ]->p += dp; - } - } - - if( patch[3] || patch[0] ) { - if( nodes[nrow ][ncol-1]->path_type == 'l' || - nodes[nrow ][ncol-1]->path_type == 'L' ) { - Geom::Point s = (nodes[nrow][ncol-3]->p - nodes[nrow][ncol]->p)/3.0; - nodes[nrow ][ncol-1]->p = nodes[nrow][ncol]->p + s; - nodes[nrow ][ncol-2]->p = nodes[nrow][ncol-3]->p - s; - } else { - nodes[nrow ][ncol-1]->p += dp; - } - } - - - // Move tensors - if( patch[0] ) nodes[nrow-1][ncol-1]->p += dp; - if( patch[1] ) nodes[nrow-1][ncol+1]->p += dp; - if( patch[2] ) nodes[nrow+1][ncol+1]->p += dp; - if( patch[3] ) nodes[nrow+1][ncol-1]->p += dp; - - // // Check if neighboring corners are selected. - - // bool do_scale = false; - - // bool do_scale_xp = do_scale; - // bool do_scale_xn = do_scale; - // bool do_scale_yp = do_scale; - // bool do_scale_yn = do_scale; - - // if( ccol < mcol+1 ) { - // if( std::find( sc.begin(), sc.end(), point_i + 1 ) != sc.end() ) { - // do_scale_xp = false; - // std::cout << " Not scaling x+" << std::endl; - // } - // } - - // if( ccol > 0 ) { - // if( std::find( sc.begin(), sc.end(), point_i - 1 ) != sc.end() ) { - // do_scale_xn = false; - // std::cout << " Not scaling x-" << std::endl; - // } - // } - - // if( crow < mrow+1 ) { - // if( std::find( sc.begin(), sc.end(), point_i + ncorners ) != sc.end() ) { - // do_scale_yp = false; - // std::cout << " Not scaling y+" << std::endl; - // } - // } - - // if( crow > 0 ) { - // if( std::find( sc.begin(), sc.end(), point_i - ncorners ) != sc.end() ) { - // do_scale_yn = false; - // std::cout << " Not scaling y-" << std::endl; - // } - // } - - // // We have four patches to adjust... - // for ( guint k = 0; k < 4; ++k ) { - - // bool do_scale_x = do_scale; - // bool do_scale_y = do_scale; - - // SPMeshNode* pnodes[4][4]; - - // // Load up matrix - // switch (k) { - - // case 0: - // if( crow < mrow+1 && ccol < mcol+1 ) { - // // Bottom right patch - - // do_scale_x = do_scale_xp; - // do_scale_y = do_scale_yp; - - // for( guint i = 0; i < 4; ++i ) { - // for( guint j = 0; j< 4; ++j ) { - // pnodes[i][j] = mg->array.nodes[nrow+i][nrow+j]; - // } - // } - // } - // break; - - // case 1: - // if( crow < mrow+1 && ccol > 0 ) { - // // Bottom left patch (note x, y swapped) - - // do_scale_y = do_scale_xn; - // do_scale_x = do_scale_yp; - - // for( guint i = 0; i < 4; ++i ) { - // for( guint j = 0; j< 4; ++j ) { - // pnodes[j][i] = mg->array.nodes[nrow+i][nrow-j]; - // } - // } - // } - // break; - - // case 2: - // if( crow > 0 && ccol > 0 ) { - // // Top left patch - - // do_scale_x = do_scale_xn; - // do_scale_y = do_scale_yn; - - // for( guint i = 0; i < 4; ++i ) { - // for( guint j = 0; j< 4; ++j ) { - // pnodes[i][j] = mg->array.nodes[nrow-i][nrow-j]; - // } - // } - // } - // break; - - // case 3: - // if( crow > 0 && ccol < mcol+1 ) { - // // Top right patch (note x, y swapped) - - // do_scale_y = do_scale_xp; - // do_scale_x = do_scale_yn; - - // for( guint i = 0; i < 4; ++i ) { - // for( guint j = 0; j< 4; ++j ) { - // pnodes[j][i] = mg->array.nodes[nrow-i][nrow+j]; - // } - // } - // } - // break; - // } - - // // Now we must move points in both x and y. - // // There are upto six points to move: P01, P02, P11, P12, P21, P22. - // // (The points P10, P20 will be moved in another branch of the loop. - // // The points P03, P13, P23, P33, P32, P31, P30 are not moved.) - // // - // // P00 P01 P02 P03 - // // P10 P11 P12 P13 - // // P20 P21 P22 P23 - // // P30 P31 P32 P33 - // // - // // The goal is to preserve the direction of the handle! - - - // Geom::Point dsx_new = pnodes[0][3]->p - pnodes[0][0]->p; // New side x - // Geom::Point dsy_new = pnodes[3][0]->p - pnodes[0][0]->p; // New side y - // Geom::Point dsx_old = pnodes[0][3]->p - pcg_old; // Old side x - // Geom::Point dsy_old = pnodes[3][0]->p - pcg_old; // Old side y - - - // double scale_factor_x = 1.0; - // if( dsx_old.length() != 0.0 ) scale_factor_x = dsx_new.length()/dsx_old.length(); - - // double scale_factor_y = 1.0; - // if( dsy_old.length() != 0.0 ) scale_factor_y = dsy_new.length()/dsy_old.length(); - - - // if( do_scalex && do_scaley ) { - - // // We have six point to move. - - // // P01 - // Geom::Point dp01 = pnodes[0][1] - pcg_old; - // dp01 *= scale_factor_x; - // pnodes[0][1] = pnodes[0][0] + dp01; - - // // P02 - // Geom::Point dp02 = pnodes[0][2] - pnodes[0][3]; - // dp02 *= scale_factor_x; - // pnodes[0][2] = pnodes[0][3] + dp02; - - // // P11 - // Geom::Point dp11 = pnodes[1][1] - pcg_old; - // dp11 *= scale_factor_x; - // pnodes[1][1] = pnodes[0][0] + dp11; - - - - // // P21 - // Geom::Point dp21 = pnodes[2][1] - pnodes[3][0]; - // dp21 *= scale_factor_x; - // dp21 *= scale_factor_y; - // pnodes[2][1] = pnodes[3][0] + dp21; - - - // Geom::Point dsx1 = pnodes[0][1]->p - -} - - -SPCurve * SPMeshNodeArray::outline_path() { - - SPCurve *outline = new SPCurve(); - - if (nodes.empty() ) { - std::cerr << "SPMeshNodeArray::outline_path: empty array!" << std::endl; - return outline; - } - - outline->moveto( nodes[0][0]->p ); - - int ncol = nodes[0].size(); - int nrow = nodes.size(); - - // Top - for (int i = 1; i < ncol; i += 3 ) { - outline->curveto( nodes[0][i]->p, nodes[0][i+1]->p, nodes[0][i+2]->p); - } - - // Right - for (int i = 1; i < nrow; i += 3 ) { - outline->curveto( nodes[i][ncol-1]->p, nodes[i+1][ncol-1]->p, nodes[i+2][ncol-1]->p); - } - - // Bottom (right to left) - for (int i = 1; i < ncol; i += 3 ) { - outline->curveto( nodes[nrow-1][ncol-i-1]->p, nodes[nrow-1][ncol-i-2]->p, nodes[nrow-1][ncol-i-3]->p); - } - - // Left (bottom to top) - for (int i = 1; i < nrow; i += 3 ) { - outline->curveto( nodes[nrow-i-1][0]->p, nodes[nrow-i-2][0]->p, nodes[nrow-i-3][0]->p); - } - - outline->closepath(); - - return outline; -} - -void SPMeshNodeArray::transform(Geom::Affine const &m) { - - for (int i = 0; i < nodes[0].size(); ++i) { - for (int j = 0; j < nodes.size(); ++j) { - nodes[j][i]->p *= m; - } - } -} - -// Transform mesh to fill box. Return true if mesh transformed. -bool SPMeshNodeArray::fill_box(Geom::OptRect &box) { - - // If gradientTransfor is set (as happens when an object is transformed - // with the "optimized" preferences set true), we need to remove it. - if (mg->gradientTransform_set) { - Geom::Affine gt = mg->gradientTransform; - transform( gt ); - mg->gradientTransform_set = false; - mg->gradientTransform.setIdentity(); - } - - SPCurve *outline = outline_path(); - Geom::OptRect mesh_bbox = outline->get_pathvector().boundsExact(); - outline->unref(); - - if ((*mesh_bbox).width() == 0 || (*mesh_bbox).height() == 0) { - return false; - } - - double scale_x = (*box).width() /(*mesh_bbox).width() ; - double scale_y = (*box).height()/(*mesh_bbox).height(); - - Geom::Translate t1(-(*mesh_bbox).min()); - Geom::Scale scale(scale_x,scale_y); - Geom::Translate t2((*box).min()); - Geom::Affine trans = t1 * scale * t2; - if (!trans.isIdentity() ) { - transform(trans); - write( mg ); - mg->requestModified(SP_OBJECT_MODIFIED_FLAG); - return true; - } - - return false; -} - -// Defined in gradient-chemistry.cpp -guint32 average_color(guint32 c1, guint32 c2, gdouble p); - -/** - Split a row into n equal parts. -*/ -void SPMeshNodeArray::split_row( unsigned int row, unsigned int n ) { - - double nn = n; - if( n > 1 ) split_row( row, (nn-1)/nn ); - if( n > 2 ) split_row( row, n-1 ); -} - -/** - Split a column into n equal parts. -*/ -void SPMeshNodeArray::split_column( unsigned int col, unsigned int n ) { - - double nn = n; - if( n > 1 ) split_column( col, (nn-1)/nn ); - if( n > 2 ) split_column( col, n-1 ); -} - -/** - Split a row into two rows at coord (fraction of row height). -*/ -void SPMeshNodeArray::split_row( unsigned int row, double coord ) { - - // std::cout << "Splitting row: " << row << " at " << coord << std::endl; - // print(); - assert( coord >= 0.0 && coord <= 1.0 ); - assert( row < patch_rows() ); - - built = false; - - // First step is to ensure that handle and tensor points are up-to-date if they are not set. - // (We can't do this on the fly as we overwrite the necessary points to do the calculation - // during the update.) - for( guint j = 0; j < patch_columns(); ++ j ) { - SPMeshPatchI patch( &nodes, row, j ); - patch.updateNodes(); - } - - // Add three new rows of empty nodes - for( guint i = 0; i < 3; ++i ) { - std::vector< SPMeshNode* > new_row; - for( guint j = 0; j < nodes[0].size(); ++j ) { - SPMeshNode* new_node = new SPMeshNode; - new_row.push_back( new_node ); - } - nodes.insert( nodes.begin()+3*(row+1), new_row ); - } - - guint i = 3 * row; // Convert from patch row to node row - for( guint j = 0; j < nodes[i].size(); ++j ) { - - // std::cout << "Splitting row: column: " << j << std::endl; - - Geom::Point p[4]; - for( guint k = 0; k < 4; ++k ) { - guint n = k; - if( k == 3 ) n = 6; // Bottom patch row has been shifted by new rows - p[k] = nodes[i+n][j]->p; - // std::cout << p[k] << std::endl; - } - - Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] ); - - std::pair<Geom::BezierCurveN<3>, Geom::BezierCurveN<3> > b_new = - b.subdivide( coord ); - - // Update points - for( guint n = 0; n < 4; ++n ) { - nodes[i+n ][j]->p = b_new.first[n]; - nodes[i+n+3][j]->p = b_new.second[n]; - // std::cout << b_new.first[n] << " " << b_new.second[n] << std::endl; - } - - if( nodes[i][j]->node_type == MG_NODE_TYPE_CORNER ) { - // We are splitting a side - - // Path type stored in handles. - gchar path_type = nodes[i+1][j]->path_type; - nodes[i+4][j]->path_type = path_type; - nodes[i+5][j]->path_type = path_type; - bool set = nodes[i+1][j]->set; - nodes[i+4][j]->set = set; - nodes[i+5][j]->set = set; - nodes[i+4][j]->node_type = MG_NODE_TYPE_HANDLE; - nodes[i+5][j]->node_type = MG_NODE_TYPE_HANDLE; - - // Color stored in corners - guint c0 = nodes[i ][j]->color.toRGBA32( 1.0 ); - guint c1 = nodes[i+6][j]->color.toRGBA32( 1.0 ); - gdouble o0 = nodes[i ][j]->opacity; - gdouble o1 = nodes[i+6][j]->opacity; - guint cnew = average_color( c0, c1, coord ); - gdouble onew = o0 * (1.0 - coord) + o1 * coord; - nodes[i+3][j]->color.set( cnew ); - nodes[i+3][j]->opacity = onew; - nodes[i+3][j]->node_type = MG_NODE_TYPE_CORNER; - nodes[i+3][j]->set = true; - - } else { - // We are splitting a middle - - bool set = nodes[i+1][j]->set || nodes[i+2][j]->set; - nodes[i+4][j]->set = set; - nodes[i+5][j]->set = set; - nodes[i+4][j]->node_type = MG_NODE_TYPE_TENSOR; - nodes[i+5][j]->node_type = MG_NODE_TYPE_TENSOR; - - // Path type, if different, choose l -> L -> c -> C. - gchar path_type0 = nodes[i ][j]->path_type; - gchar path_type1 = nodes[i+6][j]->path_type; - gchar path_type = 'l'; - if( path_type0 == 'L' || path_type1 == 'L') path_type = 'L'; - if( path_type0 == 'c' || path_type1 == 'c') path_type = 'c'; - if( path_type0 == 'C' || path_type1 == 'C') path_type = 'C'; - nodes[i+3][j]->path_type = path_type; - nodes[i+3][j]->node_type = MG_NODE_TYPE_HANDLE; - if( path_type == 'c' || path_type == 'C' ) nodes[i+3][j]->set = true; - - } - - nodes[i+3][j]->node_edge = MG_NODE_EDGE_NONE; - nodes[i+4][j]->node_edge = MG_NODE_EDGE_NONE; - nodes[i+5][j]->node_edge = MG_NODE_EDGE_NONE;; - if( j == 0 ) { - nodes[i+3][j]->node_edge |= MG_NODE_EDGE_LEFT; - nodes[i+4][j]->node_edge |= MG_NODE_EDGE_LEFT; - nodes[i+5][j]->node_edge |= MG_NODE_EDGE_LEFT; - } - if( j == nodes[i].size() - 1 ) { - nodes[i+3][j]->node_edge |= MG_NODE_EDGE_RIGHT; - nodes[i+4][j]->node_edge |= MG_NODE_EDGE_RIGHT; - nodes[i+5][j]->node_edge |= MG_NODE_EDGE_RIGHT; - } - } - - // std::cout << "Splitting row: result:" << std::endl; - // print(); -} - - - -/** - Split a column into two columns at coord (fraction of column width). -*/ -void SPMeshNodeArray::split_column( unsigned int col, double coord ) { - - // std::cout << "Splitting column: " << col << " at " << coord << std::endl; - // print(); - assert( coord >= 0.0 && coord <= 1.0 ); - assert( col < patch_columns() ); - - built = false; - - // First step is to ensure that handle and tensor points are up-to-date if they are not set. - // (We can't do this on the fly as we overwrite the necessary points to do the calculation - // during the update.) - for( guint i = 0; i < patch_rows(); ++ i ) { - SPMeshPatchI patch( &nodes, i, col ); - patch.updateNodes(); - } - - guint j = 3 * col; // Convert from patch column to node column - for( guint i = 0; i < nodes.size(); ++i ) { - - // std::cout << "Splitting column: row: " << i << std::endl; - - Geom::Point p[4]; - for( guint k = 0; k < 4; ++k ) { - p[k] = nodes[i][j+k]->p; - } - - Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] ); - - std::pair<Geom::BezierCurveN<3>, Geom::BezierCurveN<3> > b_new = - b.subdivide( coord ); - - // Add three new nodes - for( guint n = 0; n < 3; ++n ) { - SPMeshNode* new_node = new SPMeshNode; - nodes[i].insert( nodes[i].begin()+j+3, new_node ); - } - - // Update points - for( guint n = 0; n < 4; ++n ) { - nodes[i][j+n]->p = b_new.first[n]; - nodes[i][j+n+3]->p = b_new.second[n]; - } - - if( nodes[i][j]->node_type == MG_NODE_TYPE_CORNER ) { - // We are splitting a side - - // Path type stored in handles. - gchar path_type = nodes[i][j+1]->path_type; - nodes[i][j+4]->path_type = path_type; - nodes[i][j+5]->path_type = path_type; - bool set = nodes[i][j+1]->set; - nodes[i][j+4]->set = set; - nodes[i][j+5]->set = set; - nodes[i][j+4]->node_type = MG_NODE_TYPE_HANDLE; - nodes[i][j+5]->node_type = MG_NODE_TYPE_HANDLE; - - // Color stored in corners - guint c0 = nodes[i][j ]->color.toRGBA32( 1.0 ); - guint c1 = nodes[i][j+6]->color.toRGBA32( 1.0 ); - gdouble o0 = nodes[i][j ]->opacity; - gdouble o1 = nodes[i][j+6]->opacity; - guint cnew = average_color( c0, c1, coord ); - gdouble onew = o0 * (1.0 - coord) + o1 * coord; - nodes[i][j+3]->color.set( cnew ); - nodes[i][j+3]->opacity = onew; - nodes[i][j+3]->node_type = MG_NODE_TYPE_CORNER; - nodes[i][j+3]->set = true; - - } else { - // We are splitting a middle - - bool set = nodes[i][j+1]->set || nodes[i][j+2]->set; - nodes[i][j+4]->set = set; - nodes[i][j+5]->set = set; - nodes[i][j+4]->node_type = MG_NODE_TYPE_TENSOR; - nodes[i][j+5]->node_type = MG_NODE_TYPE_TENSOR; - - // Path type, if different, choose l -> L -> c -> C. - gchar path_type0 = nodes[i][j ]->path_type; - gchar path_type1 = nodes[i][j+6]->path_type; - gchar path_type = 'l'; - if( path_type0 == 'L' || path_type1 == 'L') path_type = 'L'; - if( path_type0 == 'c' || path_type1 == 'c') path_type = 'c'; - if( path_type0 == 'C' || path_type1 == 'C') path_type = 'C'; - nodes[i][j+3]->path_type = path_type; - nodes[i][j+3]->node_type = MG_NODE_TYPE_HANDLE; - if( path_type == 'c' || path_type == 'C' ) nodes[i][j+3]->set = true; - - } - - nodes[i][j+3]->node_edge = MG_NODE_EDGE_NONE; - nodes[i][j+4]->node_edge = MG_NODE_EDGE_NONE; - nodes[i][j+5]->node_edge = MG_NODE_EDGE_NONE;; - if( i == 0 ) { - nodes[i][j+3]->node_edge |= MG_NODE_EDGE_TOP; - nodes[i][j+4]->node_edge |= MG_NODE_EDGE_TOP; - nodes[i][j+5]->node_edge |= MG_NODE_EDGE_TOP; - } - if( i == nodes.size() - 1 ) { - nodes[i][j+3]->node_edge |= MG_NODE_EDGE_BOTTOM; - nodes[i][j+4]->node_edge |= MG_NODE_EDGE_BOTTOM; - nodes[i][j+5]->node_edge |= MG_NODE_EDGE_BOTTOM; - } - - } - - // std::cout << "Splitting col: result:" << std::endl; - // print(); -} - -/* - 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:fileencoding=utf-8:textwidth=99 : |
