1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /** \file
3    A group of classes and functions for manipulating mesh gradients.
4 
5    A mesh is made up of an array of patches. Each patch has four sides and four corners. The sides can
6    be shared between two patches and the corners between up to four.
7 
8    The order of the points for each side always goes from left to right or top to bottom.
9    For sides 2 and 3 the points must be reversed when used (as in calls to cairo functions).
10 
11    Two patches: (C=corner, S=side, H=handle, T=tensor)
12 
13                          C0   H1  H2 C1 C0 H1  H2  C1
14                           + ---------- + ---------- +
15                           |     S0     |     S0     |
16                        H1 |  T0    T1  |H1 T0   T1  | H1
17                           |S3        S1|S3        S1|
18                        H2 |  T3    T2  |H2 T3   T2  | H2
19                           |     S2     |     S2     |
20                           + ---------- + ---------- +
21                          C3   H1  H2 C2 C3 H1  H2   C2
22 
23    The mesh is stored internally as an array of nodes that includes the tensor nodes.
24 
25    Note: This code uses tensor points which are not part of the SVG2 plan at the moment.
26    Including tensor points was motivated by a desire to experiment with their usefulness
27    in smoothing color transitions. There doesn't seem to be much advantage for that
28    purpose. However including them internally allows for storing all the points in
29    an array which simplifies things like inserting new rows or columns.
30 */
31 
32 /*
33  * Authors:
34  *   Tavmjong Bah <tavmjong@free.fr>
35  *
36  * Copyright  (C) 2012, 2015 Tavmjong Bah
37  *
38  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
39  */
40 
41 #include <glibmm.h>
42 #include <set>
43 
44 // For color picking
45 #include "display/drawing.h"
46 #include "display/drawing-context.h"
47 #include "display/cairo-utils.h"
48 #include "document.h"
49 #include "sp-root.h"
50 
51 #include "sp-mesh-gradient.h"
52 #include "sp-mesh-array.h"
53 #include "sp-mesh-row.h"
54 #include "sp-mesh-patch.h"
55 #include "sp-stop.h"
56 #include "display/curve.h"
57 
58 // For new mesh creation
59 #include "preferences.h"
60 #include "sp-ellipse.h"
61 #include "sp-star.h"
62 
63 // For writing color/opacity to style
64 #include "svg/css-ostringstream.h"
65 
66 // For default color
67 #include "style.h"
68 #include "svg/svg-color.h"
69 
70 
71 // Includes bezier-curve.h, ray.h, crossing.h
72 #include "2geom/line.h"
73 
74 #include "xml/repr.h"
75 #include <cmath>
76 #include <algorithm>
77 
78 enum { ROW, COL };
79 
SPMeshPatchI(std::vector<std::vector<SPMeshNode * >> * n,int r,int c)80 SPMeshPatchI::SPMeshPatchI( std::vector<std::vector< SPMeshNode* > > * n, int r, int c ) {
81 
82     nodes = n;
83     row = r*3; // Convert from patch array to node array
84     col = c*3;
85 
86     guint i = 0;
87     if( row != 0 ) i = 1;
88     for( ; i < 4; ++i ) {
89         if( nodes->size() < row+i+1 ) {
90             std::vector< SPMeshNode* > row;
91             nodes->push_back( row );
92         }
93 
94         guint j = 0;
95         if( col != 0 ) j = 1;
96         for( ; j < 4; ++j ) {
97             if( (*nodes)[row+i].size() < col+j+1 ){
98                 SPMeshNode* node = new SPMeshNode;
99                 // Ensure all nodes know their type.
100                 node->node_type = MG_NODE_TYPE_HANDLE;
101                 if( (i == 0 || i == 3) && (j == 0 || j == 3 ) ) node->node_type = MG_NODE_TYPE_CORNER;
102                 if( (i == 1 || i == 2) && (j == 1 || j == 2 ) ) node->node_type = MG_NODE_TYPE_TENSOR;
103                 (*nodes)[row+i].push_back( node );
104             }
105         }
106     }
107 }
108 
109 /**
110    Returns point for side in proper order for patch
111 */
getPoint(guint s,guint pt)112 Geom::Point SPMeshPatchI::getPoint( guint s, guint pt ) {
113 
114     assert( s < 4 );
115     assert( pt < 4 );
116 
117     Geom::Point p;
118     switch ( s ) {
119         case 0:
120             p = (*nodes)[ row      ][ col+pt   ]->p;
121             break;
122         case 1:
123             p = (*nodes)[ row+pt   ][ col+3    ]->p;
124             break;
125         case 2:
126             p = (*nodes)[ row+3    ][ col+3-pt ]->p;
127             break;
128         case 3:
129             p = (*nodes)[ row+3-pt ][ col      ]->p;
130             break;
131     }
132     return p;
133 
134 };
135 
136 /**
137    Returns vector of points for a side in proper order for a patch (clockwise order).
138 */
getPointsForSide(guint i)139 std::vector< Geom::Point > SPMeshPatchI::getPointsForSide( guint i ) {
140 
141     assert( i < 4 );
142 
143     std::vector< Geom::Point> points;
144     points.push_back( getPoint( i, 0 ) );
145     points.push_back( getPoint( i, 1 ) );
146     points.push_back( getPoint( i, 2 ) );
147     points.push_back( getPoint( i, 3 ) );
148     return points;
149 };
150 
151 
152 /**
153    Set point for side in proper order for patch
154 */
setPoint(guint s,guint pt,Geom::Point p,bool set)155 void SPMeshPatchI::setPoint( guint s, guint pt, Geom::Point p, bool set ) {
156 
157     assert( s < 4 );
158     assert( pt < 4 );
159 
160     NodeType node_type = MG_NODE_TYPE_CORNER;
161     if( pt == 1 || pt == 2 ) node_type = MG_NODE_TYPE_HANDLE;
162 
163     // std::cout << "SPMeshPatchI::setPoint: s: " << s
164     //           << " pt: " << pt
165     //           << " p: " << p
166     //           << " node_type: " << node_type
167     //           << " set: " << set
168     //           << " row: " << row
169     //           << " col: " << col << std::endl;
170     switch ( s ) {
171         case 0:
172             (*nodes)[ row      ][ col+pt   ]->p = p;
173             (*nodes)[ row      ][ col+pt   ]->set = set;
174             (*nodes)[ row      ][ col+pt   ]->node_type = node_type;
175             break;
176         case 1:
177             (*nodes)[ row+pt   ][ col+3    ]->p = p;
178             (*nodes)[ row+pt   ][ col+3    ]->set = set;
179             (*nodes)[ row+pt   ][ col+3    ]->node_type = node_type;
180             break;
181         case 2:
182             (*nodes)[ row+3    ][ col+3-pt ]->p = p;
183             (*nodes)[ row+3    ][ col+3-pt ]->set = set;
184             (*nodes)[ row+3    ][ col+3-pt ]->node_type = node_type;
185             break;
186         case 3:
187             (*nodes)[ row+3-pt ][ col      ]->p = p;
188             (*nodes)[ row+3-pt ][ col      ]->set = set;
189             (*nodes)[ row+3-pt ][ col      ]->node_type = node_type;
190             break;
191     }
192 
193 };
194 
195 /**
196    Get path type for side (stored in handle nodes).
197 */
getPathType(guint s)198 gchar SPMeshPatchI::getPathType( guint s ) {
199 
200     assert( s < 4 );
201 
202     gchar type = 'x';
203 
204     switch ( s ) {
205         case 0:
206             type = (*nodes)[ row   ][ col+1 ]->path_type;
207             break;
208         case 1:
209             type = (*nodes)[ row+1 ][ col+3 ]->path_type;
210             break;
211         case 2:
212             type = (*nodes)[ row+3 ][ col+2 ]->path_type;
213             break;
214         case 3:
215             type = (*nodes)[ row+2 ][ col   ]->path_type;
216             break;
217     }
218 
219     return type;
220 };
221 
222 /**
223    Set path type for side (stored in handle nodes).
224 */
setPathType(guint s,gchar t)225 void SPMeshPatchI::setPathType( guint s, gchar t ) {
226 
227     assert( s < 4 );
228 
229     switch ( s ) {
230         case 0:
231             (*nodes)[ row   ][ col+1 ]->path_type = t;
232             (*nodes)[ row   ][ col+2 ]->path_type = t;
233             break;
234         case 1:
235             (*nodes)[ row+1 ][ col+3 ]->path_type = t;
236             (*nodes)[ row+2 ][ col+3 ]->path_type = t;
237             break;
238         case 2:
239             (*nodes)[ row+3 ][ col+1 ]->path_type = t;
240             (*nodes)[ row+3 ][ col+2 ]->path_type = t;
241             break;
242         case 3:
243             (*nodes)[ row+1 ][ col   ]->path_type = t;
244             (*nodes)[ row+2 ][ col   ]->path_type = t;
245             break;
246     }
247 
248 };
249 
250 /**
251    Set tensor control point for "corner" i.
252  */
setTensorPoint(guint i,Geom::Point p)253 void SPMeshPatchI::setTensorPoint( guint i, Geom::Point p ) {
254 
255     assert( i < 4 );
256     switch ( i ) {
257         case 0:
258             (*nodes)[ row + 1 ][ col + 1 ]->p = p;
259             (*nodes)[ row + 1 ][ col + 1 ]->set = true;
260             (*nodes)[ row + 1 ][ col + 1 ]->node_type = MG_NODE_TYPE_TENSOR;
261             break;
262         case 1:
263             (*nodes)[ row + 1 ][ col + 2 ]->p = p;
264             (*nodes)[ row + 1 ][ col + 2 ]->set = true;
265             (*nodes)[ row + 1 ][ col + 2 ]->node_type = MG_NODE_TYPE_TENSOR;
266             break;
267         case 2:
268             (*nodes)[ row + 2 ][ col + 2 ]->p = p;
269             (*nodes)[ row + 2 ][ col + 2 ]->set = true;
270             (*nodes)[ row + 2 ][ col + 2 ]->node_type = MG_NODE_TYPE_TENSOR;
271             break;
272         case 3:
273             (*nodes)[ row + 2 ][ col + 1 ]->p = p;
274             (*nodes)[ row + 2 ][ col + 1 ]->set = true;
275             (*nodes)[ row + 2 ][ col + 1 ]->node_type = MG_NODE_TYPE_TENSOR;
276             break;
277     }
278 }
279 
280 /**
281    Return if any tensor control point is set.
282  */
tensorIsSet()283 bool SPMeshPatchI::tensorIsSet() {
284     for( guint i = 0; i < 4; ++i ) {
285         if( tensorIsSet( i ) ) {
286             return true;
287         }
288     }
289     return false;
290 }
291 
292 /**
293    Return if tensor control point for "corner" i is set.
294  */
tensorIsSet(unsigned int i)295 bool SPMeshPatchI::tensorIsSet( unsigned int i ) {
296 
297     assert( i < 4 );
298 
299     bool set = false;
300     switch ( i ) {
301         case 0:
302             set = (*nodes)[ row + 1 ][ col + 1 ]->set;
303             break;
304         case 1:
305             set = (*nodes)[ row + 1 ][ col + 2 ]->set;
306             break;
307         case 2:
308             set = (*nodes)[ row + 2 ][ col + 2 ]->set;
309             break;
310         case 3:
311             set = (*nodes)[ row + 2 ][ col + 1 ]->set;
312             break;
313     }
314     return set;
315 }
316 
317 /**
318    Return tensor control point for "corner" i.
319    If not set, returns calculated (Coons) point.
320  */
getTensorPoint(guint k)321 Geom::Point SPMeshPatchI::getTensorPoint( guint k ) {
322 
323     assert( k < 4 );
324 
325     guint i = 0;
326     guint j = 0;
327 
328 
329     switch ( k ) {
330         case 0:
331             i = 1;
332             j = 1;
333             break;
334         case 1:
335             i = 1;
336             j = 2;
337             break;
338         case 2:
339             i = 2;
340             j = 2;
341             break;
342         case 3:
343             i = 2;
344             j = 1;
345             break;
346     }
347 
348     Geom::Point p;
349     if( (*nodes)[ row + i ][ col + j ]->set ) {
350         p = (*nodes)[ row + i ][ col + j ]->p;
351     } else {
352         p = coonsTensorPoint( k );
353     }
354     return p;
355 }
356 
357 /**
358    Find default tensor point (equivalent point to Coons Patch).
359    Formulas defined in PDF spec.
360    Equivalent to 1/3 of side length from corner for square patch.
361  */
coonsTensorPoint(guint i)362 Geom::Point SPMeshPatchI::coonsTensorPoint( guint i ) {
363 
364     Geom::Point t;
365     Geom::Point p[4][4]; // Points in PDF notation
366 
367     p[0][0] = getPoint( 0, 0 );
368     p[0][1] = getPoint( 0, 1 );
369     p[0][2] = getPoint( 0, 2 );
370     p[0][3] = getPoint( 0, 3 );
371     p[1][0] = getPoint( 3, 2 );
372     p[1][3] = getPoint( 1, 1 );
373     p[2][0] = getPoint( 3, 1 );
374     p[2][3] = getPoint( 1, 2 );
375     p[3][0] = getPoint( 2, 3 );
376     p[3][1] = getPoint( 2, 2 );
377     p[3][2] = getPoint( 2, 1 );
378     p[3][3] = getPoint( 2, 0 );
379 
380     switch ( i ) {
381         case 0:
382             t = ( -4.0 *   p[0][0] +
383                    6.0 * ( p[0][1] + p[1][0] ) +
384                   -2.0 * ( p[0][3] + p[3][0] ) +
385                    3.0 * ( p[3][1] + p[1][3] ) +
386                   -1.0 *   p[3][3] ) / 9.0;
387             break;
388 
389         case 1:
390             t = ( -4.0 *   p[0][3] +
391                    6.0 * ( p[0][2] + p[1][3] ) +
392                   -2.0 * ( p[0][0] + p[3][3] ) +
393                    3.0 * ( p[3][2] + p[1][0] ) +
394                   -1.0 *   p[3][0] ) / 9.0;
395             break;
396 
397         case 2:
398             t = ( -4.0 *   p[3][3] +
399                    6.0 * ( p[3][2] + p[2][3] ) +
400                   -2.0 * ( p[3][0] + p[0][3] ) +
401                    3.0 * ( p[0][2] + p[2][0] ) +
402                   -1.0 *   p[0][0] ) / 9.0;
403             break;
404 
405         case 3:
406             t = ( -4.0 *   p[3][0] +
407                    6.0 * ( p[3][1] + p[2][0] ) +
408                   -2.0 * ( p[3][3] + p[0][0] ) +
409                    3.0 * ( p[0][1] + p[2][3] ) +
410                   -1.0 *   p[0][3] ) / 9.0;
411             break;
412 
413         default:
414 
415             g_warning( "Impossible!" );
416 
417     }
418     return t;
419 }
420 
421 /**
422    Update default values for handle and tensor nodes.
423 */
updateNodes()424 void SPMeshPatchI::updateNodes() {
425 
426     // std::cout << "SPMeshPatchI::updateNodes: " << row << "," << col << std::endl;
427     // Handles first (tensors require update handles).
428     for( guint i = 0; i < 4; ++i ) {
429         for( guint j = 0; j < 4; ++j ) {
430             if( (*nodes)[ row + i ][ col + j ]->set == false ) {
431 
432                 if( (*nodes)[ row + i ][ col + j ]->node_type == MG_NODE_TYPE_HANDLE ) {
433 
434                     // If a handle is not set it is because the side is a line.
435                     // Set node points 1/3 of the way between corners.
436 
437                     if( i == 0 || i == 3 ) {
438                         Geom::Point p0 = ( (*nodes)[ row + i ][ col     ]->p );
439                         Geom::Point p3 = ( (*nodes)[ row + i ][ col + 3 ]->p );
440                         Geom::Point dp = (p3 - p0)/3.0;
441                         if( j == 2 ) dp *= 2.0;
442                         (*nodes)[ row + i ][ col + j ]->p = p0 + dp;
443                     }
444 
445                     if( j == 0 || j == 3 ) {
446                         Geom::Point p0 = ( (*nodes)[ row     ][ col + j ]->p );
447                         Geom::Point p3 = ( (*nodes)[ row + 3 ][ col + j ]->p );
448                         Geom::Point dp = (p3 - p0)/3.0;
449                         if( i == 2 ) dp *= 2.0;
450                         (*nodes)[ row + i ][ col + j ]->p = p0 + dp;
451                     }
452                 }
453             }
454         }
455     }
456 
457     // Update tensor nodes
458     for( guint i = 1; i < 3; ++i ) {
459         for( guint j = 1; j < 3; ++j ) {
460             if( (*nodes)[ row + i ][ col + j ]->set == false ) {
461 
462                 (*nodes)[ row + i ][ col + j ]->node_type = MG_NODE_TYPE_TENSOR;
463 
464                 guint t = 0;
465                 if( i == 1 && j == 2 ) t = 1;
466                 if( i == 2 && j == 2 ) t = 2;
467                 if( i == 2 && j == 1 ) t = 3;
468                 (*nodes)[ row + i ][ col + j ]->p = coonsTensorPoint( t );
469                 // std::cout << "Update node: " << i << ", " << j << " " << coonsTensorPoint( t ) << std::endl;
470 
471             }
472         }
473     }
474 }
475 
476 /**
477    Return color for corner of patch.
478 */
getColor(guint i)479 SPColor SPMeshPatchI::getColor( guint i ) {
480 
481     assert( i < 4 );
482 
483     SPColor color;
484     switch ( i ) {
485         case 0:
486             color = (*nodes)[ row   ][ col   ]->color;
487             break;
488         case 1:
489             color = (*nodes)[ row   ][ col+3 ]->color;
490             break;
491         case 2:
492             color = (*nodes)[ row+3 ][ col+3 ]->color;
493             break;
494         case 3:
495             color = (*nodes)[ row+3 ][ col   ]->color;
496             break;
497 
498     }
499 
500     return color;
501 
502 };
503 
504 /**
505    Set color for corner of patch.
506 */
setColor(guint i,SPColor color)507 void SPMeshPatchI::setColor( guint i, SPColor color ) {
508 
509     assert( i < 4 );
510 
511     switch ( i ) {
512         case 0:
513             (*nodes)[ row   ][ col   ]->color = color;
514             break;
515         case 1:
516             (*nodes)[ row   ][ col+3 ]->color = color;
517             break;
518         case 2:
519             (*nodes)[ row+3 ][ col+3 ]->color = color;
520             break;
521         case 3:
522             (*nodes)[ row+3 ][ col   ]->color = color;
523             break;
524     }
525 };
526 
527 /**
528    Return opacity for corner of patch.
529 */
getOpacity(guint i)530 gdouble SPMeshPatchI::getOpacity( guint i ) {
531 
532     assert( i < 4 );
533 
534     gdouble opacity = 0.0;
535     switch ( i ) {
536         case 0:
537             opacity = (*nodes)[ row   ][ col   ]->opacity;
538             break;
539         case 1:
540             opacity = (*nodes)[ row   ][ col+3 ]->opacity;
541             break;
542         case 2:
543             opacity = (*nodes)[ row+3 ][ col+3 ]->opacity;
544             break;
545         case 3:
546             opacity = (*nodes)[ row+3 ][ col   ]->opacity;
547             break;
548     }
549 
550     return opacity;
551 };
552 
553 
554 /**
555    Set opacity for corner of patch.
556 */
setOpacity(guint i,gdouble opacity)557 void SPMeshPatchI::setOpacity( guint i, gdouble opacity ) {
558 
559     assert( i < 4 );
560 
561     switch ( i ) {
562         case 0:
563             (*nodes)[ row   ][ col   ]->opacity = opacity;
564             break;
565         case 1:
566             (*nodes)[ row   ][ col+3 ]->opacity = opacity;
567             break;
568         case 2:
569             (*nodes)[ row+3 ][ col+3 ]->opacity = opacity;
570             break;
571         case 3:
572             (*nodes)[ row+3 ][ col   ]->opacity = opacity;
573             break;
574 
575     }
576 
577 };
578 
579 
580 /**
581    Return stop pointer for corner of patch.
582 */
getStopPtr(guint i)583 SPStop* SPMeshPatchI::getStopPtr( guint i ) {
584 
585     assert( i < 4 );
586 
587     SPStop* stop = nullptr;
588     switch ( i ) {
589         case 0:
590             stop = (*nodes)[ row   ][ col   ]->stop;
591             break;
592         case 1:
593             stop = (*nodes)[ row   ][ col+3 ]->stop;
594             break;
595         case 2:
596             stop = (*nodes)[ row+3 ][ col+3 ]->stop;
597             break;
598         case 3:
599             stop = (*nodes)[ row+3 ][ col   ]->stop;
600             break;
601     }
602 
603     return stop;
604 };
605 
606 
607 /**
608    Set stop pointer for corner of patch.
609 */
setStopPtr(guint i,SPStop * stop)610 void SPMeshPatchI::setStopPtr( guint i, SPStop* stop ) {
611 
612     assert( i < 4 );
613 
614     switch ( i ) {
615         case 0:
616             (*nodes)[ row   ][ col   ]->stop = stop;
617             break;
618         case 1:
619             (*nodes)[ row   ][ col+3 ]->stop = stop;
620             break;
621         case 2:
622             (*nodes)[ row+3 ][ col+3 ]->stop = stop;
623             break;
624         case 3:
625             (*nodes)[ row+3 ][ col   ]->stop = stop;
626             break;
627 
628     }
629 
630 };
631 
632 
SPMeshNodeArray(SPMeshGradient * mg)633 SPMeshNodeArray::SPMeshNodeArray( SPMeshGradient *mg ) {
634 
635     read( mg );
636 
637 };
638 
639 
640 // Copy constructor
SPMeshNodeArray(const SPMeshNodeArray & rhs)641 SPMeshNodeArray::SPMeshNodeArray( const SPMeshNodeArray& rhs ) :
642     nodes(rhs.nodes) // This only copies the pointers but it does size the vector of vectors.
643 {
644 
645     built = false;
646     mg = nullptr;
647     draggers_valid = false;
648 
649     for( unsigned i=0; i < nodes.size(); ++i ) {
650         for( unsigned j=0; j < nodes[i].size(); ++j ) {
651             nodes[i][j] = new SPMeshNode( *rhs.nodes[i][j] ); // Copy data.
652         }
653     }
654 };
655 
656 
657 // Copy assignment operator
operator =(const SPMeshNodeArray & rhs)658 SPMeshNodeArray& SPMeshNodeArray::operator=( const SPMeshNodeArray& rhs ) {
659 
660     if( this == &rhs ) return *this;
661 
662     clear(); // Clear any existing array.
663 
664     built = false;
665     mg = nullptr;
666     draggers_valid = false;
667 
668     nodes = rhs.nodes; // This only copies the pointers but it does size the vector of vectors.
669 
670     for( unsigned i=0; i < nodes.size(); ++i ) {
671         for( unsigned j=0; j < nodes[i].size(); ++j ) {
672             nodes[i][j] = new SPMeshNode( *rhs.nodes[i][j] ); // Copy data.
673         }
674     }
675 
676     return *this;
677 };
678 
679 // Fill array with data from mesh objects.
680 // Returns true of array's dimensions unchanged.
read(SPMeshGradient * mg_in)681 bool SPMeshNodeArray::read( SPMeshGradient *mg_in ) {
682 
683     mg = mg_in;
684     SPMeshGradient* mg_array = dynamic_cast<SPMeshGradient*>(mg->getArray());
685     if (!mg_array) {
686         std::cerr << "SPMeshNodeArray::read: No mesh array!" << std::endl;
687         return false;
688     }
689     // std::cout << "SPMeshNodeArray::read: " << mg_in << "  array: " << mg_array << std::endl;
690 
691     // Count rows and columns, if unchanged reuse array to keep draggers valid.
692     unsigned cols = 0;
693     unsigned rows = 0;
694     for (auto& ro: mg_array->children) {
695         if (SP_IS_MESHROW(&ro)) {
696             ++rows;
697             if (rows == 1 ) {
698                 for (auto& po: ro.children) {
699                     if (SP_IS_MESHPATCH(&po)) {
700                         ++cols;
701                     }
702                 }
703             }
704         }
705     }
706     bool same_size = true;
707     if (cols != patch_columns() || rows != patch_rows() ) {
708         // Draggers will be invalidated.
709         same_size = false;
710         clear();
711         draggers_valid = false;
712     }
713 
714     Geom::Point current_p( mg->x.computed, mg->y.computed );
715     // std::cout << "SPMeshNodeArray::read: p: " << current_p << std::endl;
716 
717     guint max_column = 0;
718     guint irow = 0; // Corresponds to top of patch being read in.
719     for (auto& ro: mg_array->children) {
720 
721         if (SP_IS_MESHROW(&ro)) {
722 
723             guint icolumn = 0; // Corresponds to left of patch being read in.
724             for (auto& po: ro.children) {
725 
726                 if (SP_IS_MESHPATCH(&po)) {
727 
728                     SPMeshpatch *patch = SP_MESHPATCH(&po);
729 
730                     // std::cout << "SPMeshNodeArray::read: row size: " << nodes.size() << std::endl;
731                     SPMeshPatchI new_patch( &nodes, irow, icolumn ); // Adds new nodes.
732                     // std::cout << "                          after: " << nodes.size() << std::endl;
733 
734                     gint istop = 0;
735 
736                     // Only 'top' side defined for first row.
737                     if( irow != 0 ) ++istop;
738 
739                     for (auto& so: po.children) {
740                         if (SP_IS_STOP(&so)) {
741 
742                             if( istop > 3 ) {
743                                 // std::cout << " Mesh Gradient: Too many stops: " << istop << std::endl;
744                                 break;
745                             }
746 
747                             SPStop *stop = SP_STOP(&so);
748 
749                             // Handle top of first row.
750                             if( istop == 0 && icolumn == 0 ) {
751                                 // First patch in mesh.
752                                 new_patch.setPoint( 0, 0, current_p );
753                             }
754                             // First point is always already defined by previous side (stop).
755                             current_p = new_patch.getPoint( istop, 0 );
756 
757                             // If side closes patch, then we read one less point.
758                             bool closed = false;
759                             if( icolumn == 0 && istop == 3 ) closed = true;
760                             if( icolumn  > 0 && istop == 2 ) closed = true;
761 
762 
763                             // Copy path and then replace commas by spaces so we can use stringstream to parse
764                             std::string path_string = stop->path_string->raw();
765                             std::replace(path_string.begin(),path_string.end(),',',' ');
766 
767                             // std::cout << "    path_string: " << path_string << std::endl;
768                             // std::cout << "    current_p: " << current_p << std::endl;
769 
770                             std::stringstream os( path_string );
771 
772                             // Determine type of path
773                             char path_type;
774                             os >> path_type;
775                             new_patch.setPathType( istop, path_type );
776 
777                             gdouble x, y;
778                             Geom::Point p, dp;
779                             guint max;
780                             switch ( path_type ) {
781                                 case 'l':
782                                     if( !closed ) {
783                                         os >> x >> y;
784                                         if( !os.fail() ) {
785                                             dp = Geom::Point( x, y );
786                                             new_patch.setPoint( istop, 3, current_p + dp );
787                                         } else {
788                                             std::cerr << "Failed to read l" << std::endl;
789                                         }
790                                     }
791                                     // To facilitate some side operations, set handles to 1/3 and
792                                     // 2/3 distance between corner points but flag as unset.
793                                     p = new_patch.getPoint( istop, 3 );
794                                     dp = (p - current_p)/3.0;  // Calculate since may not be set if closed.
795                                     // std::cout << "      istop: " << istop
796                                     //           << "  dp: " << dp
797                                     //           << "  p: " << p
798                                     //           << " current_p: " << current_p
799                                     //           << std::endl;
800                                     new_patch.setPoint( istop, 1, current_p + dp,       false );
801                                     new_patch.setPoint( istop, 2, current_p + 2.0 * dp, false );
802                                     break;
803                                 case 'L':
804                                     if( !closed ) {
805                                         os >> x >> y;
806                                         if( !os.fail() ) {
807                                             p = Geom::Point( x, y );
808                                             new_patch.setPoint( istop, 3, p );
809                                         } else {
810                                             std::cerr << "Failed to read L" << std::endl;
811                                         }
812                                     }
813                                     // To facilitate some side operations, set handles to 1/3 and
814                                     // 2/3 distance between corner points but flag as unset.
815                                     p = new_patch.getPoint( istop, 3 );
816                                     dp = (p - current_p)/3.0;
817                                     new_patch.setPoint( istop, 1, current_p + dp,       false );
818                                     new_patch.setPoint( istop, 2, current_p + 2.0 * dp, false );
819                                     break;
820                                 case 'c':
821                                     max = 4;
822                                     if( closed ) max = 3;
823                                     for( guint i = 1; i < max; ++i ) {
824                                         os >> x >> y;
825                                         if( !os.fail() ) {
826                                             p = Geom::Point( x, y );
827                                             p += current_p;
828                                             new_patch.setPoint( istop, i, p );
829                                         } else {
830                                             std::cerr << "Failed to read c: " << i << std::endl;
831                                         }
832                                     }
833                                     break;
834                                 case 'C':
835                                     max = 4;
836                                     if( closed ) max = 3;
837                                     for( guint i = 1; i < max; ++i ) {
838                                         os >> x >> y;
839                                         if( !os.fail() ) {
840                                             p = Geom::Point( x, y );
841                                             new_patch.setPoint( istop, i, p );
842                                         } else {
843                                             std::cerr << "Failed to read C: " << i << std::endl;
844                                         }
845                                     }
846                                     break;
847                                 default:
848                                     // should not reach
849                                     std::cerr << "Path Error: unhandled path type: " << path_type << std::endl;
850                             }
851                             current_p = new_patch.getPoint( istop, 3 );
852 
853                             // Color
854                             if( (istop == 0 && irow == 0 && icolumn > 0) || (istop == 1 && irow > 0 ) ) {
855                                 // skip
856                             } else {
857                                 SPColor color   = stop->getColor();
858                                 double opacity  = stop->getOpacity();
859                                 new_patch.setColor( istop, color );
860                                 new_patch.setOpacity( istop, opacity );
861                                 new_patch.setStopPtr( istop, stop );
862                             }
863                             ++istop;
864                         }
865                     } // Loop over stops
866 
867                     // Read in tensor string after stops since tensor nodes defined relative to corner nodes.
868 
869                     // Copy string and then replace commas by spaces so we can use stringstream to parse XXXX
870                     if( patch->tensor_string ) {
871                         std::string tensor_string = patch->tensor_string->raw();
872                         std::replace(tensor_string.begin(),tensor_string.end(),',',' ');
873 
874                         // std::cout << "    tensor_string: " << tensor_string << std::endl;
875 
876                         std::stringstream os( tensor_string );
877                         for( guint i = 0; i < 4; ++i ) {
878                             double x = 0.0;
879                             double y = 0.0;
880                             os >> x >> y;
881                             if( !os.fail() ) {
882                                 new_patch.setTensorPoint( i, new_patch.getPoint( i, 0 ) + Geom::Point( x, y ) );
883                             } else {
884                                 std::cerr << "Failed to read p: " << i << std::endl;
885                                 break;
886                             }
887                         }
888                     }
889                     ++icolumn;
890                     if( max_column < icolumn ) max_column = icolumn;
891                 }
892             }
893             ++irow;
894         }
895     }
896 
897     // Insure we have a true array.
898     for(auto & node : nodes) {
899         node.resize( max_column * 3 + 1 );
900     }
901 
902     // Set node edge.
903     for( guint i = 0; i < nodes.size(); ++i ) {
904         for( guint j = 0; j < nodes[i].size(); ++j ) {
905             nodes[i][j]->node_edge = MG_NODE_EDGE_NONE;
906             if( i == 0 )                    nodes[i][j]->node_edge |= MG_NODE_EDGE_TOP;
907             if( i == nodes.size() - 1 )     nodes[i][j]->node_edge |= MG_NODE_EDGE_BOTTOM;
908             if( j == 0 )                    nodes[i][j]->node_edge |= MG_NODE_EDGE_RIGHT;
909             if( j == nodes[i].size() - 1 )  nodes[i][j]->node_edge |= MG_NODE_EDGE_LEFT;
910         }
911     }
912 
913     // std::cout << "SPMeshNodeArray::Read: result:" << std::endl;
914     // print();
915 
916     built = true;
917 
918     return same_size;
919 };
920 
921 /**
922    Write repr using our array.
923 */
write(SPMeshGradient * mg)924 void SPMeshNodeArray::write( SPMeshGradient *mg ) {
925 
926     // std::cout << "SPMeshNodeArray::write: entrance:" << std::endl;
927     // print();
928     using Geom::X;
929     using Geom::Y;
930 
931     SPMeshGradient* mg_array = dynamic_cast<SPMeshGradient*>(mg->getArray());
932     if (!mg_array) {
933         // std::cerr << "SPMeshNodeArray::write: missing patches!" << std::endl;
934         mg_array = mg;
935     }
936 
937     // First we must delete reprs for old mesh rows and patches. We only need to call the
938     // deleteObject() method, which in turn calls sp_repr_unparent. Since iterators do not play
939     // well with boost::intrusive::list (which ChildrenList derive from) we need to iterate over a
940     // copy of the pointers to the objects.
941     std::vector<SPObject*> children_pointers;
942     for (auto& row : mg_array->children) {
943         children_pointers.push_back(&row);
944     }
945 
946     for (auto i : children_pointers) {
947         i->deleteObject();
948     }
949 
950     // Now we build new reprs
951     Inkscape::XML::Node *mesh = mg->getRepr();
952     Inkscape::XML::Node *mesh_array = mg_array->getRepr();
953 
954     SPMeshNodeArray* array = &(mg_array->array);
955     SPMeshPatchI patch0( &(array->nodes), 0, 0 );
956     Geom::Point current_p = patch0.getPoint( 0, 0 ); // Side 0, point 0
957 
958     sp_repr_set_svg_double( mesh, "x", current_p[X] );
959     sp_repr_set_svg_double( mesh, "y", current_p[Y] );
960 
961     Geom::Point current_p2( mg->x.computed, mg->y.computed );
962 
963     Inkscape::XML::Document *xml_doc = mesh->document();
964     guint rows = array->patch_rows();
965     for( guint i = 0; i < rows; ++i ) {
966 
967         // Write row
968         Inkscape::XML::Node *row = xml_doc->createElement("svg:meshrow");
969         mesh_array->appendChild( row );  // No attributes
970 
971         guint columns = array->patch_columns();
972         for( guint j = 0; j < columns; ++j ) {
973 
974             // Write patch
975             Inkscape::XML::Node *patch = xml_doc->createElement("svg:meshpatch");
976 
977             SPMeshPatchI patchi( &(array->nodes), i, j );
978 
979             // Add tensor
980             if( patchi.tensorIsSet() ) {
981 
982                 std::stringstream is;
983 
984                 for( guint k = 0; k < 4; ++k ) {
985                     Geom::Point p = patchi.getTensorPoint( k ) - patchi.getPoint( k, 0 );
986                     is << p[X] << "," << p[Y];
987                     if( k < 3 ) is << " ";
988                 }
989 
990                 patch->setAttribute("tensor", is.str());
991                 // std::cout << "  SPMeshNodeArray::write: tensor: " << is.str() << std::endl;
992             }
993 
994             row->appendChild( patch );
995 
996             // Write sides
997             for( guint k = 0; k < 4; ++k ) {
998 
999                 // Only first row has top stop
1000                 if( k == 0 && i != 0 ) continue;
1001 
1002                 // Only first column has left stop
1003                 if( k == 3 && j != 0 ) continue;
1004 
1005                 Inkscape::XML::Node *stop = xml_doc->createElement("svg:stop");
1006 
1007                 // Add path
1008                 std::stringstream is;
1009                 char path_type = patchi.getPathType( k );
1010                 is << path_type;
1011 
1012                 std::vector< Geom::Point> p = patchi.getPointsForSide( k );
1013                 current_p = patchi.getPoint( k, 0 );
1014 
1015                 switch ( path_type ) {
1016                     case 'l':
1017                         is << " "
1018                            << ( p[3][X] - current_p[X] ) << ","
1019                            << ( p[3][Y] - current_p[Y] );
1020                         break;
1021                     case 'L':
1022                         is << " "
1023                            << p[3][X] << ","
1024                            << p[3][Y];
1025                         break;
1026                     case 'c':
1027                         is << " "
1028                            << ( p[1][X] - current_p[X] ) << ","
1029                            << ( p[1][Y] - current_p[Y] ) << "  "
1030                            << ( p[2][X] - current_p[X] ) << ","
1031                            << ( p[2][Y] - current_p[Y] ) << "  "
1032                            << ( p[3][X] - current_p[X] ) << ","
1033                            << ( p[3][Y] - current_p[Y] );
1034                         break;
1035                     case 'C':
1036                         is << " "
1037                            << p[1][X] << ","
1038                            << p[1][Y] << "  "
1039                            << p[2][X] << ","
1040                            << p[2][Y] << "  "
1041                            << p[3][X] << ","
1042                            << p[3][Y];
1043                         break;
1044                     case 'z':
1045                     case 'Z':
1046                         std::cerr << "SPMeshNodeArray::write(): bad path type" << path_type << std::endl;
1047                         break;
1048                     default:
1049                         std::cerr << "SPMeshNodeArray::write(): unhandled path type" << path_type << std::endl;
1050                 }
1051                 stop->setAttribute("path", is.str());
1052                 // std::cout << "SPMeshNodeArray::write: path:  " << is.str().c_str() << std::endl;
1053                 // Add stop-color
1054                 if( ( k == 0 && i == 0 && j == 0 ) ||
1055                     ( k == 1 && i == 0           ) ||
1056                     ( k == 2                     ) ||
1057                     ( k == 3 &&           j == 0 ) ) {
1058 
1059                     // Why are we setting attribute and not style?
1060                     //stop->setAttribute("stop-color",   patchi.getColor(k).toString() );
1061                     //stop->setAttribute("stop-opacity", patchi.getOpacity(k) );
1062 
1063                     Inkscape::CSSOStringStream os;
1064                     os << "stop-color:" << patchi.getColor(k).toString() << ";stop-opacity:" << patchi.getOpacity(k);
1065                     stop->setAttribute("style", os.str());
1066                 }
1067                 patch->appendChild( stop );
1068             }
1069         }
1070     }
1071 }
1072 
1073 /**
1074  * Find default color based on colors in existing fill.
1075  */
default_color(SPItem * item)1076 static SPColor default_color( SPItem *item ) {
1077 
1078     SPColor color( 0.5, 0.0, 0.5 );
1079 
1080     if ( item->style ) {
1081         SPIPaint const &paint = ( item->style->fill ); // Could pick between style.fill/style.stroke
1082         if ( paint.isColor() ) {
1083             color = paint.value.color;
1084         } else if ( paint.isPaintserver() ) {
1085             SPObject const *server = item->style->getFillPaintServer();
1086             if ( SP_IS_GRADIENT(server) && SP_GRADIENT(server)->getVector() ) {
1087                 SPStop *firstStop = SP_GRADIENT(server)->getVector()->getFirstStop();
1088                 if ( firstStop ) {
1089                     color = firstStop->getColor();
1090                 }
1091             }
1092         }
1093     } else {
1094         std::cerr << " SPMeshNodeArray: default_color(): No style" << std::endl;
1095     }
1096 
1097     return color;
1098 }
1099 
1100 /**
1101    Create a default mesh.
1102 */
create(SPMeshGradient * mg,SPItem * item,Geom::OptRect bbox)1103 void SPMeshNodeArray::create( SPMeshGradient *mg, SPItem *item, Geom::OptRect bbox ) {
1104 
1105     // std::cout << "SPMeshNodeArray::create: Entrance" << std::endl;
1106 
1107     if( !bbox ) {
1108         // Set default size to bounding box if size not given.
1109         std::cerr << "SPMeshNodeArray::create(): bbox empty" << std::endl;
1110         bbox = item->geometricBounds();
1111 
1112         if( !bbox ) {
1113             std::cerr << "SPMeshNodeArray::create: ERROR: No bounding box!" << std::endl;
1114             return;
1115         }
1116     }
1117 
1118     Geom::Coord const width = bbox->dimensions()[Geom::X];
1119     Geom::Coord const height = bbox->dimensions()[Geom::Y];
1120     Geom::Point       center = bbox->midpoint();
1121 
1122     // Must keep repr and array in sync. We have two choices:
1123     //  Build the repr first and then "read" it.
1124     //  Construct the array and then "write" it.
1125     // We'll do the second.
1126 
1127     // Remove any existing mesh.  We could choose to simply scale an existing mesh...
1128     //clear();
1129 
1130     // We get called twice when a new mesh is created...WHY?
1131     //  return if we've already constructed the mesh.
1132     if( !nodes.empty() ) return;
1133 
1134     // Set 'gradientUnits'. Our calculations assume "userSpaceOnUse".
1135     Inkscape::XML::Node *repr = mg->getRepr();
1136     repr->setAttribute("gradientUnits", "userSpaceOnUse");
1137 
1138     // Get default color
1139     SPColor color = default_color( item );
1140 
1141     // Set some corners to white so we can see the mesh.
1142     SPColor white( 1.0, 1.0, 1.0 );
1143     if (color == white) {
1144         // If default color is white, set other color to black.
1145         white = SPColor( 0.0, 0.0, 0.0 );
1146     }
1147 
1148     // Get preferences
1149     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1150     guint prows = prefs->getInt("/tools/mesh/mesh_rows", 1);
1151     guint pcols = prefs->getInt("/tools/mesh/mesh_cols", 1);
1152 
1153     SPMeshGeometry mesh_type =
1154         (SPMeshGeometry) prefs->getInt("/tools/mesh/mesh_geometry", SP_MESH_GEOMETRY_NORMAL);
1155 
1156     if( mesh_type == SP_MESH_GEOMETRY_CONICAL ) {
1157 
1158         // Conical gradient.. for any shape/path using geometric bounding box.
1159 
1160         gdouble rx = width/2.0;
1161         gdouble ry = height/2.0;
1162 
1163         // Start and end angles
1164         gdouble start = 0.0;
1165         gdouble end   = 2.0 * M_PI;
1166 
1167         if ( SP_IS_STAR( item ) ) {
1168             // But if it is a star... use star parameters!
1169             SPStar* star = SP_STAR( item );
1170             center = star->center;
1171             rx = star->r[0];
1172             ry = star->r[0];
1173             start = star->arg[0];
1174             end   = start + 2.0 * M_PI;
1175         }
1176 
1177         if ( SP_IS_GENERICELLIPSE( item ) ) {
1178             // For arcs use set start/stop
1179             SPGenericEllipse* arc = SP_GENERICELLIPSE( item );
1180             center[Geom::X] = arc->cx.computed;
1181             center[Geom::Y] = arc->cy.computed;
1182             rx = arc->rx.computed;
1183             ry = arc->ry.computed;
1184             start = arc->start;
1185             end   = arc->end;
1186             if( end <= start ) {
1187                 end += 2.0 * M_PI;
1188             }
1189         }
1190 
1191         // std::cout << " start: " << start << "  end: " << end << std::endl;
1192 
1193         // IS THIS NECESSARY?
1194         sp_repr_set_svg_double( repr, "x", center[Geom::X] + rx * cos(start) );
1195         sp_repr_set_svg_double( repr, "y", center[Geom::Y] + ry * sin(start) );
1196 
1197         guint sections = pcols;
1198 
1199         // If less sections, arc approximation error too great. (Check!)
1200         if( sections < 4 ) sections = 4;
1201 
1202         double arc = (end - start) / (double)sections;
1203 
1204         // See: http://en.wikipedia.org/wiki/B%C3%A9zier_curve
1205         gdouble kappa = 4.0/3.0 * tan(arc/4.0);
1206         gdouble lenx = rx * kappa;
1207         gdouble leny = ry * kappa;
1208 
1209         gdouble s = start;
1210         for( guint i = 0; i < sections; ++i ) {
1211 
1212             SPMeshPatchI patch( &nodes, 0, i );
1213 
1214             gdouble x0 = center[Geom::X] + rx * cos(s);
1215             gdouble y0 = center[Geom::Y] + ry * sin(s);
1216             gdouble x1 = x0 - lenx * sin(s);
1217             gdouble y1 = y0 + leny * cos(s);
1218 
1219             s += arc;
1220             gdouble x3 = center[Geom::X] + rx * cos(s);
1221             gdouble y3 = center[Geom::Y] + ry * sin(s);
1222             gdouble x2 = x3 + lenx * sin(s);
1223             gdouble y2 = y3 - leny * cos(s);
1224 
1225             patch.setPoint( 0, 0, Geom::Point( x0, y0 ) );
1226             patch.setPoint( 0, 1, Geom::Point( x1, y1 ) );
1227             patch.setPoint( 0, 2, Geom::Point( x2, y2 ) );
1228             patch.setPoint( 0, 3, Geom::Point( x3, y3 ) );
1229 
1230             patch.setPoint( 2, 0, center );
1231             patch.setPoint( 3, 0, center );
1232 
1233             for( guint k = 0; k < 4; ++k ) {
1234                 patch.setPathType( k, 'l' );
1235                 patch.setColor( k, (i+k)%2 ? color : white );
1236                 patch.setOpacity( k, 1.0 );
1237             }
1238             patch.setPathType( 0, 'c' );
1239 
1240             // Set handle and tensor nodes.
1241             patch.updateNodes();
1242 
1243         }
1244 
1245         split_row( 0, prows );
1246 
1247     } else {
1248 
1249         // Normal grid meshes
1250 
1251         if( SP_IS_GENERICELLIPSE( item ) ) {
1252 
1253             // std::cout << "We've got ourselves an arc!" << std::endl;
1254 
1255             SPGenericEllipse* arc = SP_GENERICELLIPSE( item );
1256             center[Geom::X] = arc->cx.computed;
1257             center[Geom::Y] = arc->cy.computed;
1258             gdouble rx = arc->rx.computed;
1259             gdouble ry = arc->ry.computed;
1260 
1261             gdouble s = -3.0/2.0 * M_PI_2;
1262 
1263             sp_repr_set_svg_double( repr, "x", center[Geom::X] + rx * cos(s) );
1264             sp_repr_set_svg_double( repr, "y", center[Geom::Y] + ry * sin(s) );
1265 
1266             gdouble lenx = rx * 4*tan(M_PI_2/4)/3;
1267             gdouble leny = ry * 4*tan(M_PI_2/4)/3;
1268 
1269             SPMeshPatchI patch( &nodes, 0, 0 );
1270             for( guint i = 0; i < 4; ++i ) {
1271 
1272                 gdouble x0 = center[Geom::X] + rx * cos(s);
1273                 gdouble y0 = center[Geom::Y] + ry * sin(s);
1274                 gdouble x1 = x0 + lenx * cos(s + M_PI_2);
1275                 gdouble y1 = y0 + leny * sin(s + M_PI_2);
1276 
1277                 s += M_PI_2;
1278                 gdouble x3 = center[Geom::X] + rx * cos(s);
1279                 gdouble y3 = center[Geom::Y] + ry * sin(s);
1280                 gdouble x2 = x3 + lenx * cos(s - M_PI_2);
1281                 gdouble y2 = y3 + leny * sin(s - M_PI_2);
1282 
1283                 Geom::Point p1( x1, y1 );
1284                 Geom::Point p2( x2, y2 );
1285                 Geom::Point p3( x3, y3 );
1286                 patch.setPoint( i, 1, p1 );
1287                 patch.setPoint( i, 2, p2 );
1288                 patch.setPoint( i, 3, p3 );
1289 
1290                 patch.setPathType( i, 'c' );
1291 
1292                 patch.setColor( i, i%2 ? color : white );
1293                 patch.setOpacity( i, 1.0 );
1294             }
1295 
1296             // Fill out tensor points
1297             patch.updateNodes();
1298 
1299             split_row( 0, prows );
1300             split_column( 0, pcols );
1301 
1302             // END Arc
1303 
1304         } else if ( SP_IS_STAR( item ) ) {
1305 
1306             // Do simplest thing... assume star is not rounded or randomized.
1307             // (It should be easy to handle the rounded/randomized cases by making
1308             //  the appropriate star class function public.)
1309             SPStar* star = SP_STAR( item );
1310             guint sides =  star->sides;
1311 
1312             // std::cout << "We've got ourselves an star! Sides: " << sides << std::endl;
1313 
1314             Geom::Point p0 = sp_star_get_xy( star, SP_STAR_POINT_KNOT1, 0 );
1315             sp_repr_set_svg_double( repr, "x", p0[Geom::X] );
1316             sp_repr_set_svg_double( repr, "y", p0[Geom::Y] );
1317 
1318             for( guint i = 0; i < sides; ++i ) {
1319 
1320                 if( star->flatsided ) {
1321 
1322                     SPMeshPatchI patch( &nodes, 0, i );
1323 
1324                     patch.setPoint( 0, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT1, i ) );
1325                     guint ii = i+1;
1326                     if( ii == sides ) ii = 0;
1327                     patch.setPoint( 1, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT1, ii ) );
1328                     patch.setPoint( 2, 0, star->center );
1329                     patch.setPoint( 3, 0, star->center );
1330 
1331                     for( guint s = 0; s < 4; ++s ) {
1332                         patch.setPathType( s, 'l' );
1333                         patch.setColor( s, (i+s)%2 ? color : white );
1334                         patch.setOpacity( s, 1.0 );
1335                     }
1336 
1337                     // Set handle and tensor nodes.
1338                     patch.updateNodes();
1339 
1340                 } else {
1341 
1342                     SPMeshPatchI patch0( &nodes, 0, 2*i );
1343 
1344                     patch0.setPoint( 0, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT1, i ) );
1345                     patch0.setPoint( 1, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT2, i ) );
1346                     patch0.setPoint( 2, 0, star->center );
1347                     patch0.setPoint( 3, 0, star->center );
1348 
1349                     guint ii = i+1;
1350                     if( ii == sides ) ii = 0;
1351 
1352                     SPMeshPatchI patch1( &nodes, 0, 2*i+1 );
1353 
1354                     patch1.setPoint( 0, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT2, i ) );
1355                     patch1.setPoint( 1, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT1, ii ) );
1356                     patch1.setPoint( 2, 0, star->center );
1357                     patch1.setPoint( 3, 0, star->center );
1358 
1359                     for( guint s = 0; s < 4; ++s ) {
1360                         patch0.setPathType( s, 'l' );
1361                         patch0.setColor( s, s%2 ? color : white );
1362                         patch0.setOpacity( s, 1.0 );
1363                         patch1.setPathType( s, 'l' );
1364                         patch1.setColor( s, s%2 ? white : color );
1365                         patch1.setOpacity( s, 1.0 );
1366                     }
1367 
1368                     // Set handle and tensor nodes.
1369                     patch0.updateNodes();
1370                     patch1.updateNodes();
1371 
1372                 }
1373             }
1374 
1375             //print();
1376 
1377             split_row( 0, prows );
1378             //split_column( 0, pcols );
1379 
1380         } else {
1381 
1382             // Generic
1383 
1384             sp_repr_set_svg_double(repr, "x", bbox->min()[Geom::X]);
1385             sp_repr_set_svg_double(repr, "y", bbox->min()[Geom::Y]);
1386 
1387             // Get node array size
1388             guint nrows = prows * 3 + 1;
1389             guint ncols = pcols * 3 + 1;
1390 
1391             gdouble dx = width  / (gdouble)(ncols-1.0);
1392             gdouble dy = height / (gdouble)(nrows-1.0);
1393 
1394             Geom::Point p0( mg->x.computed, mg->y.computed );
1395 
1396             for( guint i = 0; i < nrows; ++i ) {
1397                 std::vector< SPMeshNode* > row;
1398                 for( guint j = 0; j < ncols; ++j ) {
1399                     SPMeshNode* node = new SPMeshNode;
1400                     node->p = p0 + Geom::Point( j * dx, i * dy );
1401 
1402                     node->node_edge = MG_NODE_EDGE_NONE;
1403                     if( i == 0        ) node->node_edge |= MG_NODE_EDGE_TOP;
1404                     if( i == nrows -1 ) node->node_edge |= MG_NODE_EDGE_BOTTOM;
1405                     if( j == 0        ) node->node_edge |= MG_NODE_EDGE_LEFT;
1406                     if( j == ncols -1 ) node->node_edge |= MG_NODE_EDGE_RIGHT;
1407 
1408                     if( i%3 == 0 ) {
1409 
1410                         if( j%3 == 0) {
1411                             // Corner
1412                             node->node_type = MG_NODE_TYPE_CORNER;
1413                             node->set = true;
1414                             node->color = (i+j)%2 ? color : white;
1415                             node->opacity = 1.0;
1416 
1417                         } else {
1418                             // Side
1419                             node->node_type = MG_NODE_TYPE_HANDLE;
1420                             node->set = true;
1421                             node->path_type = 'c';
1422                         }
1423 
1424                     } else {
1425 
1426                         if( j%3 == 0) {
1427                             // Side
1428                             node->node_type = MG_NODE_TYPE_HANDLE;
1429                             node->set = true;
1430                             node->path_type = 'c';
1431                         } else {
1432                             // Tensor
1433                             node->node_type = MG_NODE_TYPE_TENSOR;
1434                             node->set = false;
1435                         }
1436 
1437                     }
1438 
1439                     row.push_back( node );
1440                 }
1441                 nodes.push_back( row );
1442             }
1443             // End normal
1444         }
1445 
1446     } // If conical
1447 
1448     //print();
1449 
1450     // Write repr
1451     write( mg );
1452 }
1453 
1454 
1455 /**
1456    Clear mesh gradient.
1457 */
clear()1458 void SPMeshNodeArray::clear() {
1459 
1460     for(auto & node : nodes) {
1461         for(auto & j : node) {
1462             if( j ) {
1463                 delete j;
1464             }
1465         }
1466     }
1467     nodes.clear();
1468 };
1469 
1470 
1471 /**
1472    Print mesh gradient (for debugging).
1473 */
print()1474 void SPMeshNodeArray::print() {
1475     for( guint i = 0; i < nodes.size(); ++i ) {
1476         std::cout << "New node row:" << std::endl;
1477         for( guint j = 0; j < nodes[i].size(); ++j ) {
1478             if( nodes[i][j] ) {
1479                 std::cout.width(4);
1480                 std::cout << "  Node: " << i << "," << j << ":  "
1481                           << nodes[i][j]->p
1482                           << "  Node type: " << nodes[i][j]->node_type
1483                           << "  Node edge: " << nodes[i][j]->node_edge
1484                           << "  Set: "  << nodes[i][j]->set
1485                           << "  Path type: " << nodes[i][j]->path_type
1486                           << "  Stop: " << nodes[i][j]->stop
1487                           << std::endl;
1488             } else {
1489                 std::cout << "Error: missing mesh node." << std::endl;
1490             }
1491         } // Loop over patches
1492     } // Loop over rows
1493 };
1494 
1495 
1496 
1497 /*
1498 double hermite( const double p0, const double p1, const double m0, const double m1, const double t ) {
1499     double t2 = t*t;
1500     double t3 = t2*t;
1501 
1502     double result = (2.0*t3 - 3.0*t2 +1.0) * p0
1503                   + (t3 - 2.0*t2 + t)      * m0
1504                   + (-2.0*t3 + 3.0*t2)     * p1
1505                   + (t3 -t2)               * m1;
1506 
1507     return result;
1508 }
1509 */
1510 
1511 class SPMeshSmoothCorner {
1512 
1513 public:
SPMeshSmoothCorner()1514     SPMeshSmoothCorner() {
1515         for(auto & i : g) {
1516             for( unsigned j = 0; j < 4; ++j ) {
1517                 i[j] = 0;
1518             }
1519         }
1520     }
1521 
1522     double g[3][8]; // 3 colors, 8 parameters: see enum.
1523     Geom::Point p;  // Location of point
1524 };
1525 
1526 // Find slope at point 1 given values at previous and next points
1527 // Return value is slope in user space
find_slope1(const double & p0,const double & p1,const double & p2,const double & d01,const double & d12)1528 double find_slope1( const double &p0, const double &p1, const double &p2,
1529                     const double &d01, const double &d12 ) {
1530 
1531     double slope = 0;
1532 
1533     if( d01 > 0 && d12 > 0 ) {
1534         slope = 0.5 * ( (p1 - p0)/d01 + (p2 - p1)/d12 );
1535 
1536         if( ( p0 > p1 && p1 < p2 ) ||
1537             ( p0 < p1 && p1 > p2 ) ) {
1538             // At minimum or maximum, use slope of zero
1539             slope = 0;
1540         } else {
1541             // Ensure we don't overshoot
1542             if( fabs(slope) > fabs(3*(p1-p0)/d01) ) {
1543                 slope = 3*(p1-p0)/d01;
1544             }
1545             if( fabs(slope) > fabs(3*(p2-p1)/d12) ) {
1546                 slope = 3*(p2-p1)/d12;
1547             }
1548         }
1549     } else {
1550         // Do something clever
1551     }
1552     return slope;
1553 };
1554 
1555 
1556 /*
1557 // Find slope at point 0 given values at previous and next points
1558 // TO DO: TAKE DISTANCE BETWEEN POINTS INTO ACCOUNT
1559 double find_slope2( double pmm, double ppm, double pmp, double ppp, double p0 ) {
1560 
1561     // pmm == d[i-1][j-1], ...  'm' is minus, 'p' is plus
1562     double slope = (ppp - ppm - pmp + pmm)/2.0;
1563     if( (ppp > p0 && ppm > p0 && pmp > p0 && pmm > 0) ||
1564         (ppp < p0 && ppm < p0 && pmp < p0 && pmm < 0) ) {
1565         // At minimum or maximum, use slope of zero
1566         slope = 0;
1567     } else {
1568         // Don't really know what to do here
1569         if( fabs(slope) > fabs(3*(ppp-p0)) ) {
1570             slope = 3*(ppp-p0);
1571         }
1572         if( fabs(slope) > fabs(3*(pmp-p0)) ) {
1573             slope = 3*(pmp-p0);
1574         }
1575         if( fabs(slope) > fabs(3*(ppm-p0)) ) {
1576             slope = 3*(ppm-p0);
1577         }
1578         if( fabs(slope) > fabs(3*(pmm-p0)) ) {
1579             slope = 3*(pmm-p0);
1580         }
1581     }
1582     return slope;
1583 }
1584 */
1585 
1586 // https://en.wikipedia.org/wiki/Bicubic_interpolation
invert(const double v[16],double alpha[16])1587 void invert( const double v[16], double alpha[16] ) {
1588 
1589     const double  A[16][16] = {
1590 
1591         { 1, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0 },
1592         { 0, 0, 0, 0,  1, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0 },
1593         {-3, 3, 0, 0, -2,-1, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0 },
1594         { 2,-2, 0, 0,  1, 1, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0 },
1595         { 0, 0, 0, 0,  0, 0, 0, 0,  1, 0, 0, 0,  0, 0, 0, 0 },
1596         { 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  1, 0, 0, 0 },
1597         { 0, 0, 0, 0,  0, 0, 0, 0, -3, 3, 0, 0, -2,-1, 0, 0 },
1598         { 0, 0, 0, 0,  0, 0, 0, 0,  2,-2, 0, 0,  1, 1, 0, 0 },
1599         {-3, 0, 3, 0,  0, 0, 0, 0, -2, 0,-1, 0,  0, 0, 0, 0 },
1600         { 0, 0, 0, 0, -3, 0, 3, 0,  0, 0, 0, 0, -2, 0,-1, 0 },
1601         { 9,-9,-9, 9,  6, 3,-6,-3,  6,-6, 3,-3,  4, 2, 2, 1 },
1602         {-6, 6, 6,-6, -3,-3, 3, 3, -4, 4,-2, 2, -2,-2,-1,-1 },
1603         { 2, 0,-2, 0,  0, 0, 0, 0,  1, 0, 1, 0,  0, 0, 0, 0 },
1604         { 0, 0, 0, 0,  2, 0,-2, 0,  0, 0, 0, 0,  1, 0, 1, 0 },
1605         {-6, 6, 6,-6, -4,-2, 4, 2, -3, 3,-3, 3, -2,-1,-2,-1 },
1606         { 4,-4,-4, 4,  2, 2,-2,-2,  2,-2, 2,-2,  1, 1, 1, 1 }
1607     };
1608 
1609     for( unsigned i = 0; i < 16; ++i ) {
1610         alpha[i] = 0;
1611         for( unsigned j = 0; j < 16; ++j ) {
1612             alpha[i] += A[i][j]*v[j];
1613         }
1614     }
1615 }
1616 
sum(const double alpha[16],const double & x,const double & y)1617 double sum( const double alpha[16], const double& x, const double& y ) {
1618 
1619     double result = 0;
1620 
1621     double xx = x*x;
1622     double xxx = xx * x;
1623     double yy = y*y;
1624     double yyy = yy * y;
1625 
1626     result += alpha[  0 ];
1627     result += alpha[  1 ] * x;
1628     result += alpha[  2 ] * xx;
1629     result += alpha[  3 ] * xxx;
1630     result += alpha[  4 ] * y;
1631     result += alpha[  5 ] * y * x;
1632     result += alpha[  6 ] * y * xx;
1633     result += alpha[  7 ] * y * xxx;
1634     result += alpha[  8 ] * yy;
1635     result += alpha[  9 ] * yy * x;
1636     result += alpha[ 10 ] * yy * xx;
1637     result += alpha[ 11 ] * yy * xxx;
1638     result += alpha[ 12 ] * yyy;
1639     result += alpha[ 13 ] * yyy * x;
1640     result += alpha[ 14 ] * yyy * xx;
1641     result += alpha[ 15 ] * yyy * xxx;
1642 
1643     return result;
1644 }
1645 
1646 /**
1647    Fill 'smooth' with a smoothed version of the array by subdividing each patch into smaller patches.
1648 */
bicubic(SPMeshNodeArray * smooth,SPMeshType type)1649 void SPMeshNodeArray::bicubic( SPMeshNodeArray* smooth, SPMeshType type ) {
1650 
1651 
1652     *smooth = *this;  // Deep copy via copy assignment constructor, smooth cleared before copy
1653     // std::cout << "SPMeshNodeArray::smooth2(): " << this->patch_rows() << " " << smooth->patch_columns() << std::endl;
1654     // std::cout << "  " << smooth << " " << this << std::endl;
1655 
1656     // Find derivatives at corners
1657 
1658     // Create array of corner points
1659     std::vector< std::vector <SPMeshSmoothCorner> > d;
1660     d.resize( smooth->patch_rows() + 1 );
1661     for( unsigned i = 0; i < d.size(); ++i ) {
1662         d[i].resize( smooth->patch_columns() + 1 );
1663         for( unsigned j = 0; j < d[i].size(); ++j ) {
1664             float rgb_color[3];
1665             this->nodes[ i*3 ][ j*3 ]->color.get_rgb_floatv(rgb_color);
1666             d[i][j].g[0][0] =  rgb_color[ 0 ];
1667             d[i][j].g[1][0] =  rgb_color[ 1 ];
1668             d[i][j].g[2][0] =  rgb_color[ 2 ];
1669             d[i][j].p = this->nodes[ i*3 ][ j*3 ]->p;
1670         }
1671     }
1672 
1673     // Calculate interior derivatives
1674     for( unsigned i = 0; i < d.size(); ++i ) {
1675         for( unsigned j = 0; j < d[i].size(); ++j ) {
1676             for( unsigned k = 0; k < 3; ++k ) { // Loop over colors
1677 
1678                 // dx
1679 
1680                 if( i != 0 && i != d.size()-1 ) {
1681                     double lm = Geom::distance(  d[i-1][j].p, d[i][j].p );
1682                     double lp = Geom::distance(  d[i+1][j].p, d[i][j].p );
1683                     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 );
1684                 }
1685 
1686                 // dy
1687                 if( j != 0 && j != d[i].size()-1 ) {
1688                     double lm = Geom::distance(  d[i][j-1].p, d[i][j].p );
1689                     double lp = Geom::distance(  d[i][j+1].p, d[i][j].p );
1690                     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 );
1691                 }
1692 
1693                 // dxdy  if needed, need to take lengths into account
1694                 // if( i != 0 && i != d.size()-1 && j != 0 && j != d[i].size()-1 ) {
1695                 //     d[i][j].g[k][3] = find_slope2( d[i-1][j-1].g[k][0], d[i+1][j-1].g[k][0],
1696                 //                                    d[i-1][j+1].g[k][0], d[i-1][j-1].g[k][0],
1697                 //                                    d[i][j].g[k][0] );
1698                 // }
1699 
1700             }
1701         }
1702     }
1703 
1704     // Calculate exterior derivatives
1705     // We need to do this after calculating interior derivatives as we need to already
1706     // have the non-exterior derivative calculated for finding the parabola.
1707     for( unsigned j = 0; j< d[0].size(); ++j ) {
1708         for( unsigned k = 0; k < 3; ++k ) { // Loop over colors
1709 
1710             // Parabolic
1711             double d0 = Geom::distance( d[1][j].p, d[0  ][j].p );
1712             if( d0 > 0 ) {
1713                 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];
1714             } else {
1715                 d[0][j].g[k][1] = 0;
1716             }
1717 
1718             unsigned z = d.size()-1;
1719             double dz = Geom::distance( d[z][j].p, d[z-1][j].p );
1720             if( dz > 0 ) {
1721                 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];
1722             } else {
1723                 d[z][j].g[k][1] = 0;
1724             }
1725         }
1726     }
1727 
1728     for( unsigned i = 0; i< d.size(); ++i ) {
1729         for( unsigned k = 0; k < 3; ++k ) { // Loop over colors
1730 
1731             // Parabolic
1732             double d0 = Geom::distance( d[i][1].p, d[i][0  ].p );
1733             if( d0 > 0 ) {
1734                 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];
1735             } else {
1736                 d[i][0].g[k][2] = 0;
1737             }
1738 
1739             unsigned z = d[0].size()-1;
1740             double dz = Geom::distance( d[i][z].p, d[i][z-1].p );
1741             if( dz > 0 ) {
1742                 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];
1743             } else {
1744                 d[i][z].g[k][2] = 0;
1745             }
1746         }
1747     }
1748 
1749     // Leave outside corner cross-derivatives at zero.
1750 
1751     // Next split each patch into 8x8 smaller patches.
1752 
1753     // Split each row into eight rows.
1754     // Must do it from end so inserted rows don't mess up indexing
1755     for( int i = smooth->patch_rows() - 1; i >= 0; --i ) {
1756         smooth->split_row( i, unsigned(8) );
1757     }
1758 
1759     // Split each column into eight columns.
1760     // Must do it from end so inserted columns don't mess up indexing
1761     for( int i = smooth->patch_columns() - 1; i >= 0; --i ) {
1762         smooth->split_column( i, (unsigned)8 );
1763     }
1764 
1765     // Fill new patches
1766     for( unsigned i = 0; i < this->patch_rows(); ++i ) {
1767         for( unsigned j = 0; j < this->patch_columns(); ++j ) {
1768 
1769             double dx0 = Geom::distance( d[i  ][j  ].p, d[i+1][j  ].p );
1770             double dx1 = Geom::distance( d[i  ][j+1].p, d[i+1][j+1].p );
1771             double dy0 = Geom::distance( d[i  ][j  ].p, d[i  ][j+1].p );
1772             double dy1 = Geom::distance( d[i+1][j  ].p, d[i+1][j+1].p );
1773 
1774             // Temp loop over 0..8 to get last column/row edges
1775             float r[3][9][9]; // result
1776             for( unsigned m = 0; m < 3; ++m ) {
1777 
1778                 double v[16];
1779                 v[ 0] = d[i  ][j  ].g[m][0];
1780                 v[ 1] = d[i+1][j  ].g[m][0];
1781                 v[ 2] = d[i  ][j+1].g[m][0];
1782                 v[ 3] = d[i+1][j+1].g[m][0];
1783                 v[ 4] = d[i  ][j  ].g[m][1]*dx0;
1784                 v[ 5] = d[i+1][j  ].g[m][1]*dx0;
1785                 v[ 6] = d[i  ][j+1].g[m][1]*dx1;
1786                 v[ 7] = d[i+1][j+1].g[m][1]*dx1;
1787                 v[ 8] = d[i  ][j  ].g[m][2]*dy0;
1788                 v[ 9] = d[i+1][j  ].g[m][2]*dy1;
1789                 v[10] = d[i  ][j+1].g[m][2]*dy0;
1790                 v[11] = d[i+1][j+1].g[m][2]*dy1;
1791                 v[12] = d[i  ][j  ].g[m][3];
1792                 v[13] = d[i+1][j  ].g[m][3];
1793                 v[14] = d[i  ][j+1].g[m][3];
1794                 v[15] = d[i+1][j+1].g[m][3];
1795 
1796                 double alpha[16];
1797                 invert( v, alpha );
1798 
1799                 for( unsigned k = 0; k < 9; ++k ) {
1800                     for( unsigned l = 0; l < 9; ++l ) {
1801                         double x = k/8.0;
1802                         double y = l/8.0;
1803                         r[m][k][l] = sum( alpha, x, y );
1804                         // Clamp to allowed values
1805                         if( r[m][k][l] > 1.0 )
1806                             r[m][k][l] = 1.0;
1807                         if( r[m][k][l] < 0.0 )
1808                             r[m][k][l] = 0.0;
1809                     }
1810                 }
1811 
1812             } // Loop over colors
1813 
1814             for( unsigned k = 0; k < 9; ++k ) {
1815                 for( unsigned l = 0; l < 9; ++l ) {
1816                     // Every third node is a corner node
1817                     smooth->nodes[ (i*8+k)*3 ][(j*8+l)*3 ]->color.set( r[0][k][l], r[1][k][l], r[2][k][l] );
1818                 }
1819             }
1820         }
1821     }
1822 }
1823 
1824 /**
1825    Number of patch rows.
1826 */
patch_rows()1827 guint SPMeshNodeArray::patch_rows() {
1828 
1829     return nodes.size()/3;
1830 }
1831 
1832 /**
1833    Number of patch columns.
1834 */
patch_columns()1835 guint SPMeshNodeArray::patch_columns() {
1836     if (nodes.empty()) {
1837         return 0;
1838     }
1839     return nodes[0].size()/3;
1840 }
1841 
1842 /**
1843    Inputs:
1844      i, j: Corner draggable indices.
1845    Returns:
1846      true if corners adjacent.
1847      n[] is array of nodes in top/bottom or left/right order.
1848 */
adjacent_corners(guint i,guint j,SPMeshNode * n[4])1849 bool SPMeshNodeArray::adjacent_corners( guint i, guint j, SPMeshNode* n[4] ) {
1850 
1851     // This works as all corners have indices and they
1852     // are numbered in order by row and column (and
1853     // the node array is rectangular).
1854 
1855     bool adjacent = false;
1856 
1857     guint c1 = i;
1858     guint c2 = j;
1859     if( j < i ) {
1860         c1 = j;
1861         c2 = i;
1862     }
1863 
1864     // Number of corners in a row of patches.
1865     guint ncorners = patch_columns() + 1;
1866 
1867     guint crow1 = c1 / ncorners;
1868     guint crow2 = c2 / ncorners;
1869     guint ccol1 = c1 % ncorners;
1870     guint ccol2 = c2 % ncorners;
1871 
1872     guint nrow  = crow1 * 3;
1873     guint ncol  = ccol1 * 3;
1874 
1875     // std::cout << "  i: " << i
1876     //           << "  j: " << j
1877     //           << "  ncorners: " << ncorners
1878     //           << "  c1: " << c1
1879     //           << "  crow1: " << crow1
1880     //           << "  ccol1: " << ccol1
1881     //           << "  c2: " << c2
1882     //           << "  crow2: " << crow2
1883     //           << "  ccol2: " << ccol2
1884     //           << "  nrow: " << nrow
1885     //           << "  ncol: " << ncol
1886     //           << std::endl;
1887 
1888     // Check for horizontal neighbors
1889     if ( crow1 == crow2 && (ccol2 - ccol1) == 1 ) {
1890         adjacent = true;
1891         for( guint k = 0; k < 4; ++k ) {
1892             n[k] = nodes[nrow][ncol+k];
1893         }
1894     }
1895 
1896     // Check for vertical neighbors
1897     if ( ccol1 == ccol2 && (crow2 - crow1) == 1 ) {
1898         adjacent = true;
1899         for( guint k = 0; k < 4; ++k ) {
1900             n[k] = nodes[nrow+k][ncol];
1901         }
1902     }
1903 
1904     return adjacent;
1905 }
1906 
1907 /**
1908    Toggle sides between lineto and curve to if both corners selected.
1909    Input is a list of selected corner draggable indices.
1910 */
side_toggle(std::vector<guint> corners)1911 guint SPMeshNodeArray::side_toggle( std::vector<guint> corners ) {
1912 
1913     guint toggled = 0;
1914 
1915     if( corners.size() < 2 ) return 0;
1916 
1917     for( guint i = 0; i < corners.size()-1; ++i ) {
1918         for( guint j = i+1; j < corners.size(); ++j ) {
1919 
1920             SPMeshNode* n[4];
1921             if( adjacent_corners( corners[i], corners[j], n ) ) {
1922 
1923                 gchar path_type = n[1]->path_type;
1924                 switch (path_type)
1925                 {
1926                     case 'L':
1927                         n[1]->path_type = 'C';
1928                         n[2]->path_type = 'C';
1929                         n[1]->set = true;
1930                         n[2]->set = true;
1931                         break;
1932 
1933                     case 'l':
1934                         n[1]->path_type = 'c';
1935                         n[2]->path_type = 'c';
1936                         n[1]->set = true;
1937                         n[2]->set = true;
1938                         break;
1939 
1940                     case 'C': {
1941                         n[1]->path_type = 'L';
1942                         n[2]->path_type = 'L';
1943                         n[1]->set = false;
1944                         n[2]->set = false;
1945                         // 'L' acts as if handles are 1/3 of path length from corners.
1946                         Geom::Point dp = (n[3]->p - n[0]->p)/3.0;
1947                         n[1]->p = n[0]->p + dp;
1948                         n[2]->p = n[3]->p - dp;
1949                         break;
1950                     }
1951                     case 'c': {
1952                         n[1]->path_type = 'l';
1953                         n[2]->path_type = 'l';
1954                         n[1]->set = false;
1955                         n[2]->set = false;
1956                         // 'l' acts as if handles are 1/3 of path length from corners.
1957                         Geom::Point dp = (n[3]->p - n[0]->p)/3.0;
1958                         n[1]->p = n[0]->p + dp;
1959                         n[2]->p = n[3]->p - dp;
1960                         // std::cout << "Toggle sides: "
1961                         //           << n[0]->p << " "
1962                         //           << n[1]->p << " "
1963                         //           << n[2]->p << " "
1964                         //           << n[3]->p << " "
1965                         //           << dp << std::endl;
1966                         break;
1967                     }
1968                     default:
1969                         std::cout << "Toggle sides: Invalid path type: " << path_type << std::endl;
1970                 }
1971                 ++toggled;
1972             }
1973         }
1974     }
1975     if( toggled > 0 ) built = false;
1976     return toggled;
1977 }
1978 
1979 /**
1980  * Converts generic Beziers to Beziers approximating elliptical arcs, preserving handle direction.
1981  * There are infinite possible solutions. The solution chosen here is to generate a section of an
1982  * ellipse that is centered on the intersection of the two lines passing through the two nodes but
1983  * parallel to the other node's handle direction. This is the section of an ellipse that
1984  * corresponds to a quarter of a circle squished and then skewed.
1985  */
side_arc(std::vector<guint> corners)1986 guint SPMeshNodeArray::side_arc( std::vector<guint> corners ) {
1987 
1988     if( corners.size() < 2 ) return 0;
1989 
1990     guint arced = 0;
1991     for( guint i = 0; i < corners.size()-1; ++i ) {
1992         for( guint j = i+1; j < corners.size(); ++j ) {
1993 
1994             SPMeshNode* n[4];
1995             if( adjacent_corners( corners[i], corners[j], n ) ) {
1996 
1997                 gchar path_type = n[1]->path_type;
1998                 switch (path_type)
1999                 {
2000                     case 'L':
2001                     case 'l':
2002                         std::cerr << "SPMeshNodeArray::side_arc: Can't convert straight lines to arcs." << std::endl;
2003                         break;
2004 
2005                     case 'C':
2006                     case 'c': {
2007 
2008                         Geom::Ray  ray1( n[0]->p, n[1]->p );
2009                         Geom::Ray  ray2( n[3]->p, n[2]->p );
2010                         if( !are_parallel( (Geom::Line)ray1, (Geom::Line)ray2 ) ) {
2011 
2012                             Geom::OptCrossing crossing = intersection( ray1, ray2 );
2013 
2014                             if( crossing ) {
2015 
2016                                 Geom::Point intersection = ray1.pointAt( (*crossing).ta );
2017 
2018                                 const double f = 4.0/3.0 * tan( M_PI/2.0/4.0 );
2019 
2020                                 Geom::Point h1 = intersection - n[0]->p;
2021                                 Geom::Point h2 = intersection - n[3]->p;
2022 
2023                                 n[1]->p = n[0]->p + f*h1;
2024                                 n[2]->p = n[3]->p + f*h2;
2025                                 ++arced;
2026 
2027                             } else {
2028                                 std::cerr << "SPMeshNodeArray::side_arc: No crossing, can't turn into arc." << std::endl;
2029                             }
2030                         } else {
2031                             std::cerr << "SPMeshNodeArray::side_arc: Handles parallel, can't turn into arc." << std::endl;
2032                         }
2033                         break;
2034                     }
2035                     default:
2036                         std::cerr << "SPMeshNodeArray::side_arc: Invalid path type: " << n[1]->path_type << std::endl;
2037                 }
2038             }
2039         }
2040     }
2041     if( arced > 0 ) built = false;
2042     return arced;
2043 }
2044 
2045 /**
2046    Toggle sides between lineto and curve to if both corners selected.
2047    Input is a list of selected corner draggable indices.
2048 */
tensor_toggle(std::vector<guint> corners)2049 guint SPMeshNodeArray::tensor_toggle( std::vector<guint> corners ) {
2050 
2051     // std::cout << "SPMeshNodeArray::tensor_toggle" << std::endl;
2052 
2053     if( corners.size() < 4 ) return 0;
2054 
2055     guint toggled = 0;
2056 
2057     // Number of corners in a row of patches.
2058     guint ncorners = patch_columns() + 1;
2059 
2060     for( guint i = 0; i < corners.size()-3; ++i ) {
2061         for( guint j = i+1; j < corners.size()-2; ++j ) {
2062             for( guint k = j+1; k < corners.size()-1; ++k ) {
2063                 for( guint l = k+1; l < corners.size(); ++l ) {
2064 
2065                     guint c[4];
2066                     c[0] = corners[i];
2067                     c[1] = corners[j];
2068                     c[2] = corners[k];
2069                     c[3] = corners[l];
2070                     std::sort( c, c+4 );
2071 
2072                     // Check we have four corners of one patch selected
2073                     if( c[1]-c[0] == 1 &&
2074                         c[3]-c[2] == 1 &&
2075                         c[2]-c[0] == ncorners &&
2076                         c[3]-c[1] == ncorners &&
2077                         c[0] % ncorners < ncorners - 1 ) {
2078 
2079                         // Patch
2080                         guint prow = c[0] / ncorners;
2081                         guint pcol = c[0] % ncorners;
2082 
2083                         // Upper left node of patch
2084                         guint irow = prow * 3;
2085                         guint jcol = pcol * 3;
2086 
2087                         // std::cout << "tensor::toggle: "
2088                         //           << c[0] << ", "
2089                         //           << c[1] << ", "
2090                         //           << c[2] << ", "
2091                         //           << c[3] << std::endl;
2092 
2093                         // std::cout << "tensor::toggle: "
2094                         //           << " irow: " << irow
2095                         //           << " jcol: " << jcol
2096                         //           << " prow: " << prow
2097                         //           << " pcol: " << pcol
2098                         //           << std::endl;
2099 
2100                         SPMeshPatchI patch( &nodes, prow, pcol );
2101                         patch.updateNodes();
2102 
2103                         if( patch.tensorIsSet() ) {
2104                             // Unset tensor points
2105                             nodes[irow+1][jcol+1]->set = false;
2106                             nodes[irow+1][jcol+2]->set = false;
2107                             nodes[irow+2][jcol+1]->set = false;
2108                             nodes[irow+2][jcol+2]->set = false;
2109                         } else {
2110                             // Set tensor points
2111                             nodes[irow+1][jcol+1]->set = true;
2112                             nodes[irow+1][jcol+2]->set = true;
2113                             nodes[irow+2][jcol+1]->set = true;
2114                             nodes[irow+2][jcol+2]->set = true;
2115                         }
2116 
2117                         ++toggled;
2118                     }
2119                 }
2120             }
2121         }
2122     }
2123     if( toggled > 0 ) built = false;
2124     return toggled;
2125 }
2126 
2127 /**
2128    Attempts to smooth color transitions across corners.
2129    Input is a list of selected corner draggable indices.
2130 */
color_smooth(std::vector<guint> corners)2131 guint SPMeshNodeArray::color_smooth( std::vector<guint> corners ) {
2132 
2133     // std::cout << "SPMeshNodeArray::color_smooth" << std::endl;
2134 
2135     guint smoothed = 0;
2136 
2137     // Number of corners in a row of patches.
2138     guint ncorners = patch_columns() + 1;
2139 
2140     // Number of node rows and columns
2141     guint ncols = patch_columns() * 3 + 1;
2142     guint nrows = patch_rows() * 3 + 1;
2143 
2144     for(unsigned int corner : corners) {
2145 
2146         // std::cout << "SPMeshNodeArray::color_smooth: " << i << " " << corner << std::endl;
2147 
2148         // Node row & col
2149         guint nrow = (corner / ncorners) * 3;
2150         guint ncol = (corner % ncorners) * 3;
2151 
2152         SPMeshNode* n[7];
2153         for( guint s = 0; s < 2; ++s ) {
2154 
2155             bool smooth = false;
2156 
2157             // Find neighboring nodes
2158             if( s == 0 ) {
2159 
2160                 // Horizontal
2161                 if( ncol > 2 && ncol+3 < ncols) {
2162                     for( guint j = 0; j < 7; ++j ) {
2163                         n[j] = nodes[ nrow ][ ncol - 3 + j ];
2164                     }
2165                     smooth = true;
2166                 }
2167 
2168             } else {
2169 
2170                 // Vertical
2171                 if( nrow > 2 && nrow+3 < nrows) {
2172                     for( guint j = 0; j < 7; ++j ) {
2173                         n[j] = nodes[ nrow - 3 + j ][ ncol ];
2174                     }
2175                     smooth = true;
2176                 }
2177             }
2178 
2179             if( smooth ) {
2180 
2181                 // Let the smoothing begin
2182                 // std::cout << " checking: " << ncol << " " << nrow << std::endl;
2183 
2184                 // Get initial slopes using closest handles.
2185                 double slope[2][3];
2186                 double slope_ave[3];
2187                 double slope_diff[3];
2188 
2189                 // Color of corners
2190                 SPColor color0 = n[0]->color;
2191                 SPColor color3 = n[3]->color;
2192                 SPColor color6 = n[6]->color;
2193 
2194                 // Distance nodes from selected corner
2195                 Geom::Point d[7];
2196                 for( guint k = 0; k < 7; ++k ) {
2197                     d[k]= n[k]->p - n[3]->p;
2198                     // std::cout << " d[" << k << "]: " << d[k].length() << std::endl;
2199                 }
2200 
2201                 double sdm = -1.0; // Slope Diff Max
2202                 guint cdm = 0; // Color Diff Max  (Which color has the maximum difference in slopes)
2203                 for( guint c = 0; c < 3; ++c ) {
2204                     if( d[2].length() != 0.0 ) {
2205                         slope[0][c] = (color3.v.c[c] - color0.v.c[c]) / d[2].length();
2206                     }
2207                     if( d[4].length() != 0.0 ) {
2208                         slope[1][c] = (color6.v.c[c] - color3.v.c[c]) / d[4].length();
2209                     }
2210                     slope_ave[c]  = (slope[0][c]+slope[1][c]) / 2.0;
2211                     slope_diff[c] = (slope[0][c]-slope[1][c]);
2212                     // std::cout << "  color: " << c << " :"
2213                     //           << color0.v.c[c] << " "
2214                     //           << color3.v.c[c] << " "
2215                     //           << color6.v.c[c]
2216                     //           << "  slope: "
2217                     //           << slope[0][c] << " "
2218                     //           << slope[1][c]
2219                     //           << "  slope_ave: " << slope_ave[c]
2220                     //           << "  slope_diff: " << slope_diff[c]
2221                     //           << std::endl;
2222 
2223                     // Find color with maximum difference
2224                     if( std::abs( slope_diff[c] ) > sdm ) {
2225                         sdm = std::abs( slope_diff[c] );
2226                         cdm = c;
2227                     }
2228                 }
2229                 // std::cout << " cdm: " << cdm << std::endl;
2230 
2231                 // Find new handle positions:
2232                 double length_left  = d[0].length();
2233                 double length_right = d[6].length();
2234                 if( slope_ave[ cdm ] != 0.0 ) {
2235                     length_left  = std::abs( (color3.v.c[cdm] - color0.v.c[cdm]) / slope_ave[ cdm ] );
2236                     length_right = std::abs( (color6.v.c[cdm] - color3.v.c[cdm]) / slope_ave[ cdm ] );
2237                 }
2238 
2239                 // Move closest handle a maximum of mid point... but don't shorten
2240                 double max = 0.8;
2241                 if( length_left  > max * d[0].length() && length_left > d[2].length() ) {
2242                     std::cout << " Can't smooth left side" << std::endl;
2243                     length_left  = std::max( max * d[0].length(), d[2].length() );
2244                 }
2245                 if( length_right  > max * d[6].length() && length_right > d[4].length() ) {
2246                     std::cout << " Can't smooth right side" << std::endl;
2247                     length_right  = std::max( max * d[6].length(), d[4].length() );
2248                 }
2249 
2250                 if( d[2].length() != 0.0 ) d[2] *= length_left/d[2].length();
2251                 if( d[4].length() != 0.0 ) d[4] *= length_right/d[4].length();
2252 
2253                 // std::cout << "  length_left: " << length_left
2254                 //           << "  d[0]: " << d[0].length()
2255                 //           << "  length_right: " << length_right
2256                 //           << "  d[6]: " << d[6].length()
2257                 //           << std::endl;
2258 
2259                 n[2]->p = n[3]->p + d[2];
2260                 n[4]->p = n[3]->p + d[4];
2261 
2262                 ++smoothed;
2263             }
2264         }
2265 
2266     }
2267 
2268     if( smoothed > 0 ) built = false;
2269     return smoothed;
2270 }
2271 
2272 /**
2273    Pick color from background for selected corners.
2274 */
color_pick(std::vector<guint> icorners,SPItem * item)2275 guint SPMeshNodeArray::color_pick( std::vector<guint> icorners, SPItem* item ) {
2276 
2277     // std::cout << "SPMeshNodeArray::color_pick" << std::endl;
2278 
2279     guint picked = 0;
2280 
2281     // Code inspired from clone tracing
2282 
2283     // Setup...
2284 
2285     // We need a copy of the drawing so we can hide the mesh.
2286     Inkscape::Drawing *pick_drawing = new Inkscape::Drawing();
2287     unsigned pick_visionkey = SPItem::display_key_new(1);
2288 
2289     SPDocument *pick_doc = mg->document;
2290 
2291     pick_drawing->setRoot(pick_doc->getRoot()->invoke_show(*pick_drawing, pick_visionkey, SP_ITEM_SHOW_DISPLAY));
2292 
2293     item->invoke_hide(pick_visionkey);
2294 
2295     pick_doc->getRoot()->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
2296     pick_doc->ensureUpToDate();
2297 
2298     //gdouble pick_zoom = 1.0; // zoom;
2299     //pick_drawing->root()->setTransform(Geom::Scale(pick_zoom));
2300     pick_drawing->update();
2301 
2302     // std::cout << " transform: " << std::endl;
2303     // std::cout << item->transform << std::endl;
2304     // std::cout << " i2doc: " << std::endl;
2305     // std::cout << item->i2doc_affine() << std::endl;
2306     // std::cout << " i2dt: " << std::endl;
2307     // std::cout << item->i2dt_affine() << std::endl;
2308     // std::cout << " dt2i: " << std::endl;
2309     // std::cout << item->dt2i_affine() << std::endl;
2310     SPGradient* gr = SP_GRADIENT( mg );
2311     // if( gr->gradientTransform_set ) {
2312     //     std::cout << " gradient transform set: " << std::endl;
2313     //     std::cout << gr->gradientTransform << std::endl;
2314     // } else {
2315     //     std::cout << " gradient transform not set! " << std::endl;
2316     // }
2317 
2318     // Do picking
2319     for(unsigned int corner : icorners) {
2320 
2321         SPMeshNode* n = corners[ corner ];
2322 
2323         // Region to average over
2324         Geom::Point p = n->p;
2325         // std::cout << " before transform: p: " << p << std::endl;
2326         p *= gr->gradientTransform;
2327         // std::cout << " after transform:  p: " << p << std::endl;
2328         p *= item->i2doc_affine();
2329         // std::cout << " after transform:  p: " << p << std::endl;
2330 
2331         // If on edge, move inward
2332         guint cols = patch_columns()+1;
2333         guint rows = patch_rows()+1;
2334         guint col = corner % cols;
2335         guint row = corner / cols;
2336         guint ncol  = col * 3;
2337         guint nrow  = row * 3;
2338 
2339         const double size = 3.0;
2340 
2341         // Top edge
2342         if( row == 0 ) {
2343             Geom::Point dp = nodes[nrow+1][ncol]->p - p;
2344             p += unit_vector( dp ) * size;
2345         }
2346         // Right edge
2347         if( col == cols-1 ) {
2348             Geom::Point dp = nodes[nrow][ncol-1]->p - p;
2349             p += unit_vector( dp ) * size;
2350         }
2351         // Bottom edge
2352         if( row == rows-1 ) {
2353             Geom::Point dp = nodes[nrow-1][ncol]->p - p;
2354             p += unit_vector( dp ) * size;
2355         }
2356         // Left edge
2357         if( col == 0 ) {
2358             Geom::Point dp = nodes[nrow][ncol+1]->p - p;
2359             p += unit_vector( dp ) * size;
2360         }
2361 
2362         Geom::Rect box( p[Geom::X]-size/2.0, p[Geom::Y]-size/2.0,
2363                         p[Geom::X]+size/2.0, p[Geom::Y]+size/2.0 );
2364 
2365         /* Item integer bbox in points */
2366         Geom::IntRect ibox = box.roundOutwards();
2367 
2368         /* Find visible area */
2369         cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ibox.width(), ibox.height());
2370         Inkscape::DrawingContext dc(s, ibox.min());
2371 
2372         /* Render copy and pick color */
2373         pick_drawing->render(dc, ibox);
2374         double R = 0, G = 0, B = 0, A = 0;
2375         ink_cairo_surface_average_color(s, R, G, B, A);
2376         cairo_surface_destroy(s);
2377 
2378         // std::cout << " p: " << p
2379         //           << " box: " << ibox
2380         //           << " R: " << R
2381         //           << " G: " << G
2382         //           << " B: " << B
2383         //           << std::endl;
2384         n->color.set( R, G, B );
2385     }
2386 
2387     pick_doc->getRoot()->invoke_hide(pick_visionkey);
2388     delete pick_drawing;
2389 
2390     picked = 1; // Picking always happens
2391     if( picked > 0 ) built = false;
2392     return picked;
2393 }
2394 
2395 /**
2396    Splits selected rows and/or columns in half (according to the path 't' parameter).
2397    Input is a list of selected corner draggable indices.
2398 */
insert(std::vector<guint> corners)2399 guint SPMeshNodeArray::insert( std::vector<guint> corners ) {
2400 
2401     guint inserted = 0;
2402 
2403     if( corners.size() < 2 ) return 0;
2404 
2405     std::set<guint> columns;
2406     std::set<guint> rows;
2407 
2408     for( guint i = 0; i < corners.size()-1; ++i ) {
2409         for( guint j = i+1; j < corners.size(); ++j ) {
2410 
2411             // This works as all corners have indices and they
2412             // are numbered in order by row and column (and
2413             // the node array is rectangular).
2414 
2415             guint c1 = corners[i];
2416             guint c2 = corners[j];
2417             if (c2 < c1) {
2418                 c1 = corners[j];
2419                 c2 = corners[i];
2420             }
2421 
2422             // Number of corners in a row of patches.
2423             guint ncorners = patch_columns() + 1;
2424 
2425             guint crow1 = c1 / ncorners;
2426             guint crow2 = c2 / ncorners;
2427             guint ccol1 = c1 % ncorners;
2428             guint ccol2 = c2 % ncorners;
2429 
2430             // Check for horizontal neighbors
2431             if ( crow1 == crow2 && (ccol2 - ccol1) == 1 ) {
2432                 columns.insert( ccol1 );
2433             }
2434 
2435             // Check for vertical neighbors
2436             if ( ccol1 == ccol2 && (crow2 - crow1) == 1 ) {
2437                 rows.insert( crow1 );
2438             }
2439         }
2440     }
2441 
2442     // Iterate backwards so column/row numbers are not invalidated.
2443     std::set<guint>::reverse_iterator rit;
2444     for (rit=columns.rbegin(); rit != columns.rend(); ++rit) {
2445         split_column( *rit, 0.5);
2446         ++inserted;
2447     }
2448     for (rit=rows.rbegin(); rit != rows.rend(); ++rit) {
2449         split_row( *rit, 0.5);
2450         ++inserted;
2451     }
2452 
2453     if( inserted > 0 ) built = false;
2454     return inserted;
2455 }
2456 
2457 /**
2458    Moves handles in response to a corner node move.
2459    p_old: original position of moved corner node.
2460    corner: the corner node moved (draggable index, i.e. point_i).
2461    selected: list of all corners selected (draggable indices).
2462    op: how other corners should be moved.
2463    Corner node must already have been moved!
2464 */
update_handles(guint corner,std::vector<guint>,Geom::Point p_old,MeshNodeOperation)2465 void SPMeshNodeArray::update_handles( guint corner, std::vector< guint > /*selected*/, Geom::Point p_old, MeshNodeOperation /*op*/ )
2466 {
2467     if (!draggers_valid) {
2468         std::cerr << "SPMeshNodeArray::update_handles: Draggers not valid!" << std::endl;
2469         return;
2470     }
2471     // assert( draggers_valid );
2472 
2473     // std::cout << "SPMeshNodeArray::update_handles: "
2474     //           << "  corner: " << corner
2475     //           << "  op: " << op
2476     //           << std::endl;
2477 
2478     // Find number of patch rows and columns
2479     guint mrow = patch_rows();
2480     guint mcol = patch_columns();
2481 
2482     // Number of corners in a row of patches.
2483     guint ncorners = mcol + 1;
2484 
2485     // Find corner row/column
2486     guint crow = corner / ncorners;
2487     guint ccol = corner % ncorners;
2488 
2489     // Find node row/column
2490     guint nrow  = crow * 3;
2491     guint ncol  = ccol * 3;
2492 
2493     // std::cout << "  mrow: " << mrow
2494     //           << "  mcol: " << mcol
2495     //           << "  crow: " << crow
2496     //           << "  ccol: " << ccol
2497     //           << "  ncorners: " << ncorners
2498     //           << "  nrow: " << nrow
2499     //           << "  ncol: " << ncol
2500     //           << std::endl;
2501 
2502     // New corner mesh coordinate.
2503     Geom::Point p_new = nodes[nrow][ncol]->p;
2504 
2505     // Corner point move dpg in mesh coordinate system.
2506     Geom::Point dp = p_new - p_old;
2507 
2508     // std::cout << "  p_old: " << p_old << std::endl;
2509     // std::cout << "  p_new: " << p_new << std::endl;
2510     // std::cout << "     dp: " << dp << std::endl;
2511 
2512     // STEP 1: ONLY DO DIRECT MOVE
2513     bool patch[4];
2514     patch[0] = patch[1] = patch[2] = patch[3] = false;
2515     if( ccol > 0    && crow > 0    ) patch[0] = true;
2516     if( ccol < mcol && crow > 0    ) patch[1] = true;
2517     if( ccol < mcol && crow < mrow ) patch[2] = true;
2518     if( ccol > 0    && crow < mrow ) patch[3] = true;
2519 
2520     // std::cout << patch[0] << " "
2521     //           << patch[1] << " "
2522     //           << patch[2] << " "
2523     //           << patch[3] << std::endl;
2524 
2525     // Move handles
2526     if( patch[0] || patch[1] ) {
2527         if( nodes[nrow-1][ncol]->path_type == 'l' ||
2528             nodes[nrow-1][ncol]->path_type == 'L' ) {
2529             Geom::Point s = (nodes[nrow-3][ncol]->p - nodes[nrow][ncol]->p)/3.0;
2530             nodes[nrow-1][ncol  ]->p = nodes[nrow][ncol]->p + s;
2531             nodes[nrow-2][ncol  ]->p = nodes[nrow-3][ncol]->p - s;
2532         } else {
2533             nodes[nrow-1][ncol  ]->p += dp;
2534         }
2535     }
2536 
2537     if( patch[1] || patch[2] )  {
2538         if( nodes[nrow  ][ncol+1]->path_type == 'l' ||
2539             nodes[nrow  ][ncol+1]->path_type == 'L' ) {
2540             Geom::Point s = (nodes[nrow][ncol+3]->p - nodes[nrow][ncol]->p)/3.0;
2541             nodes[nrow  ][ncol+1]->p = nodes[nrow][ncol]->p + s;
2542             nodes[nrow  ][ncol+2]->p = nodes[nrow][ncol+3]->p - s;
2543         } else {
2544             nodes[nrow  ][ncol+1]->p += dp;
2545         }
2546     }
2547 
2548     if( patch[2] || patch[3] ) {
2549         if( nodes[nrow+1][ncol  ]->path_type == 'l' ||
2550             nodes[nrow+1][ncol  ]->path_type == 'L' ) {
2551             Geom::Point s = (nodes[nrow+3][ncol]->p - nodes[nrow][ncol]->p)/3.0;
2552             nodes[nrow+1][ncol  ]->p = nodes[nrow][ncol]->p + s;
2553             nodes[nrow+2][ncol  ]->p = nodes[nrow+3][ncol]->p - s;
2554         } else {
2555             nodes[nrow+1][ncol  ]->p += dp;
2556         }
2557     }
2558 
2559     if( patch[3] || patch[0] ) {
2560         if( nodes[nrow  ][ncol-1]->path_type == 'l' ||
2561             nodes[nrow  ][ncol-1]->path_type == 'L' ) {
2562             Geom::Point s = (nodes[nrow][ncol-3]->p - nodes[nrow][ncol]->p)/3.0;
2563             nodes[nrow  ][ncol-1]->p = nodes[nrow][ncol]->p + s;
2564             nodes[nrow  ][ncol-2]->p = nodes[nrow][ncol-3]->p - s;
2565         } else {
2566             nodes[nrow  ][ncol-1]->p += dp;
2567         }
2568     }
2569 
2570 
2571     // Move tensors
2572     if( patch[0] ) nodes[nrow-1][ncol-1]->p += dp;
2573     if( patch[1] ) nodes[nrow-1][ncol+1]->p += dp;
2574     if( patch[2] ) nodes[nrow+1][ncol+1]->p += dp;
2575     if( patch[3] ) nodes[nrow+1][ncol-1]->p += dp;
2576 
2577     // // Check if neighboring corners are selected.
2578 
2579     // bool do_scale = false;
2580 
2581     // bool do_scale_xp = do_scale;
2582     // bool do_scale_xn = do_scale;
2583     // bool do_scale_yp = do_scale;
2584     // bool do_scale_yn = do_scale;
2585 
2586     // if( ccol < mcol+1 ) {
2587     //     if( std::find( sc.begin(), sc.end(), point_i + 1 ) != sc.end() ) {
2588     //         do_scale_xp = false;
2589     //         std::cout << "  Not scaling x+" << std::endl;
2590     //     }
2591     // }
2592 
2593     // if( ccol > 0 ) {
2594     //     if( std::find( sc.begin(), sc.end(), point_i - 1 ) != sc.end() ) {
2595     //         do_scale_xn = false;
2596     //         std::cout << "  Not scaling x-" << std::endl;
2597     //     }
2598     // }
2599 
2600     // if( crow < mrow+1 ) {
2601     //     if( std::find( sc.begin(), sc.end(), point_i + ncorners ) != sc.end() ) {
2602     //         do_scale_yp = false;
2603     //         std::cout << "  Not scaling y+" << std::endl;
2604     //     }
2605     // }
2606 
2607     // if( crow > 0 ) {
2608     //     if( std::find( sc.begin(), sc.end(), point_i - ncorners ) != sc.end() ) {
2609     //         do_scale_yn = false;
2610     //         std::cout << "  Not scaling y-" << std::endl;
2611     //     }
2612     // }
2613 
2614     // // We have four patches to adjust...
2615     // for ( guint k = 0; k < 4;  ++k ) {
2616 
2617     //     bool do_scale_x = do_scale;
2618     //     bool do_scale_y = do_scale;
2619 
2620     //     SPMeshNode* pnodes[4][4];
2621 
2622     //     // Load up matrix
2623     //     switch (k) {
2624 
2625     //         case 0:
2626     //             if( crow < mrow+1 && ccol < mcol+1 ) {
2627     //                 // Bottom right patch
2628 
2629     //                 do_scale_x = do_scale_xp;
2630     //                 do_scale_y = do_scale_yp;
2631 
2632     //                 for( guint i = 0; i < 4; ++i ) {
2633     //                     for( guint j = 0; j< 4; ++j ) {
2634     //                         pnodes[i][j] = mg->array.nodes[nrow+i][nrow+j];
2635     //                     }
2636     //                 }
2637     //             }
2638     //             break;
2639 
2640     //         case 1:
2641     //             if( crow < mrow+1 && ccol > 0 ) {
2642     //                 // Bottom left patch (note x, y swapped)
2643 
2644     //                 do_scale_y = do_scale_xn;
2645     //                 do_scale_x = do_scale_yp;
2646 
2647     //                 for( guint i = 0; i < 4; ++i ) {
2648     //                     for( guint j = 0; j< 4; ++j ) {
2649     //                         pnodes[j][i] = mg->array.nodes[nrow+i][nrow-j];
2650     //                     }
2651     //                 }
2652     //             }
2653     //             break;
2654 
2655     //         case 2:
2656     //             if( crow > 0 && ccol > 0 ) {
2657     //                 // Top left patch
2658 
2659     //                 do_scale_x = do_scale_xn;
2660     //                 do_scale_y = do_scale_yn;
2661 
2662     //                 for( guint i = 0; i < 4; ++i ) {
2663     //                     for( guint j = 0; j< 4; ++j ) {
2664     //                         pnodes[i][j] = mg->array.nodes[nrow-i][nrow-j];
2665     //                     }
2666     //                 }
2667     //             }
2668     //             break;
2669 
2670     //         case 3:
2671     //             if( crow > 0 && ccol < mcol+1 ) {
2672     //                 // Top right patch (note x, y swapped)
2673 
2674     //                 do_scale_y = do_scale_xp;
2675     //                 do_scale_x = do_scale_yn;
2676 
2677     //                 for( guint i = 0; i < 4; ++i ) {
2678     //                     for( guint j = 0; j< 4; ++j ) {
2679     //                         pnodes[j][i] = mg->array.nodes[nrow-i][nrow+j];
2680     //                     }
2681     //                 }
2682     //             }
2683     //             break;
2684     //     }
2685 
2686     //     // Now we must move points in both x and y.
2687     //     // There are upto six points to move: P01, P02, P11, P12, P21, P22.
2688     //     // (The points P10, P20 will be moved in another branch of the loop.
2689     //     // The points P03, P13, P23, P33, P32, P31, P30 are not moved.)
2690     //     //
2691     //     //   P00  P01  P02  P03
2692     //     //   P10  P11  P12  P13
2693     //     //   P20  P21  P22  P23
2694     //     //   P30  P31  P32  P33
2695     //     //
2696     //     // The goal is to preserve the direction of the handle!
2697 
2698 
2699     //     Geom::Point dsx_new = pnodes[0][3]->p - pnodes[0][0]->p; // New side x
2700     //     Geom::Point dsy_new = pnodes[3][0]->p - pnodes[0][0]->p; // New side y
2701     //     Geom::Point dsx_old = pnodes[0][3]->p - pcg_old; // Old side x
2702     //     Geom::Point dsy_old = pnodes[3][0]->p - pcg_old; // Old side y
2703 
2704 
2705     //     double scale_factor_x = 1.0;
2706     //     if( dsx_old.length() != 0.0 ) scale_factor_x = dsx_new.length()/dsx_old.length();
2707 
2708     //     double scale_factor_y = 1.0;
2709     //     if( dsy_old.length() != 0.0 ) scale_factor_y = dsy_new.length()/dsy_old.length();
2710 
2711 
2712     //     if( do_scalex && do_scaley ) {
2713 
2714     //         // We have six point to move.
2715 
2716     //         // P01
2717     //         Geom::Point dp01 = pnodes[0][1] - pcg_old;
2718     //         dp01 *= scale_factor_x;
2719     //         pnodes[0][1] = pnodes[0][0] + dp01;
2720 
2721     //         // P02
2722     //         Geom::Point dp02 = pnodes[0][2] - pnodes[0][3];
2723     //         dp02 *= scale_factor_x;
2724     //         pnodes[0][2] = pnodes[0][3] + dp02;
2725 
2726     //         // P11
2727     //         Geom::Point dp11 = pnodes[1][1] - pcg_old;
2728     //         dp11 *= scale_factor_x;
2729     //         pnodes[1][1] = pnodes[0][0] + dp11;
2730 
2731 
2732 
2733     //         // P21
2734     //         Geom::Point dp21 = pnodes[2][1] - pnodes[3][0];
2735     //         dp21 *= scale_factor_x;
2736     //         dp21 *= scale_factor_y;
2737     //         pnodes[2][1] = pnodes[3][0] + dp21;
2738 
2739 
2740     //         Geom::Point dsx1 = pnodes[0][1]->p -
2741 }
2742 
outline_path() const2743 std::unique_ptr<SPCurve> SPMeshNodeArray::outline_path() const
2744 {
2745     auto outline = std::make_unique<SPCurve>();
2746 
2747     if (nodes.empty() ) {
2748         std::cerr << "SPMeshNodeArray::outline_path: empty array!" << std::endl;
2749         return outline;
2750     }
2751 
2752     outline->moveto( nodes[0][0]->p );
2753 
2754     int ncol = nodes[0].size();
2755     int nrow = nodes.size();
2756 
2757     // Top
2758     for (int i = 1; i < ncol; i += 3 ) {
2759         outline->curveto( nodes[0][i]->p, nodes[0][i+1]->p, nodes[0][i+2]->p);
2760     }
2761 
2762     // Right
2763     for (int i = 1; i < nrow; i += 3 ) {
2764         outline->curveto( nodes[i][ncol-1]->p, nodes[i+1][ncol-1]->p, nodes[i+2][ncol-1]->p);
2765     }
2766 
2767     // Bottom (right to left)
2768     for (int i = 1; i < ncol; i += 3 ) {
2769         outline->curveto( nodes[nrow-1][ncol-i-1]->p, nodes[nrow-1][ncol-i-2]->p, nodes[nrow-1][ncol-i-3]->p);
2770     }
2771 
2772     // Left (bottom to top)
2773     for (int i = 1; i < nrow; i += 3 ) {
2774         outline->curveto( nodes[nrow-i-1][0]->p, nodes[nrow-i-2][0]->p, nodes[nrow-i-3][0]->p);
2775     }
2776 
2777     outline->closepath();
2778 
2779     return outline;
2780 }
2781 
transform(Geom::Affine const & m)2782 void SPMeshNodeArray::transform(Geom::Affine const &m) {
2783 
2784     for (int i = 0; i < nodes[0].size(); ++i) {
2785         for (auto & node : nodes) {
2786             node[i]->p *= m;
2787         }
2788     }
2789 }
2790 
2791 // Transform mesh to fill box. Return true if mesh transformed.
fill_box(Geom::OptRect & box)2792 bool SPMeshNodeArray::fill_box(Geom::OptRect &box) {
2793 
2794     // If gradientTransfor is set (as happens when an object is transformed
2795     // with the "optimized" preferences set true), we need to remove it.
2796     if (mg->gradientTransform_set) {
2797         Geom::Affine gt = mg->gradientTransform;
2798         transform( gt );
2799         mg->gradientTransform_set = false;
2800         mg->gradientTransform.setIdentity();
2801     }
2802 
2803     auto outline = outline_path();
2804     Geom::OptRect mesh_bbox = outline->get_pathvector().boundsExact();
2805 
2806     if ((*mesh_bbox).width() == 0 || (*mesh_bbox).height() == 0) {
2807         return false;
2808     }
2809 
2810     double scale_x = (*box).width() /(*mesh_bbox).width() ;
2811     double scale_y = (*box).height()/(*mesh_bbox).height();
2812 
2813     Geom::Translate t1(-(*mesh_bbox).min());
2814     Geom::Scale scale(scale_x,scale_y);
2815     Geom::Translate t2((*box).min());
2816     Geom::Affine trans = t1 * scale * t2;
2817     if (!trans.isIdentity() ) {
2818         transform(trans);
2819         write( mg );
2820         mg->requestModified(SP_OBJECT_MODIFIED_FLAG);
2821         return true;
2822     }
2823 
2824     return false;
2825 }
2826 
2827 // Defined in gradient-chemistry.cpp
2828 guint32 average_color(guint32 c1, guint32 c2, gdouble p);
2829 
2830 /**
2831    Split a row into n equal parts.
2832 */
split_row(unsigned int row,unsigned int n)2833 void SPMeshNodeArray::split_row( unsigned int row, unsigned int n ) {
2834 
2835     double nn = n;
2836     if( n > 1 ) split_row( row, (nn-1)/nn );
2837     if( n > 2 ) split_row( row, n-1 );
2838 }
2839 
2840 /**
2841    Split a column into n equal parts.
2842 */
split_column(unsigned int col,unsigned int n)2843 void SPMeshNodeArray::split_column( unsigned int col, unsigned int n ) {
2844 
2845     double nn = n;
2846     if( n > 1 ) split_column( col, (nn-1)/nn );
2847     if( n > 2 ) split_column( col, n-1 );
2848 }
2849 
2850 /**
2851    Split a row into two rows at coord (fraction of row height).
2852 */
split_row(unsigned int row,double coord)2853 void SPMeshNodeArray::split_row( unsigned int row, double coord ) {
2854 
2855     // std::cout << "Splitting row: " << row << " at " << coord << std::endl;
2856     // print();
2857     assert( coord >= 0.0 && coord <= 1.0 );
2858     assert( row < patch_rows() );
2859 
2860     built = false;
2861 
2862     // First step is to ensure that handle and tensor points are up-to-date if they are not set.
2863     // (We can't do this on the fly as we overwrite the necessary points to do the calculation
2864     //  during the update.)
2865     for( guint j = 0; j < patch_columns(); ++ j ) {
2866         SPMeshPatchI patch( &nodes, row, j );
2867         patch.updateNodes();
2868     }
2869 
2870     // Add three new rows of empty nodes
2871     for( guint i = 0; i < 3; ++i ) {
2872         std::vector< SPMeshNode* > new_row;
2873         for( guint j = 0; j < nodes[0].size(); ++j ) {
2874             SPMeshNode* new_node = new SPMeshNode;
2875             new_row.push_back( new_node );
2876         }
2877         nodes.insert( nodes.begin()+3*(row+1), new_row );
2878     }
2879 
2880     guint i = 3 * row; // Convert from patch row to node row
2881     for( guint j = 0; j < nodes[i].size(); ++j ) {
2882 
2883         // std::cout << "Splitting row: column: " << j << std::endl;
2884 
2885         Geom::Point p[4];
2886         for( guint k = 0; k < 4; ++k ) {
2887             guint n = k;
2888             if( k == 3 ) n = 6; // Bottom patch row has been shifted by new rows
2889             p[k] = nodes[i+n][j]->p;
2890             // std::cout << p[k] << std::endl;
2891         }
2892 
2893         Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] );
2894 
2895         std::pair<Geom::BezierCurveN<3>, Geom::BezierCurveN<3> > b_new =
2896             b.subdivide( coord );
2897 
2898         // Update points
2899         for( guint n = 0; n < 4; ++n ) {
2900             nodes[i+n  ][j]->p = b_new.first[n];
2901             nodes[i+n+3][j]->p = b_new.second[n];
2902             // std::cout << b_new.first[n] << "  " << b_new.second[n] << std::endl;
2903         }
2904 
2905         if( nodes[i][j]->node_type == MG_NODE_TYPE_CORNER ) {
2906             // We are splitting a side
2907 
2908             // Path type stored in handles.
2909             gchar path_type = nodes[i+1][j]->path_type;
2910             nodes[i+4][j]->path_type = path_type;
2911             nodes[i+5][j]->path_type = path_type;
2912             bool  set = nodes[i+1][j]->set;
2913             nodes[i+4][j]->set = set;
2914             nodes[i+5][j]->set = set;
2915             nodes[i+4][j]->node_type = MG_NODE_TYPE_HANDLE;
2916             nodes[i+5][j]->node_type = MG_NODE_TYPE_HANDLE;
2917 
2918             // Color stored in corners
2919             guint c0 = nodes[i  ][j]->color.toRGBA32( 1.0 );
2920             guint c1 = nodes[i+6][j]->color.toRGBA32( 1.0 );
2921             gdouble o0 = nodes[i  ][j]->opacity;
2922             gdouble o1 = nodes[i+6][j]->opacity;
2923             guint cnew = average_color( c0, c1, coord );
2924             gdouble onew = o0 * (1.0 - coord) + o1 * coord;
2925             nodes[i+3][j]->color.set( cnew );
2926             nodes[i+3][j]->opacity = onew;
2927             nodes[i+3][j]->node_type = MG_NODE_TYPE_CORNER;
2928             nodes[i+3][j]->set = true;
2929 
2930         } else {
2931             // We are splitting a middle
2932 
2933             bool set = nodes[i+1][j]->set || nodes[i+2][j]->set;
2934             nodes[i+4][j]->set = set;
2935             nodes[i+5][j]->set = set;
2936             nodes[i+4][j]->node_type = MG_NODE_TYPE_TENSOR;
2937             nodes[i+5][j]->node_type = MG_NODE_TYPE_TENSOR;
2938 
2939             // Path type, if different, choose l -> L -> c -> C.
2940             gchar path_type0 = nodes[i  ][j]->path_type;
2941             gchar path_type1 = nodes[i+6][j]->path_type;
2942             gchar path_type = 'l';
2943             if( path_type0 == 'L' || path_type1 == 'L') path_type = 'L';
2944             if( path_type0 == 'c' || path_type1 == 'c') path_type = 'c';
2945             if( path_type0 == 'C' || path_type1 == 'C') path_type = 'C';
2946             nodes[i+3][j]->path_type = path_type;
2947             nodes[i+3][j]->node_type = MG_NODE_TYPE_HANDLE;
2948             if( path_type == 'c' || path_type == 'C' ) nodes[i+3][j]->set = true;
2949 
2950         }
2951 
2952         nodes[i+3][j]->node_edge = MG_NODE_EDGE_NONE;
2953         nodes[i+4][j]->node_edge = MG_NODE_EDGE_NONE;
2954         nodes[i+5][j]->node_edge = MG_NODE_EDGE_NONE;;
2955         if( j == 0 ) {
2956             nodes[i+3][j]->node_edge |= MG_NODE_EDGE_LEFT;
2957             nodes[i+4][j]->node_edge |= MG_NODE_EDGE_LEFT;
2958             nodes[i+5][j]->node_edge |= MG_NODE_EDGE_LEFT;
2959         }
2960         if( j == nodes[i].size() - 1 ) {
2961             nodes[i+3][j]->node_edge |= MG_NODE_EDGE_RIGHT;
2962             nodes[i+4][j]->node_edge |= MG_NODE_EDGE_RIGHT;
2963             nodes[i+5][j]->node_edge |= MG_NODE_EDGE_RIGHT;
2964         }
2965     }
2966 
2967     // std::cout << "Splitting row: result:" << std::endl;
2968     // print();
2969 }
2970 
2971 
2972 
2973 /**
2974    Split a column into two columns at coord (fraction of column width).
2975 */
split_column(unsigned int col,double coord)2976 void SPMeshNodeArray::split_column( unsigned int col, double coord ) {
2977 
2978     // std::cout << "Splitting column: " << col << " at " << coord << std::endl;
2979     // print();
2980     assert( coord >= 0.0 && coord <= 1.0 );
2981     assert( col < patch_columns() );
2982 
2983     built = false;
2984 
2985     // First step is to ensure that handle and tensor points are up-to-date if they are not set.
2986     // (We can't do this on the fly as we overwrite the necessary points to do the calculation
2987     //  during the update.)
2988     for( guint i = 0; i < patch_rows(); ++ i ) {
2989         SPMeshPatchI patch( &nodes, i, col );
2990         patch.updateNodes();
2991     }
2992 
2993     guint j = 3 * col; // Convert from patch column to node column
2994     for( guint i = 0; i < nodes.size(); ++i ) {
2995 
2996         // std::cout << "Splitting column: row: " << i << std::endl;
2997 
2998         Geom::Point p[4];
2999         for( guint k = 0; k < 4; ++k ) {
3000             p[k] = nodes[i][j+k]->p;
3001         }
3002 
3003         Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] );
3004 
3005         std::pair<Geom::BezierCurveN<3>, Geom::BezierCurveN<3> > b_new =
3006             b.subdivide( coord );
3007 
3008         // Add three new nodes
3009         for( guint n = 0; n < 3; ++n ) {
3010             SPMeshNode* new_node = new SPMeshNode;
3011             nodes[i].insert( nodes[i].begin()+j+3, new_node );
3012         }
3013 
3014         // Update points
3015         for( guint n = 0; n < 4; ++n ) {
3016             nodes[i][j+n]->p = b_new.first[n];
3017             nodes[i][j+n+3]->p = b_new.second[n];
3018         }
3019 
3020         if( nodes[i][j]->node_type == MG_NODE_TYPE_CORNER ) {
3021             // We are splitting a side
3022 
3023             // Path type stored in handles.
3024             gchar path_type = nodes[i][j+1]->path_type;
3025             nodes[i][j+4]->path_type = path_type;
3026             nodes[i][j+5]->path_type = path_type;
3027             bool  set = nodes[i][j+1]->set;
3028             nodes[i][j+4]->set = set;
3029             nodes[i][j+5]->set = set;
3030             nodes[i][j+4]->node_type = MG_NODE_TYPE_HANDLE;
3031             nodes[i][j+5]->node_type = MG_NODE_TYPE_HANDLE;
3032 
3033             // Color stored in corners
3034             guint c0 = nodes[i][j  ]->color.toRGBA32( 1.0 );
3035             guint c1 = nodes[i][j+6]->color.toRGBA32( 1.0 );
3036             gdouble o0 = nodes[i][j  ]->opacity;
3037             gdouble o1 = nodes[i][j+6]->opacity;
3038             guint cnew = average_color( c0, c1, coord );
3039             gdouble onew = o0 * (1.0 - coord) + o1 * coord;
3040             nodes[i][j+3]->color.set( cnew );
3041             nodes[i][j+3]->opacity = onew;
3042             nodes[i][j+3]->node_type = MG_NODE_TYPE_CORNER;
3043             nodes[i][j+3]->set = true;
3044 
3045         } else {
3046             // We are splitting a middle
3047 
3048             bool  set = nodes[i][j+1]->set || nodes[i][j+2]->set;
3049             nodes[i][j+4]->set = set;
3050             nodes[i][j+5]->set = set;
3051             nodes[i][j+4]->node_type = MG_NODE_TYPE_TENSOR;
3052             nodes[i][j+5]->node_type = MG_NODE_TYPE_TENSOR;
3053 
3054             // Path type, if different, choose l -> L -> c -> C.
3055             gchar path_type0 = nodes[i][j  ]->path_type;
3056             gchar path_type1 = nodes[i][j+6]->path_type;
3057             gchar path_type = 'l';
3058             if( path_type0 == 'L' || path_type1 == 'L') path_type = 'L';
3059             if( path_type0 == 'c' || path_type1 == 'c') path_type = 'c';
3060             if( path_type0 == 'C' || path_type1 == 'C') path_type = 'C';
3061             nodes[i][j+3]->path_type = path_type;
3062             nodes[i][j+3]->node_type = MG_NODE_TYPE_HANDLE;
3063             if( path_type == 'c' || path_type == 'C' ) nodes[i][j+3]->set = true;
3064 
3065         }
3066 
3067         nodes[i][j+3]->node_edge = MG_NODE_EDGE_NONE;
3068         nodes[i][j+4]->node_edge = MG_NODE_EDGE_NONE;
3069         nodes[i][j+5]->node_edge = MG_NODE_EDGE_NONE;;
3070         if( i == 0 ) {
3071             nodes[i][j+3]->node_edge |= MG_NODE_EDGE_TOP;
3072             nodes[i][j+4]->node_edge |= MG_NODE_EDGE_TOP;
3073             nodes[i][j+5]->node_edge |= MG_NODE_EDGE_TOP;
3074         }
3075         if( i == nodes.size() - 1 ) {
3076             nodes[i][j+3]->node_edge |= MG_NODE_EDGE_BOTTOM;
3077             nodes[i][j+4]->node_edge |= MG_NODE_EDGE_BOTTOM;
3078             nodes[i][j+5]->node_edge |= MG_NODE_EDGE_BOTTOM;
3079         }
3080 
3081     }
3082 
3083     // std::cout << "Splitting col: result:" << std::endl;
3084     // print();
3085 }
3086 
3087 /*
3088   Local Variables:
3089   mode:c++
3090   c-file-style:"stroustrup"
3091   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
3092   indent-tabs-mode:nil
3093   fill-column:99
3094   End:
3095 */
3096 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
3097