summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTavmjong Bah <tavmjong@free.fr>2012-05-23 12:00:49 +0000
committertavmjong-free <tavmjong@free.fr>2012-05-23 12:00:49 +0000
commit0ea9b448ee16fe941d46395c2877f4d2e815b9ed (patch)
treea49cb57d691df46475d1e9fe197aeade8b0fa10c /src
parentCorrect improper flipping of sRGB transform from RGB to BGR with cairo change... (diff)
downloadinkscape-0ea9b448ee16fe941d46395c2877f4d2e815b9ed.tar.gz
inkscape-0ea9b448ee16fe941d46395c2877f4d2e815b9ed.zip
Add Mesh tool (experimental, requires Cario >= 1.11.4, disabled by default).
(bzr r11406)
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt12
-rw-r--r--src/Makefile_insert8
-rw-r--r--src/attributes.cpp3
-rw-r--r--src/attributes.h3
-rw-r--r--src/display/sp-ctrlcurve.cpp2
-rw-r--r--src/gradient-chemistry.cpp326
-rw-r--r--src/gradient-drag.cpp610
-rw-r--r--src/gradient-drag.h11
-rw-r--r--src/mesh-context.cpp1045
-rw-r--r--src/mesh-context.h71
-rw-r--r--src/preferences-skeleton.h1
-rw-r--r--src/sp-gradient-fns.h3
-rw-r--r--src/sp-gradient.cpp628
-rw-r--r--src/sp-gradient.h37
-rw-r--r--src/sp-mesh-array.cpp2508
-rw-r--r--src/sp-mesh-array.h197
-rw-r--r--src/sp-mesh-gradient-fns.h41
-rw-r--r--src/sp-mesh-gradient.h35
-rw-r--r--src/sp-mesh-patch-fns.h39
-rw-r--r--src/sp-mesh-patch.cpp63
-rw-r--r--src/sp-mesh-patch.h59
-rw-r--r--src/sp-mesh-row-fns.h39
-rw-r--r--src/sp-mesh-row.cpp63
-rw-r--r--src/sp-mesh-row.h53
-rw-r--r--src/sp-object-repr.cpp6
-rw-r--r--src/sp-stop.h3
-rw-r--r--src/tools-switch.cpp9
-rw-r--r--src/tools-switch.h1
-rw-r--r--src/verbs.cpp11
-rw-r--r--src/verbs.h2
-rw-r--r--src/widgets/CMakeLists.txt2
-rw-r--r--src/widgets/Makefile_insert2
-rw-r--r--src/widgets/gradient-toolbar.h2
-rw-r--r--src/widgets/mesh-toolbar.cpp373
-rw-r--r--src/widgets/mesh-toolbar.h22
-rw-r--r--src/widgets/toolbox.cpp23
36 files changed, 6156 insertions, 157 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 4632f0907..ddfed3e75 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -43,6 +43,9 @@ set(sp_SRC
sp-line.cpp
sp-lpe-item.cpp
sp-mask.cpp
+ sp-mesh-array.cpp
+ sp-mesh-patch.cpp
+ sp-mesh-row.cpp
sp-metadata.cpp
sp-metrics.cpp
sp-missing-glyph.cpp
@@ -127,6 +130,13 @@ set(sp_SRC
sp-lpe-item.h
sp-marker-loc.h
sp-mask.h
+ sp-mesh-array.h
+ sp-mesh-gradient-fns.h
+ sp-mesh-gradient.h
+ sp-mesh-patch-fns.h
+ sp-mesh-patch.h
+ sp-mesh-row-fns.h
+ sp-mesh-row.h
sp-metadata.h
sp-metric.h
sp-metrics.h
@@ -239,6 +249,7 @@ set(inkscape_SRC
marker.cpp
measure-context.cpp
media.cpp
+ mesh-context.cpp
message-context.cpp
message-stack.cpp
mod360.cpp
@@ -402,6 +413,7 @@ set(inkscape_SRC
media.h
memeq.h
menus-skeleton.h
+ mesh-context.h
message-context.h
message-stack.h
message.h
diff --git a/src/Makefile_insert b/src/Makefile_insert
index e9c149cb2..1ee721abe 100644
--- a/src/Makefile_insert
+++ b/src/Makefile_insert
@@ -104,6 +104,7 @@ ink_common_sources += \
media.cpp media.h \
memeq.h \
menus-skeleton.h \
+ mesh-context.cpp mesh-context.h \
message-context.cpp message-context.h \
message.h \
message-stack.cpp message-stack.h \
@@ -193,6 +194,13 @@ ink_common_sources += \
sp-marker-loc.h \
sp-mask.cpp sp-mask.h \
sp-metadata.cpp sp-metadata.h \
+ sp-mesh-array.cpp sp-mesh-array.h \
+ sp-mesh-gradient-fns.h \
+ sp-mesh-gradient.h \
+ sp-mesh-patch-fns.h \
+ sp-mesh-patch.cpp sp-mesh-patch.h \
+ sp-mesh-row-fns.h \
+ sp-mesh-row.cpp sp-mesh-row.h \
sp-metric.h \
sp-metrics.cpp sp-metrics.h \
sp-missing-glyph.cpp sp-missing-glyph.h \
diff --git a/src/attributes.cpp b/src/attributes.cpp
index 87cc0488c..7d6f8614d 100644
--- a/src/attributes.cpp
+++ b/src/attributes.cpp
@@ -289,6 +289,8 @@ static SPStyleProp const props[] = {
/* SPRadialGradient */
{SP_ATTR_FX, "fx"},
{SP_ATTR_FY, "fy"},
+ /* SPMeshPatch */
+ {SP_ATTR_TENSOR, "tensor"},
/* SPPattern */
{SP_ATTR_PATTERNUNITS, "patternUnits"},
{SP_ATTR_PATTERNCONTENTUNITS, "patternContentUnits"},
@@ -453,6 +455,7 @@ static SPStyleProp const props[] = {
/* Gradient */
{SP_PROP_STOP_COLOR, "stop-color"},
{SP_PROP_STOP_OPACITY, "stop-opacity"},
+ {SP_PROP_STOP_PATH, "path"},
/* Interactivity */
{SP_PROP_POINTER_EVENTS, "pointer-events"},
/* Paint */
diff --git a/src/attributes.h b/src/attributes.h
index c78882cbf..d8eb087b2 100644
--- a/src/attributes.h
+++ b/src/attributes.h
@@ -290,6 +290,8 @@ enum SPAttributeEnum {
/* SPRadialGradient */
SP_ATTR_FX,
SP_ATTR_FY,
+ /* SPMeshPatch */
+ SP_ATTR_TENSOR,
/* SPPattern */
SP_ATTR_PATTERNUNITS,
SP_ATTR_PATTERNCONTENTUNITS,
@@ -454,6 +456,7 @@ enum SPAttributeEnum {
/* Gradient */
SP_PROP_STOP_COLOR,
SP_PROP_STOP_OPACITY,
+ SP_PROP_STOP_PATH,
/* Interactivity */
SP_PROP_POINTER_EVENTS,
/* Paint */
diff --git a/src/display/sp-ctrlcurve.cpp b/src/display/sp-ctrlcurve.cpp
index ec078af48..5ef4c4d3f 100644
--- a/src/display/sp-ctrlcurve.cpp
+++ b/src/display/sp-ctrlcurve.cpp
@@ -1,5 +1,3 @@
-#define __INKSCAPE_CTRLCURVE_C__
-
/*
* Simple bezier curve used for Mesh Gradients
*
diff --git a/src/gradient-chemistry.cpp b/src/gradient-chemistry.cpp
index 119adfc79..ab525e482 100644
--- a/src/gradient-chemistry.cpp
+++ b/src/gradient-chemistry.cpp
@@ -7,7 +7,9 @@
* Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
* Jon A. Cruz <jon@joncruz.org>
* Abhishek Sharma
+ * Tavmjong Bah <tavmjong@free.fr>
*
+ * Copyright (C) 2012 Tavmjong Bah
* Copyright (C) 2010 Authors
* Copyright (C) 2007 Johan Engelen
* Copyright (C) 2001-2005 authors
@@ -27,8 +29,10 @@
#include "sp-gradient-vector.h"
#include "sp-linear-gradient.h"
#include "sp-radial-gradient.h"
+#include "sp-mesh-gradient.h"
#include "sp-stop.h"
#include "widgets/gradient-vector.h"
+#include "gradient-drag.h"
#include "sp-text.h"
#include "sp-tspan.h"
@@ -130,14 +134,18 @@ static SPGradient *sp_gradient_get_private_normalized(SPDocument *document, SPGr
Inkscape::XML::Node *repr;
if (type == SP_GRADIENT_TYPE_LINEAR) {
repr = xml_doc->createElement("svg:linearGradient");
- } else {
+ } else if(type == SP_GRADIENT_TYPE_RADIAL) {
repr = xml_doc->createElement("svg:radialGradient");
+ } else {
+ // Rows/patches added in sp_gradient_reset_to_userspace for new meshes.
+ repr = xml_doc->createElement("svg:meshGradient");
}
// privates are garbage-collectable
repr->setAttribute("inkscape:collect", "always");
// link to vector
+ // MESH FIXME: Meshes don't used vector... but meshes simulating gradient across/along path might.
sp_gradient_repr_set_link(repr, vector);
/* Append the new private gradient to defs */
@@ -248,11 +256,13 @@ SPGradient *sp_gradient_fork_private_if_necessary(SPGradient *gr, SPGradient *ve
repr_new->setAttribute("fx", repr->attribute("fx"));
repr_new->setAttribute("fy", repr->attribute("fy"));
repr_new->setAttribute("r", repr->attribute("r"));
- } else {
+ } else if (SP_IS_LINEARGRADIENT(gr)) {
repr_new->setAttribute("x1", repr->attribute("x1"));
repr_new->setAttribute("y1", repr->attribute("y1"));
repr_new->setAttribute("x2", repr->attribute("x2"));
repr_new->setAttribute("y2", repr->attribute("y2"));
+ } else {
+ std::cout << "sp_gradient_fork_private_if_necessary: mesh not implemented" << std::endl;
}
return gr_new;
@@ -344,11 +354,19 @@ SPGradient *sp_gradient_reset_to_userspace(SPGradient *gr, SPItem *item)
gr->getRepr()->setAttribute("gradientTransform", c);
g_free(c);
}
- } else {
+ } else if (SP_IS_LINEARGRADIENT(gr)) {
sp_repr_set_svg_double(repr, "x1", (center - Geom::Point(width/2, 0))[Geom::X]);
sp_repr_set_svg_double(repr, "y1", (center - Geom::Point(width/2, 0))[Geom::Y]);
sp_repr_set_svg_double(repr, "x2", (center + Geom::Point(width/2, 0))[Geom::X]);
sp_repr_set_svg_double(repr, "y2", (center + Geom::Point(width/2, 0))[Geom::Y]);
+ } else {
+ // Mesh
+ // THIS IS BEING CALLED TWICE WHENEVER A NEW GRADIENT IS CREATED, WRITING HERE CAUSES PROBLEMS
+ // IN SPMeshNodeArray::create()
+ //sp_repr_set_svg_double(repr, "x", bbox->min()[Geom::X]);
+ //sp_repr_set_svg_double(repr, "y", bbox->min()[Geom::Y]);
+ SPMeshGradient* mg = SP_MESHGRADIENT( gr );
+ mg->array.create( mg, item, bbox );
}
// set the gradientUnits
@@ -633,6 +651,7 @@ void sp_item_gradient_edit_stop(SPItem *item, GrPointType point_type, guint poin
}
break;
default:
+ g_warning( "Unhandled gradient handle" );
break;
}
}
@@ -644,49 +663,78 @@ guint32 sp_item_gradient_stop_query_style(SPItem *item, GrPointType point_type,
if (!gradient || !SP_IS_GRADIENT(gradient))
return 0;
- SPGradient *vector = gradient->getVector();
+ if (SP_IS_LINEARGRADIENT(gradient) || SP_IS_RADIALGRADIENT(gradient) ) {
- if (!vector) // orphan!
- return 0; // what else to do?
+ SPGradient *vector = gradient->getVector();
- switch (point_type) {
- case POINT_LG_BEGIN:
- case POINT_RG_CENTER:
- case POINT_RG_FOCUS:
- {
- SPStop *first = vector->getFirstStop();
- if (first) {
- return sp_stop_get_rgba32(first);
+ if (!vector) // orphan!
+ return 0; // what else to do?
+
+ switch (point_type) {
+ case POINT_LG_BEGIN:
+ case POINT_RG_CENTER:
+ case POINT_RG_FOCUS:
+ {
+ SPStop *first = vector->getFirstStop();
+ if (first) {
+ return sp_stop_get_rgba32(first);
+ }
}
- }
- break;
+ break;
- case POINT_LG_END:
- case POINT_RG_R1:
- case POINT_RG_R2:
- {
- SPStop *last = sp_last_stop (vector);
- if (last) {
- return sp_stop_get_rgba32(last);
+ case POINT_LG_END:
+ case POINT_RG_R1:
+ case POINT_RG_R2:
+ {
+ SPStop *last = sp_last_stop (vector);
+ if (last) {
+ return sp_stop_get_rgba32(last);
+ }
}
- }
- break;
+ break;
- case POINT_LG_MID:
- case POINT_RG_MID1:
- case POINT_RG_MID2:
- {
- SPStop *stopi = sp_get_stop_i (vector, point_i);
- if (stopi) {
- return sp_stop_get_rgba32(stopi);
+ case POINT_LG_MID:
+ case POINT_RG_MID1:
+ case POINT_RG_MID2:
+ {
+ SPStop *stopi = sp_get_stop_i (vector, point_i);
+ if (stopi) {
+ return sp_stop_get_rgba32(stopi);
+ }
}
+ break;
+
+ default:
+ g_warning( "Bad linear/radial gradient handle type" );
+ break;
}
- break;
+ return 0;
+ } else {
- default:
- break;
+ // Mesh gradient
+ SPMeshGradient *mg = SP_MESHGRADIENT(gradient);
+
+ switch (point_type) {
+ case POINT_MG_CORNER: {
+
+ SPColor color = mg->array.corners[ point_i ]->color;
+ double opacity = mg->array.corners[ point_i ]->opacity;
+ return color.toRGBA32( opacity );
+ break;
+ }
+
+ case POINT_MG_HANDLE:
+ case POINT_MG_TENSOR:
+ {
+ // Do nothing. Handles and tensors don't have color
+ break;
+ }
+
+ default:
+ g_warning( "Bad mesh handle type" );
+ }
+ return 0;
}
- return 0;
}
void sp_item_gradient_stop_set_style(SPItem *item, GrPointType point_type, guint point_i, Inkscape::PaintTarget fill_or_stroke, SPCSSAttr *stop)
@@ -699,52 +747,102 @@ void sp_item_gradient_stop_set_style(SPItem *item, GrPointType point_type, guint
if (!gradient || !SP_IS_GRADIENT(gradient))
return;
- SPGradient *vector = gradient->getVector();
+ if (SP_IS_LINEARGRADIENT(gradient) || SP_IS_RADIALGRADIENT(gradient) ) {
- if (!vector) // orphan!
- return;
+ SPGradient *vector = gradient->getVector();
- vector = sp_gradient_fork_vector_if_necessary (vector);
- if ( gradient != vector && gradient->ref->getObject() != vector ) {
- sp_gradient_repr_set_link(gradient->getRepr(), vector);
- }
+ if (!vector) // orphan!
+ return;
- switch (point_type) {
- case POINT_LG_BEGIN:
- case POINT_RG_CENTER:
- case POINT_RG_FOCUS:
- {
- SPStop *first = vector->getFirstStop();
- if (first) {
- sp_repr_css_change(first->getRepr(), stop, "style");
- }
+ vector = sp_gradient_fork_vector_if_necessary (vector);
+ if ( gradient != vector && gradient->ref->getObject() != vector ) {
+ sp_gradient_repr_set_link(gradient->getRepr(), vector);
}
- break;
- case POINT_LG_END:
- case POINT_RG_R1:
- case POINT_RG_R2:
- {
- SPStop *last = sp_last_stop (vector);
- if (last) {
- sp_repr_css_change(last->getRepr(), stop, "style");
+ switch (point_type) {
+ case POINT_LG_BEGIN:
+ case POINT_RG_CENTER:
+ case POINT_RG_FOCUS:
+ {
+ SPStop *first = vector->getFirstStop();
+ if (first) {
+ sp_repr_css_change(first->getRepr(), stop, "style");
+ }
}
- }
- break;
+ break;
- case POINT_LG_MID:
- case POINT_RG_MID1:
- case POINT_RG_MID2:
- {
- SPStop *stopi = sp_get_stop_i (vector, point_i);
- if (stopi) {
- sp_repr_css_change(stopi->getRepr(), stop, "style");
+ case POINT_LG_END:
+ case POINT_RG_R1:
+ case POINT_RG_R2:
+ {
+ SPStop *last = sp_last_stop (vector);
+ if (last) {
+ sp_repr_css_change(last->getRepr(), stop, "style");
+ }
}
- }
- break;
+ break;
- default:
+ case POINT_LG_MID:
+ case POINT_RG_MID1:
+ case POINT_RG_MID2:
+ {
+ SPStop *stopi = sp_get_stop_i (vector, point_i);
+ if (stopi) {
+ sp_repr_css_change(stopi->getRepr(), stop, "style");
+ }
+ }
break;
+
+ default:
+ g_warning( "Bad linear/radial gradient handle type" );
+ break;
+ }
+ } else {
+
+ // Mesh gradient
+ SPMeshGradient *mg = SP_MESHGRADIENT(gradient);
+
+ bool changed = false;
+ switch (point_type) {
+ case POINT_MG_CORNER: {
+
+ gchar const* color_str = sp_repr_css_property( stop, "stop-color", NULL );
+ if( color_str ) {
+ SPColor color( 0 );
+ SPStyle* style = sp_style_new(0);
+ SPIPaint paint;
+ paint.read( color_str, *style );
+ if( paint.isColor() ) {
+ color = paint.value.color;
+ }
+ mg->array.corners[ point_i ]->color = color;
+ changed = true;
+ }
+ gchar const* opacity_str = sp_repr_css_property( stop, "stop-opacity", NULL );
+ if( opacity_str ) {
+ std::stringstream os( opacity_str );
+ double opacity = 1.0;
+ os >> opacity;
+ mg->array.corners[ point_i ]->opacity = opacity;
+ changed = true;
+ }
+ if( changed ) {
+ gradient->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ mg->array.write( mg );
+ }
+ break;
+ }
+
+ case POINT_MG_HANDLE:
+ case POINT_MG_TENSOR:
+ {
+ // Do nothing. Handles and tensors don't have colors.
+ break;
+ }
+
+ default:
+ g_warning( "Bad mesh handle type" );
+ }
}
}
@@ -820,6 +918,7 @@ void sp_item_gradient_set_coords(SPItem *item, GrPointType point_type, guint poi
if (!gradient || !SP_IS_GRADIENT(gradient))
return;
+ // Needed only if units are set to SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX
gradient = sp_gradient_convert_to_userspace(gradient, item, (fill_or_stroke == Inkscape::FOR_FILL) ? "fill" : "stroke");
Geom::Affine i2d (item->i2dt_affine ());
@@ -887,6 +986,7 @@ void sp_item_gradient_set_coords(SPItem *item, GrPointType point_type, guint poi
}
break;
default:
+ g_warning( "Bad linear gradient handle type" );
break;
}
} else if (SP_IS_RADIALGRADIENT(gradient)) {
@@ -963,7 +1063,7 @@ void sp_item_gradient_set_coords(SPItem *item, GrPointType point_type, guint poi
break;
}
- case POINT_RG_MID1:
+ case POINT_RG_MID1:
{
Geom::Point start = Geom::Point (rg->cx.computed, rg->cy.computed);
Geom::Point end = Geom::Point (rg->cx.computed + rg->r.computed, rg->cy.computed);
@@ -980,7 +1080,8 @@ void sp_item_gradient_set_coords(SPItem *item, GrPointType point_type, guint poi
}
break;
}
- case POINT_RG_MID2:
+ case POINT_RG_MID2:
+ {
Geom::Point start = Geom::Point (rg->cx.computed, rg->cy.computed);
Geom::Point end = Geom::Point (rg->cx.computed, rg->cy.computed - rg->r.computed);
double offset = Geom::LineSegment(start, end).nearestPoint(p);
@@ -995,6 +1096,10 @@ void sp_item_gradient_set_coords(SPItem *item, GrPointType point_type, guint poi
stopi->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
}
break;
+ }
+ default:
+ g_warning( "Bad radial gradient handle type" );
+ break;
}
if (transform_set) {
@@ -1008,7 +1113,41 @@ void sp_item_gradient_set_coords(SPItem *item, GrPointType point_type, guint poi
gradient->requestModified(SP_OBJECT_MODIFIED_FLAG);
}
}
+ } else if (SP_IS_MESHGRADIENT(gradient)) {
+ SPMeshGradient *mg = SP_MESHGRADIENT(gradient);
+ //Geom::Affine new_transform;
+ //bool transform_set = false;
+
+ switch (point_type) {
+ case POINT_MG_CORNER:
+ {
+ mg->array.corners[ point_i ]->p = p;
+ // Handles are moved in gradient-drag.cpp
+ gradient->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ break;
+ }
+
+ case POINT_MG_HANDLE: {
+ mg->array.handles[ point_i ]->p = p;
+ gradient->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ break;
+ }
+
+ case POINT_MG_TENSOR: {
+ mg->array.tensors[ point_i ]->p = p;
+ gradient->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ break;
+ }
+
+ default:
+ g_warning( "Bad mesh handle type" );
+ }
+ if( write_repr ) {
+ //std::cout << "Write mesh repr" << std::endl;
+ sp_meshgradient_repr_write( mg );
+ }
}
+
}
SPGradient *sp_item_gradient_get_vector(SPItem *item, Inkscape::PaintTarget fill_or_stroke)
@@ -1033,6 +1172,10 @@ SPGradientSpread sp_item_gradient_get_spread(SPItem *item, Inkscape::PaintTarget
}
+/**
+Returns the position of point point_type of the gradient applied to item (either fill_or_stroke),
+in desktop coordinates.
+*/
Geom::Point getGradientCoords(SPItem *item, GrPointType point_type, guint point_i, Inkscape::PaintTarget fill_or_stroke)
{
#ifdef SP_GR_VERBOSE
@@ -1060,6 +1203,9 @@ Geom::Point getGradientCoords(SPItem *item, GrPointType point_type, guint point_
p = (1-offset) * Geom::Point(lg->x1.computed, lg->y1.computed) + offset * Geom::Point(lg->x2.computed, lg->y2.computed);
}
break;
+ default:
+ g_warning( "Bad linear gradient handle type" );
+ break;
}
} else if (SP_IS_RADIALGRADIENT(gradient)) {
SPRadialGradient *rg = SP_RADIALGRADIENT(gradient);
@@ -1088,9 +1234,34 @@ Geom::Point getGradientCoords(SPItem *item, GrPointType point_type, guint point_
p = (1-offset) * Geom::Point (rg->cx.computed, rg->cy.computed) + offset * Geom::Point(rg->cx.computed, rg->cy.computed - rg->r.computed);
}
break;
+ default:
+ g_warning( "Bad radial gradient handle type" );
+ break;
+ }
+ } else if (SP_IS_MESHGRADIENT(gradient)) {
+ SPMeshGradient *mg = SP_MESHGRADIENT(gradient);
+ switch (point_type) {
+
+ case POINT_MG_CORNER:
+ p = mg->array.corners[ point_i ]->p;
+ break;
+
+ case POINT_MG_HANDLE: {
+ p = mg->array.handles[ point_i ]->p;
+ break;
+ }
+
+ case POINT_MG_TENSOR: {
+ p = mg->array.tensors[ point_i ]->p;
+ break;
+ }
+
+ default:
+ g_warning( "Bad mesh handle type" );
}
}
+
if (SP_GRADIENT(gradient)->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
item->document->ensureUpToDate();
Geom::OptRect bbox = item->visualBounds(); // we need "true" bbox without item_i2d_affine
@@ -1104,6 +1275,11 @@ Geom::Point getGradientCoords(SPItem *item, GrPointType point_type, guint point_
return p;
}
+/**
+ * Sets item fill or stroke to the gradient of the specified type with given vector, creating
+ * new private gradient, if needed.
+ * gr has to be a normalized vector.
+ */
SPGradient *sp_item_set_gradient(SPItem *item, SPGradient *gr, SPGradientType type, Inkscape::PaintTarget fill_or_stroke)
{
@@ -1168,6 +1344,8 @@ SPGradient *sp_item_set_gradient(SPItem *item, SPGradient *gr, SPGradientType ty
} else {
/* Current fill style is not a gradient or wrong type, so construct everything */
+ /* This is where mesh gradients are constructed. */
+ g_assert(SP_IS_GRADIENT(gr)); // TEMP
SPGradient *constructed = sp_gradient_get_private_normalized(item->document, gr, type);
constructed = sp_gradient_reset_to_userspace(constructed, item);
sp_style_set_property_url(item, ( (fill_or_stroke == Inkscape::FOR_FILL) ? "fill" : "stroke" ), constructed, true);
diff --git a/src/gradient-drag.cpp b/src/gradient-drag.cpp
index 6ea430585..64d82c87d 100644
--- a/src/gradient-drag.cpp
+++ b/src/gradient-drag.cpp
@@ -6,6 +6,7 @@
* Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
* Jon A. Cruz <jon@joncruz.org>
* Abhishek Sharma
+ * Tavmjong Bah <tavmjong@free.fr>
*
* Copyright (C) 2007 Johan Engelen
* Copyright (C) 2005,2010 Authors
@@ -20,7 +21,7 @@
#include <glibmm/i18n.h>
#include <cstring>
#include <string>
-#include <2geom/bezier-curve.h>
+//#include <2geom/bezier-curve.h>
#include "desktop-handles.h"
#include "selection.h"
@@ -30,6 +31,7 @@
#include "document.h"
#include "document-undo.h"
#include "display/sp-ctrlline.h"
+#include "display/sp-ctrlcurve.h"
#include "display/sp-canvas-util.h"
#include "xml/repr.h"
#include "svg/css-ostringstream.h"
@@ -40,6 +42,9 @@
#include "knot.h"
#include "sp-linear-gradient.h"
#include "sp-radial-gradient.h"
+#include "sp-mesh-gradient.h"
+#include "sp-mesh-row.h"
+#include "sp-mesh-patch.h"
#include "gradient-chemistry.h"
#include "gradient-drag.h"
#include "sp-stop.h"
@@ -61,23 +66,29 @@ using Inkscape::CTLINE_SECONDARY;
#define GR_KNOT_COLOR_MOUSEOVER 0xff000000
#define GR_KNOT_COLOR_SELECTED 0x0000ff00
+#define GR_LINE_COLOR_FILL 0x0000ff7f
+#define GR_LINE_COLOR_STROKE 0x9999007f
+
// screen pixels between knots when they snap:
#define SNAP_DIST 5
// absolute distance between gradient points for them to become a single dragger when the drag is created:
#define MERGE_DIST 0.1
-// knot shapes corresponding to GrPointType enum
+// knot shapes corresponding to GrPointType enum (in sp-gradient.h)
SPKnotShapeType gr_knot_shapes [] = {
- SP_KNOT_SHAPE_SQUARE, //POINT_LG_BEGIN
- SP_KNOT_SHAPE_CIRCLE, //POINT_LG_END
- SP_KNOT_SHAPE_DIAMOND, //POINT_LG_MID
+ SP_KNOT_SHAPE_SQUARE, // POINT_LG_BEGIN
+ SP_KNOT_SHAPE_CIRCLE, // POINT_LG_END
+ SP_KNOT_SHAPE_DIAMOND, // POINT_LG_MID
SP_KNOT_SHAPE_SQUARE, // POINT_RG_CENTER
SP_KNOT_SHAPE_CIRCLE, // POINT_RG_R1
SP_KNOT_SHAPE_CIRCLE, // POINT_RG_R2
- SP_KNOT_SHAPE_CROSS, // POINT_RG_FOCUS
- SP_KNOT_SHAPE_DIAMOND, //POINT_RG_MID1
- SP_KNOT_SHAPE_DIAMOND //POINT_RG_MID2
+ SP_KNOT_SHAPE_CROSS, // POINT_RG_FOCUS
+ SP_KNOT_SHAPE_DIAMOND, // POINT_RG_MID1
+ SP_KNOT_SHAPE_DIAMOND, // POINT_RG_MID2
+ SP_KNOT_SHAPE_DIAMOND, // POINT_MG_CORNER
+ SP_KNOT_SHAPE_CIRCLE, // POINT_MG_HANDLE
+ SP_KNOT_SHAPE_SQUARE // POINT_MG_TENSOR
};
const gchar *gr_knot_descr [] = {
@@ -89,7 +100,10 @@ const gchar *gr_knot_descr [] = {
N_("Radial gradient <b>radius</b>"),
N_("Radial gradient <b>focus</b>"), // POINT_RG_FOCUS
N_("Radial gradient <b>mid stop</b>"),
- N_("Radial gradient <b>mid stop</b>")
+ N_("Radial gradient <b>mid stop</b>"),
+ N_("Mesh gradient <b>corner</b>"),
+ N_("Mesh gradient <b>handle</b>"),
+ N_("Mesh gradient <b>tensor</b>")
};
static void
@@ -347,6 +361,11 @@ SPStop *GrDrag::addStopNearPoint(SPItem *item, Geom::Point mouse_p, double toler
SPGradient *gradient = 0;
//bool r1_knot = false;
+ // For Mesh
+ int divide_row = -1;
+ int divide_column = -1;
+ double divide_coord = 0.5;
+
bool addknot = false;
for (std::vector<Inkscape::PaintTarget>::const_iterator it = allPaintTargets().begin(); (it != allPaintTargets().end()) && !addknot; ++it)
@@ -393,36 +412,159 @@ SPStop *GrDrag::addStopNearPoint(SPItem *item, Geom::Point mouse_p, double toler
//r1_knot = false;
}
}
- }
+ } else if (SP_IS_MESHGRADIENT(gradient)) {
+
+ // add_stop_near_point()
+ // Find out which curve pointer is over and use that curve to determine
+ // which row or column will be divided.
+ // This is silly as we already should know which line we are over...
+ // but that information is not saved (sp_gradient_context_is_over_line).
+
+ SPMeshGradient *mg = SP_MESHGRADIENT(gradient);
+ Geom::Affine transform = Geom::Affine(mg->gradientTransform)*(Geom::Affine)item->i2dt_affine();
+
+ uint rows = mg->array.patch_rows();
+ uint columns = mg->array.patch_columns();
+
+ double closest = 1e10;
+ for( uint i = 0; i < rows; ++i ) {
+ for( uint j = 0; j < columns; ++j ) {
+
+ SPMeshPatchI patch( &(mg->array.nodes), i, j );
+ Geom::Point p[4];
+
+ // Top line
+ {
+ p[0] = patch.getPoint( 0, 0 ) * transform;
+ p[1] = patch.getPoint( 0, 1 ) * transform;
+ p[2] = patch.getPoint( 0, 2 ) * transform;
+ p[3] = patch.getPoint( 0, 3 ) * transform;
+ Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] );
+ Geom::Coord coord = b.nearestPoint( mouse_p );
+ Geom::Point nearest = b( coord );
+ double dist_screen = Geom::L2 ( mouse_p - nearest );
+ if ( dist_screen < closest ) {
+ closest = dist_screen;
+ divide_row = -1;
+ divide_column = j;
+ divide_coord = coord;
+ }
+ }
+
+ // Right line (only for last column)
+ if( j == columns - 1 ) {
+ p[0] = patch.getPoint( 1, 0 ) * transform;
+ p[1] = patch.getPoint( 1, 1 ) * transform;
+ p[2] = patch.getPoint( 1, 2 ) * transform;
+ p[3] = patch.getPoint( 1, 3 ) * transform;
+ Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] );
+ Geom::Coord coord = b.nearestPoint( mouse_p );
+ Geom::Point nearest = b( coord );
+ double dist_screen = Geom::L2 ( mouse_p - nearest );
+ if ( dist_screen < closest ) {
+ closest = dist_screen;
+ divide_row = i;
+ divide_column = -1;
+ divide_coord = coord;
+ }
+ }
+
+ // Bottom line (only for last row)
+ if( i == rows - 1 ) {
+ p[0] = patch.getPoint( 2, 0 ) * transform;
+ p[1] = patch.getPoint( 2, 1 ) * transform;
+ p[2] = patch.getPoint( 2, 2 ) * transform;
+ p[3] = patch.getPoint( 2, 3 ) * transform;
+ Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] );
+ Geom::Coord coord = b.nearestPoint( mouse_p );
+ Geom::Point nearest = b( coord );
+ double dist_screen = Geom::L2 ( mouse_p - nearest );
+ if ( dist_screen < closest ) {
+ closest = dist_screen;
+ divide_row = -1;
+ divide_column = j;
+ divide_coord = 1.0 - coord;
+ }
+ }
+
+ // Left line
+ {
+ p[0] = patch.getPoint( 3, 0 ) * transform;
+ p[1] = patch.getPoint( 3, 1 ) * transform;
+ p[2] = patch.getPoint( 3, 2 ) * transform;
+ p[3] = patch.getPoint( 3, 3 ) * transform;
+ Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] );
+ Geom::Coord coord = b.nearestPoint( mouse_p );
+ Geom::Point nearest = b( coord );
+ double dist_screen = Geom::L2 ( mouse_p - nearest );
+ if ( dist_screen < closest ) {
+ closest = dist_screen;
+ divide_row = i;
+ divide_column = -1;
+ divide_coord = 1.0 - coord;
+ }
+ }
+
+ } // End loop over columns
+ } // End loop rows
+
+ if( closest < tolerance ) {
+ addknot = true;
+ }
+
+ } // End if mesh
+
}
if (addknot) {
- SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (gradient, false);
- SPStop* prev_stop = vector->getFirstStop();
- SPStop* next_stop = prev_stop->getNextStop();
- guint i = 1;
- while ( (next_stop) && (next_stop->offset < new_stop_offset) ) {
- prev_stop = next_stop;
- next_stop = next_stop->getNextStop();
- i++;
- }
- if (!next_stop) {
- // logical error: the endstop should have offset 1 and should always be more than this offset here
- return NULL;
- }
+ if( SP_IS_LINEARGRADIENT(gradient) || SP_IS_RADIALGRADIENT( gradient ) ) {
+ SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (gradient, false);
+ SPStop* prev_stop = vector->getFirstStop();
+ SPStop* next_stop = prev_stop->getNextStop();
+ guint i = 1;
+ while ( (next_stop) && (next_stop->offset < new_stop_offset) ) {
+ prev_stop = next_stop;
+ next_stop = next_stop->getNextStop();
+ i++;
+ }
+ if (!next_stop) {
+ // logical error: the endstop should have offset 1 and should always be more than this offset here
+ return NULL;
+ }
+
+
+ SPStop *newstop = sp_vector_add_stop (vector, prev_stop, next_stop, new_stop_offset);
+ gradient->ensureVector();
+ updateDraggers();
+
+ // so that it does not automatically update draggers in idle loop, as this would deselect
+ local_change = true;
+
+ // select the newly created stop
+ selectByStop(newstop);
+
+ return newstop;
- SPStop *newstop = sp_vector_add_stop (vector, prev_stop, next_stop, new_stop_offset);
- gradient->ensureVector();
- updateDraggers();
+ } else {
+
+ SPMeshGradient *mg = SP_MESHGRADIENT(gradient);
- // so that it does not automatically update draggers in idle loop, as this would deselect
- local_change = true;
+ if( divide_row > -1 ) {
+ mg->array.split_row( divide_row, divide_coord );
+ } else {
+ mg->array.split_column( divide_column, divide_coord );
+ }
- // select the newly created stop
- selectByStop(newstop);
+ // Update repr
+ sp_meshgradient_repr_write( mg );
+ mg->array.built = false;
+ mg->ensureArray();
+ // How do we do this?
+ DocumentUndo::done(sp_desktop_document (desktop), SP_VERB_CONTEXT_MESH,
+ _("Added patch row or column"));
- return newstop;
+ } // Mesh
}
return NULL;
@@ -703,6 +845,10 @@ static void gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, gui
} else if (draggable->point_type == POINT_RG_CENTER) {
// radial center snaps to hor/vert relative to its original position
dr_snap = dragger->point_original;
+ } else if (draggable->point_type == POINT_MG_CORNER ||
+ draggable->point_type == POINT_MG_HANDLE ||
+ draggable->point_type == POINT_MG_TENSOR ) {
+ // std::cout << " gr_knot_moved_handler: Got mesh point!" << std::endl;
}
// dr_snap contains the origin of the gradient, whereas p will be the new endpoint which we will try to snap now
@@ -746,11 +892,12 @@ static void gr_knot_moved_handler(SPKnot *knot, Geom::Point const &ppointer, gui
Geom::Point diff = p - dragger->point;
drag->selected_move_nowrite (diff[Geom::X], diff[Geom::Y], scale_radial);
} else {
+ Geom::Point p_old = dragger->point;
dragger->point = p;
dragger->fireDraggables (false, scale_radial);
dragger->updateDependencies(false);
+ dragger->updateHandles( p_old, MG_NODE_NO_SCALE );
}
-
}
@@ -928,6 +1075,7 @@ static void gr_knot_ungrabbed_handler(SPKnot *knot, unsigned int state, gpointer
} else {
dragger->fireDraggables (true);
}
+ dragger->updateHandles( dragger->point_original, MG_NODE_NO_SCALE );
for (GList *i = dragger->parent->selected; i != NULL; i = i->next) {
GrDragger *d = (GrDragger *) i->data;
@@ -966,34 +1114,41 @@ static void gr_knot_clicked_handler(SPKnot */*knot*/, guint state, gpointer data
if (gradient->vector.stops.size() > 2) { // 2 is the minimum
SPStop *stop = NULL;
switch (draggable->point_type) { // if we delete first or last stop, move the next/previous to the edge
- case POINT_LG_BEGIN:
- case POINT_RG_CENTER:
- stop = gradient->getFirstStop();
- {
- SPStop *next = stop->getNextStop();
- if (next) {
- next->offset = 0;
- sp_repr_set_css_double(next->getRepr(), "offset", 0);
+
+ case POINT_LG_BEGIN:
+ case POINT_RG_CENTER:
+ stop = gradient->getFirstStop();
+ {
+ SPStop *next = stop->getNextStop();
+ if (next) {
+ next->offset = 0;
+ sp_repr_set_css_double(next->getRepr(), "offset", 0);
+ }
}
- }
- break;
- case POINT_LG_END:
- case POINT_RG_R1:
- case POINT_RG_R2:
- stop = sp_last_stop(gradient);
- {
- SPStop *prev = stop->getPrevStop();
- if (prev) {
- prev->offset = 1;
- sp_repr_set_css_double(prev->getRepr(), "offset", 1);
+ break;
+
+ case POINT_LG_END:
+ case POINT_RG_R1:
+ case POINT_RG_R2:
+ stop = sp_last_stop(gradient);
+ {
+ SPStop *prev = stop->getPrevStop();
+ if (prev) {
+ prev->offset = 1;
+ sp_repr_set_css_double(prev->getRepr(), "offset", 1);
+ }
}
- }
- break;
- case POINT_LG_MID:
- case POINT_RG_MID1:
- case POINT_RG_MID2:
- stop = sp_get_stop_i(gradient, draggable->point_i);
- break;
+ break;
+
+ case POINT_LG_MID:
+ case POINT_RG_MID1:
+ case POINT_RG_MID2:
+ stop = sp_get_stop_i(gradient, draggable->point_i);
+ break;
+
+ default:
+ break;
+
}
gradient->getRepr()->removeChild(stop->getRepr());
@@ -1002,6 +1157,7 @@ static void gr_knot_clicked_handler(SPKnot */*knot*/, guint state, gpointer data
}
} else {
// select the dragger
+
dragger->point_original = dragger->point;
if ( state & GDK_SHIFT_MASK ) {
@@ -1140,6 +1296,110 @@ bool GrDragger::mayMerge(GrDraggable *da2)
}
/**
+ * Update mesh handles when mesh corner is moved.
+ * pc_old: old position of corner (could be changed to dp if we figure out transforms).
+ * op: how other nodes (handles, tensors) should be moved.
+ * Scaling takes place only between a selected and an unselected corner,
+ * other wise a handle is displaced the same distance as the adjacent corner.
+ * If a side is a line, then the handles are always placed 1/3 of side length
+ * from each corner.
+ *
+ * Ooops, needs to be reimplemented.
+ */
+void
+GrDragger::updateHandles ( Geom::Point pc_old, MeshNodeOperation op )
+{
+
+ // This routine might more properly be in mesh-context.cpp but moving knots is
+ // handled here rather than there.
+
+ // We need to update two places:
+ // 1. In SPMeshArrayI with object coordinates
+ // 2. In Drager/Knots with desktop coordinates.
+
+ // This routine is more complicated than it might need to be inorder to allow
+ // corner points to be selected in multiple meshes at the same time... with some
+ // sharing the same dragger (overkill, perhaps?).
+
+ // If no corner point in GrDragger then do nothing.
+ if( !isA (POINT_MG_CORNER ) ) return;
+
+ GrDrag *drag = this->parent;
+
+ // We need a list of selected corners per mesh if scaling.
+ std::map<SPGradient*, std::vector<uint> > selected_corners;
+ bool scale = false;
+ if( scale == true ) {
+
+ for ( GList *i = drag->selected; i != NULL; i = i->next ) {
+ GrDragger *dragger = (GrDragger *) i->data;
+ for ( GSList *j = dragger->draggables; j != NULL; j = j->next ) {
+ GrDraggable *draggable = (GrDraggable *) j->data;
+
+ // Check draggable is of type POINT_MG_CORNER (don't allow selection of POINT_MG_HANDLE)
+ if( draggable->point_type != POINT_MG_CORNER ) continue;
+
+ // Must be a mesh gradient
+ SPGradient *gradient = getGradient(draggable->item, draggable->fill_or_stroke);
+ if ( !SP_IS_MESHGRADIENT( gradient ) ) continue;
+
+ selected_corners[ gradient ].push_back( draggable->point_i );
+ }
+ }
+ }
+
+ // Now we do the handle moves.
+
+ // Loop over all draggables in moved corner
+ std::map<SPGradient*, std::vector<uint> > dragger_corners;
+ for ( GSList *j = draggables; j != NULL; j = j->next ) {
+ GrDraggable *draggable = (GrDraggable *) j->data;
+
+ SPItem *item = draggable->item;
+ gint point_type = draggable->point_type;
+ gint point_i = draggable->point_i;
+ Inkscape::PaintTarget
+ fill_or_stroke = draggable->fill_or_stroke;
+
+ // Check draggable is of type POINT_MG_CORNER (don't allow selection of POINT_MG_HANDLE)
+ if( point_type != POINT_MG_CORNER ) continue;
+
+ // Must be a mesh gradient
+ SPGradient *gradient = getGradient(item, fill_or_stroke);
+ if ( !SP_IS_MESHGRADIENT( gradient ) ) continue;
+ SPMeshGradient *mg = SP_MESHGRADIENT( gradient );
+
+ // pc_old is the old corner position in desktop coordinates, we need it in gradient coordinate.
+ gradient = sp_gradient_convert_to_userspace (gradient, item, (fill_or_stroke == Inkscape::FOR_FILL) ? "fill" : "stroke");
+ Geom::Affine i2d ( item->i2dt_affine() );
+ Geom::Point pcg_old = pc_old * i2d.inverse();
+ pcg_old *= (gradient->gradientTransform).inverse();
+
+ mg->array.update_handles( point_i, selected_corners[ gradient ], pcg_old, op );
+
+ // Move on-screen knots
+ for( uint i = 0; i < mg->array.handles.size(); ++i ) {
+ GrDragger *handle = drag->getDraggerFor( item, POINT_MG_HANDLE, i, fill_or_stroke );
+ SPKnot *knot = handle->knot;
+ Geom::Point pk = getGradientCoords( item, POINT_MG_HANDLE, i, fill_or_stroke );
+ sp_knot_moveto( knot, pk );
+
+ }
+
+ for( uint i = 0; i < mg->array.tensors.size(); ++i ) {
+
+ GrDragger *handle = drag->getDraggerFor( item, POINT_MG_TENSOR, i, fill_or_stroke );
+ SPKnot *knot = handle->knot;
+ Geom::Point pk = getGradientCoords( item, POINT_MG_TENSOR, i, fill_or_stroke );
+ sp_knot_moveto( knot, pk );
+
+ }
+
+ } // Loop over draggables.
+}
+
+
+/**
* Updates the statusbar tip of the dragger knot, based on its draggables.
*/
void GrDragger::updateTip()
@@ -1225,6 +1485,7 @@ void GrDragger::moveThisToDraggable(SPItem *item, GrPointType point_type, gint p
(point_type == -1 || da->point_type == point_type) &&
(point_i == -1 || da->point_i == point_i) &&
(da->fill_or_stroke == fill_or_stroke) ) {
+ // Don't move initial draggable
continue;
}
sp_item_gradient_set_coords(da->item, da->point_type, da->point_i, this->point, da->fill_or_stroke, write_repr, false);
@@ -1361,7 +1622,10 @@ GrDragger::GrDragger(GrDrag *parent, Geom::Point p, GrDraggable *draggable)
GrDragger::~GrDragger()
{
// unselect if it was selected
- this->parent->setDeselected(this);
+ // Hmm, this causes a race condition as it triggers a call to gradient_selection_changed which
+ // can be executed while a list of draggers is being deleted. It doesn't acutally seem to be
+ // necessary.
+ //this->parent->setDeselected(this);
// disconnect signals
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (gr_knot_moved_handler), this);
@@ -1418,6 +1682,13 @@ void GrDragger::select()
{
this->knot->fill [SP_KNOT_STATE_NORMAL] = GR_KNOT_COLOR_SELECTED;
g_object_set (G_OBJECT (this->knot->item), "fill_color", GR_KNOT_COLOR_SELECTED, NULL);
+ //if( isA(POINT_MG_CORNER) ) {
+ // for (GSList * drgble = this->draggables; drgble != NULL; drgble = drgble->next) {
+ // GrDraggable *draggable = (GrDraggable*) drgble->data;
+ // //if( draggable != NULL ) std::cout << " draggable" << std::endl;
+ // // MESH FIXME: TURN ON CORRESPONDING SIDE/TENSOR NODE VISIBILITY
+ // }
+ //}
}
/**
@@ -1427,6 +1698,7 @@ void GrDragger::deselect()
{
this->knot->fill [SP_KNOT_STATE_NORMAL] = GR_KNOT_COLOR_NORMAL;
g_object_set (G_OBJECT (this->knot->item), "fill_color", GR_KNOT_COLOR_NORMAL, NULL);
+ // MESH FIXME: TURN OFF CORRESPONDING SIDE/TENSOR NODE VISIBILITY
}
bool
@@ -1525,6 +1797,10 @@ void GrDrag::setSelected(GrDragger *dragger, bool add_to_selection, bool overrid
{
GrDragger *seldragger = NULL;
+ // Don't allow selecting a mesh handle or mesh tensor.
+ // We might want to rethink since a dragger can have draggables of different types.
+ if ( dragger->isA( POINT_MG_HANDLE ) || dragger->isA( POINT_MG_TENSOR ) ) return;
+
if (add_to_selection) {
if (!dragger) return;
if (override) {
@@ -1588,6 +1864,23 @@ void GrDrag::addLine(SPItem *item, Geom::Point p1, Geom::Point p2, Inkscape::Pai
this->lines = g_slist_append(this->lines, line);
}
+
+
+/**
+ * Create a curve from p0 to p3 and add it to the lines list. Used for mesh sides.
+ */
+void GrDrag::addCurve(SPItem *item, Geom::Point p0, Geom::Point p1, Geom::Point p2, Geom::Point p3, Inkscape::PaintTarget fill_or_stroke)
+{
+ CtrlLineType type = (fill_or_stroke == Inkscape::FOR_FILL) ? CTLINE_PRIMARY : CTLINE_SECONDARY;
+ SPCtrlCurve *line = ControlManager::getManager().createControlCurve(sp_desktop_controls(this->desktop), p0, p1, p2, p3, type);
+
+ sp_canvas_item_move_to_z(line, 0);
+ line->item = item;
+ sp_canvas_item_show (line);
+ this->lines = g_slist_append (this->lines, line);
+}
+
+
/**
* If there already exists a dragger within MERGE_DIST of p, add the draggable to it; otherwise create
* new dragger and add it to draggers list.
@@ -1651,7 +1944,94 @@ void GrDrag::addDraggersLinear(SPLinearGradient *lg, SPItem *item, Inkscape::Pai
}
/**
+ *Add draggers for the mesh gradient mg on item
+ */
+void GrDrag::addDraggersMesh(SPMeshGradient *mg, SPItem *item, Inkscape::PaintTarget fill_or_stroke)
+{
+ std::vector< std::vector< SPMeshNode* > > nodes = mg->array.nodes;
+
+ // Show/hide mesh on fill/stroke. This doesn't work at the moment... and prevents node color updating.
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ bool edit_fill = true; //abs(prefs->getBool("/tools/mesh/edit_fill", true));
+ bool edit_stroke = true; //abs(prefs->getBool("/tools/mesh/edit_stroke", true));
+ bool show_handles = true; //abs(prefs->getBool("/tools/mesh/show_handles", true));
+
+ if( (fill_or_stroke == Inkscape::FOR_FILL && !edit_fill) ||
+ (fill_or_stroke == Inkscape::FOR_STROKE && !edit_stroke) ) {
+ return;
+ }
+
+ // Make sure we have at least one patch defined.
+ if( mg->array.patch_rows() == 0 || mg->array.patch_columns() == 0 ) {
+
+ std::cout << "Empty Mesh Gradient, No Draggers to Add" << std::endl;
+ return;
+ }
+
+ uint icorner = 0;
+ uint ihandle = 0;
+ uint itensor = 0;
+ mg->array.corners.clear();
+ mg->array.handles.clear();
+ mg->array.tensors.clear();
+
+
+ for( uint i = 0; i < nodes.size(); ++i ) {
+ for( uint j = 0; j < nodes[i].size(); ++j ) {
+
+ // std::cout << " Draggers: " << i << " " << j << " " << nodes[i][j]->node_type << std::endl;
+
+ if( nodes[i][j]->set ) {
+ switch ( nodes[i][j]->node_type ) {
+
+ case MG_NODE_TYPE_CORNER:
+ {
+ mg->array.corners.push_back( nodes[i][j] );
+ GrDraggable *corner = new GrDraggable (item, POINT_MG_CORNER, icorner, fill_or_stroke);
+ addDragger ( corner );
+ nodes[i][j]->draggable = icorner;
+ ++icorner;
+ break;
+ }
+
+ case MG_NODE_TYPE_HANDLE:
+ {
+ if( show_handles ) {
+ mg->array.handles.push_back( nodes[i][j] );
+ GrDraggable *handle = new GrDraggable (item, POINT_MG_HANDLE, ihandle, fill_or_stroke);
+ addDragger ( handle );
+ nodes[i][j]->draggable = ihandle;
+ ++ihandle;
+ break;
+ }
+ }
+
+ case MG_NODE_TYPE_TENSOR:
+ {
+ if( show_handles ) {
+ mg->array.tensors.push_back( nodes[i][j] );
+ GrDraggable *tensor = new GrDraggable (item, POINT_MG_TENSOR, itensor, fill_or_stroke);
+ addDragger ( tensor );
+ nodes[i][j]->draggable = itensor;
+ ++itensor;
+ break;
+ }
+ }
+
+ default:
+ std::cout << "Bad Mesh Gradient draggable type" << std::endl;
+ break;
+ }
+ }
+ }
+ }
+
+ mg->array.drag_valid = true;
+}
+
+/**
* Artificially grab the knot of this dragger; used by the gradient context.
+ * Not used at the moment.
*/
void GrDrag::grabKnot(GrDragger *dragger, gint x, gint y, guint32 etime)
{
@@ -1662,6 +2042,7 @@ void GrDrag::grabKnot(GrDragger *dragger, gint x, gint y, guint32 etime)
/**
* Artificially grab the knot of the dragger with this draggable; used by the gradient context.
+ * This allows setting the final point from the end of the drag when creating a new gradient.
*/
void GrDrag::grabKnot(SPItem *item, GrPointType point_type, gint point_i, Inkscape::PaintTarget fill_or_stroke, gint x, gint y, guint32 etime)
{
@@ -1701,7 +2082,10 @@ void GrDrag::updateDraggers()
addDraggersLinear( SP_LINEARGRADIENT(server), item, Inkscape::FOR_FILL );
} else if ( SP_IS_RADIALGRADIENT(server) ) {
addDraggersRadial( SP_RADIALGRADIENT(server), item, Inkscape::FOR_FILL );
+ } else if ( SP_IS_MESHGRADIENT(server) ) {
+ addDraggersMesh( SP_MESHGRADIENT(server), item, Inkscape::FOR_FILL );
}
+
}
if (style && (style->stroke.isPaintserver())) {
@@ -1712,6 +2096,8 @@ void GrDrag::updateDraggers()
addDraggersLinear( SP_LINEARGRADIENT(server), item, Inkscape::FOR_STROKE );
} else if ( SP_IS_RADIALGRADIENT(server) ) {
addDraggersRadial( SP_RADIALGRADIENT(server), item, Inkscape::FOR_STROKE );
+ } else if ( SP_IS_MESHGRADIENT(server) ) {
+ addDraggersMesh( SP_MESHGRADIENT(server), item, Inkscape::FOR_STROKE );
}
}
}
@@ -1763,6 +2149,52 @@ void GrDrag::updateLines()
Geom::Point center = getGradientCoords(item, POINT_RG_CENTER, 0, Inkscape::FOR_FILL);
addLine(item, center, getGradientCoords(item, POINT_RG_R1, 0, Inkscape::FOR_FILL), Inkscape::FOR_FILL);
addLine(item, center, getGradientCoords(item, POINT_RG_R2, 0, Inkscape::FOR_FILL), Inkscape::FOR_FILL);
+ } else if ( SP_IS_MESHGRADIENT(server) ) {
+
+ SPMeshGradient *mg = SP_MESHGRADIENT(server);
+
+ uint rows = mg->array.patch_rows();
+ uint columns = mg->array.patch_columns();
+ for ( uint i = 0; i < rows; ++i ) {
+ for ( uint j = 0; j < columns; ++j ) {
+
+ std::vector<Geom::Point> h;
+
+ SPMeshPatchI patch( &(mg->array.nodes), i, j );
+
+ // Top line
+ h = patch.getPointsForSide( 0 );
+ for( uint p = 0; p < 4; ++p ) {
+ h[p] *= Geom::Affine(mg->gradientTransform) * (Geom::Affine)item->i2dt_affine();
+ }
+ addCurve (item, h[0], h[1], h[2], h[3], Inkscape::FOR_FILL );
+
+ // Right line
+ if( j == columns - 1 ) {
+ h = patch.getPointsForSide( 1 );
+ for( uint p = 0; p < 4; ++p ) {
+ h[p] *= Geom::Affine(mg->gradientTransform) * (Geom::Affine)item->i2dt_affine();
+ }
+ addCurve (item, h[0], h[1], h[2], h[3], Inkscape::FOR_FILL );
+ }
+
+ // Bottom line
+ if( i == rows - 1 ) {
+ h = patch.getPointsForSide( 2 );
+ for( uint p = 0; p < 4; ++p ) {
+ h[p] *= Geom::Affine(mg->gradientTransform) * (Geom::Affine)item->i2dt_affine();
+ }
+ addCurve (item, h[0], h[1], h[2], h[3], Inkscape::FOR_FILL );
+ }
+
+ // Left line
+ h = patch.getPointsForSide( 3 );
+ for( uint p = 0; p < 4; ++p ) {
+ h[p] *= Geom::Affine(mg->gradientTransform) * (Geom::Affine)item->i2dt_affine();
+ }
+ addCurve (item, h[0], h[1], h[2], h[3], Inkscape::FOR_FILL );
+ }
+ }
}
}
@@ -1776,6 +2208,53 @@ void GrDrag::updateLines()
Geom::Point center = getGradientCoords(item, POINT_RG_CENTER, 0, Inkscape::FOR_STROKE);
addLine(item, center, getGradientCoords(item, POINT_RG_R1, 0, Inkscape::FOR_STROKE), Inkscape::FOR_STROKE);
addLine(item, center, getGradientCoords(item, POINT_RG_R2, 0, Inkscape::FOR_STROKE), Inkscape::FOR_STROKE);
+ } else if ( SP_IS_MESHGRADIENT(server) ) {
+
+ // MESH FIXME: TURN ROUTINE INTO FUNCTION AND CALL FOR BOTH FILL AND STROKE.
+ SPMeshGradient *mg = SP_MESHGRADIENT(server);
+
+ uint rows = mg->array.patch_rows();
+ uint columns = mg->array.patch_columns();
+ for ( uint i = 0; i < rows; ++i ) {
+ for ( uint j = 0; j < columns; ++j ) {
+
+ std::vector<Geom::Point> h;
+
+ SPMeshPatchI patch( &(mg->array.nodes), i, j );
+
+ // Top line
+ h = patch.getPointsForSide( 0 );
+ for( uint p = 0; p < 4; ++p ) {
+ h[p] *= Geom::Affine(mg->gradientTransform) * (Geom::Affine)item->i2dt_affine();
+ }
+ addCurve (item, h[0], h[1], h[2], h[3], Inkscape::FOR_STROKE );
+
+ // Right line
+ if( j == columns - 1 ) {
+ h = patch.getPointsForSide( 1 );
+ for( uint p = 0; p < 4; ++p ) {
+ h[p] *= Geom::Affine(mg->gradientTransform) * (Geom::Affine)item->i2dt_affine();
+ }
+ addCurve (item, h[0], h[1], h[2], h[3], Inkscape::FOR_STROKE );
+ }
+
+ // Bottom line
+ if( i == rows - 1 ) {
+ h = patch.getPointsForSide( 2 );
+ for( uint p = 0; p < 4; ++p ) {
+ h[p] *= Geom::Affine(mg->gradientTransform) * (Geom::Affine)item->i2dt_affine();
+ }
+ addCurve (item, h[0], h[1], h[2], h[3], Inkscape::FOR_STROKE );
+ }
+
+ // Left line
+ h = patch.getPointsForSide( 3 );
+ for( uint p = 0; p < 4; ++p ) {
+ h[p] *= Geom::Affine(mg->gradientTransform) * (Geom::Affine)item->i2dt_affine();
+ }
+ addCurve (item, h[0], h[1], h[2], h[3], Inkscape::FOR_STROKE );
+ }
+ }
}
}
}
@@ -1783,6 +2262,7 @@ void GrDrag::updateLines()
/**
* Regenerates the levels list from the current selection.
+ * Levels correspond to bounding box edges and midpoints.
*/
void GrDrag::updateLevels()
{
@@ -1857,12 +2337,13 @@ void GrDrag::selected_move(double x, double y, bool write_repr, bool scale_radia
}
did = true;
+ Geom::Point p_old = d->point;
d->point += Geom::Point (x, y);
d->point_original = d->point;
sp_knot_moveto (d->knot, d->point);
d->fireDraggables (write_repr, scale_radial);
-
+ d->updateHandles( p_old, MG_NODE_NO_SCALE );
d->updateDependencies(write_repr);
}
}
@@ -2027,6 +2508,7 @@ void GrDrag::deleteSelected(bool just_one)
}
}
break;
+
default:
break;
}
@@ -2128,7 +2610,8 @@ void GrDrag::deleteSelected(bool just_one)
break;
case POINT_RG_R1:
case POINT_RG_R2:
- stopinfo->vector->getRepr()->removeChild(stopinfo->spstop->getRepr());
+ {
+ stopinfo->vector->getRepr()->removeChild(stopinfo->spstop->getRepr());
SPRadialGradient *rg = SP_RADIALGRADIENT(stopinfo->gradient);
double oldradius = rg->r.computed;
@@ -2150,7 +2633,10 @@ void GrDrag::deleteSelected(bool just_one)
sp_repr_set_css_double(stop->getRepr(), "offset", stop->offset);
stop = stop->getNextStop();
}
- break;
+ }
+ break;
+ default:
+ break;
}
}
else
diff --git a/src/gradient-drag.h b/src/gradient-drag.h
index 2150ef3d9..7fb5c5de2 100644
--- a/src/gradient-drag.h
+++ b/src/gradient-drag.h
@@ -8,6 +8,7 @@
* bulia byak <bulia@users.sf.net>
* Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
* Jon A. Cruz <jon@joncruz.org>
+ * Tavmjong Bah <tavmjong@free.fr>
*
* Copyright (C) 2012 Authors
* Copyright (C) 2007 Johan Engelen
@@ -26,12 +27,14 @@
#include "knot-enums.h"
#include "sp-gradient.h" // TODO refactor enums to external .h file
+#include "sp-mesh-array.h"
struct SPKnot;
class SPDesktop;
class SPCSSAttr;
class SPLinearGradient;
+class SPMeshGradient;
class SPItem;
class SPObject;
class SPRadialGradient;
@@ -98,11 +101,15 @@ struct GrDragger {
void deselect();
bool isSelected();
+ /* Given one GrDraggable, these all update other draggables belonging to same GrDragger */
void moveThisToDraggable(SPItem *item, GrPointType point_type, gint point_i, Inkscape::PaintTarget fill_or_stroke, bool write_repr);
void moveOtherToDraggable(SPItem *item, GrPointType point_type, gint point_i, Inkscape::PaintTarget fill_or_stroke, bool write_repr);
void updateMidstopDependencies(GrDraggable *draggable, bool write_repr);
void updateDependencies(bool write_repr);
+ /* Update handles/tensors when mesh corner moved */
+ void updateHandles( Geom::Point pc_old, MeshNodeOperation op );
+
bool mayMerge(GrDragger *other);
bool mayMerge(GrDraggable *da2);
@@ -184,12 +191,14 @@ public: // FIXME: make more of this private!
private:
void deselect_all();
- void addLine(SPItem *item, Geom::Point p1, Geom::Point p2, Inkscape::PaintTarget fill_or_stroke);
+ void addLine( SPItem *item, Geom::Point p1, Geom::Point p2, Inkscape::PaintTarget fill_or_stroke);
+ void addCurve(SPItem *item, Geom::Point p0, Geom::Point p1, Geom::Point p2, Geom::Point p3, Inkscape::PaintTarget fill_or_stroke);
void addDragger(GrDraggable *draggable);
void addDraggersRadial(SPRadialGradient *rg, SPItem *item, Inkscape::PaintTarget fill_or_stroke);
void addDraggersLinear(SPLinearGradient *lg, SPItem *item, Inkscape::PaintTarget fill_or_stroke);
+ void addDraggersMesh( SPMeshGradient *mg, SPItem *item, Inkscape::PaintTarget fill_or_stroke);
bool styleSet( const SPCSSAttr *css );
diff --git a/src/mesh-context.cpp b/src/mesh-context.cpp
new file mode 100644
index 000000000..d34782e12
--- /dev/null
+++ b/src/mesh-context.cpp
@@ -0,0 +1,1045 @@
+/*
+ * Mesh drawing and editing tool
+ *
+ * Authors:
+ * bulia byak <buliabyak@users.sf.net>
+ * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
+ * Abhishek Sharma
+ * Tavmjong Bah <tavmjong@free.fr>
+ *
+ * Copyright (C) 2012 Tavmjong Bah
+ * Copyright (C) 2007 Johan Engelen
+ * Copyright (C) 2005 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+//#define DEBUG_MESH
+
+
+// Libraries
+#include <gdk/gdkkeysyms.h>
+#include <glibmm/i18n.h>
+
+// General
+#include "desktop.h"
+#include "desktop-handles.h"
+#include "document.h"
+#include "document-undo.h"
+#include "macros.h"
+#include "message-context.h"
+#include "message-stack.h"
+#include "preferences.h"
+#include "rubberband.h"
+#include "selection.h"
+#include "snap.h"
+#include "sp-namedview.h"
+#include "verbs.h"
+
+// Gradient specific
+#include "gradient-drag.h"
+#include "gradient-chemistry.h"
+#include "pixmaps/cursor-gradient.xpm"
+#include "pixmaps/cursor-gradient-add.xpm"
+
+// Mesh specific
+#include "mesh-context.h"
+#include "sp-mesh-gradient.h"
+#include "display/sp-ctrlcurve.h"
+
+#if !GTK_CHECK_VERSION(2,22,0)
+#include "compat-key-syms.h"
+#endif
+
+using Inkscape::DocumentUndo;
+
+static void sp_mesh_context_class_init(SPMeshContextClass *klass);
+static void sp_mesh_context_init(SPMeshContext *gr_context);
+static void sp_mesh_context_dispose(GObject *object);
+
+static void sp_mesh_context_setup(SPEventContext *ec);
+
+static gint sp_mesh_context_root_handler(SPEventContext *event_context, GdkEvent *event);
+
+static void sp_mesh_drag(SPMeshContext &rc, Geom::Point const pt, guint state, guint32 etime);
+
+static SPEventContextClass *parent_class;
+
+
+GType sp_mesh_context_get_type()
+{
+ static GType type = 0;
+ if (!type) {
+ GTypeInfo info = {
+ sizeof(SPMeshContextClass),
+ NULL, NULL,
+ (GClassInitFunc) sp_mesh_context_class_init,
+ NULL, NULL,
+ sizeof(SPMeshContext),
+ 4,
+ (GInstanceInitFunc) sp_mesh_context_init,
+ NULL, /* value_table */
+ };
+ type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPMeshContext", &info, (GTypeFlags) 0);
+ }
+ return type;
+}
+
+static void sp_mesh_context_class_init(SPMeshContextClass *klass)
+{
+ GObjectClass *object_class = (GObjectClass *) klass;
+ SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
+
+ parent_class = (SPEventContextClass *) g_type_class_peek_parent(klass);
+
+ object_class->dispose = sp_mesh_context_dispose;
+
+ event_context_class->setup = sp_mesh_context_setup;
+ event_context_class->root_handler = sp_mesh_context_root_handler;
+}
+
+static void sp_mesh_context_init(SPMeshContext *gr_context)
+{
+ SPEventContext *event_context = SP_EVENT_CONTEXT(gr_context);
+
+ gr_context->cursor_addnode = false;
+ event_context->cursor_shape = cursor_gradient_xpm;
+ event_context->hot_x = 4;
+ event_context->hot_y = 4;
+ event_context->xp = 0;
+ event_context->yp = 0;
+ event_context->tolerance = 6;
+ event_context->within_tolerance = false;
+ event_context->item_to_select = NULL;
+}
+
+static void sp_mesh_context_dispose(GObject *object)
+{
+ SPMeshContext *rc = SP_MESH_CONTEXT(object);
+ SPEventContext *ec = SP_EVENT_CONTEXT(object);
+
+ ec->enableGrDrag(false);
+
+ if (rc->_message_context) {
+ delete rc->_message_context;
+ }
+
+ rc->selcon->disconnect();
+ delete rc->selcon;
+ rc->subselcon->disconnect();
+ delete rc->subselcon;
+
+ G_OBJECT_CLASS(parent_class)->dispose(object);
+}
+
+const gchar *ms_handle_descr [] = {
+ N_("Mesh gradient <b>corner</b>"),
+ N_("Mesh gradient <b>handle</b>"),
+ N_("Mesh gradient <b>tensor</b>")
+};
+
+static void
+mesh_selection_changed (Inkscape::Selection *, gpointer data)
+{
+ SPMeshContext *rc = (SPMeshContext *) data;
+
+ GrDrag *drag = rc->_grdrag;
+ Inkscape::Selection *selection = sp_desktop_selection(SP_EVENT_CONTEXT(rc)->desktop);
+ if (selection == NULL) {
+ return;
+ }
+ guint n_obj = g_slist_length((GSList *) selection->itemList());
+
+ if (!drag->isNonEmpty() || selection->isEmpty())
+ return;
+ guint n_tot = drag->numDraggers();
+ guint n_sel = drag->numSelected();
+
+ //The use of ngettext in the following code is intentional even if the English singular form would never be used
+ if (n_sel == 1) {
+ if (drag->singleSelectedDraggerNumDraggables() == 1) {
+ gchar * message = g_strconcat(
+ //TRANSLATORS: %s will be substituted with the point name (see previous messages); This is part of a compound message
+ _("%s selected"),
+ //TRANSLATORS: Mind the space in front. This is part of a compound message
+ ngettext(" out of %d mesh handle"," out of %d mesh handles",n_tot),
+ ngettext(" on %d selected object"," on %d selected objects",n_obj),NULL);
+ rc->_message_context->setF(Inkscape::NORMAL_MESSAGE,
+ message,_(ms_handle_descr[drag->singleSelectedDraggerSingleDraggableType()]), n_tot, n_obj);
+ } else {
+ gchar * message =
+ g_strconcat(
+ //TRANSLATORS: This is a part of a compound message (out of two more indicating: grandint handle count & object count)
+ ngettext("One handle merging %d stop (drag with <b>Shift</b> to separate) selected",
+ "One handle merging %d stops (drag with <b>Shift</b> to separate) selected",
+ drag->singleSelectedDraggerNumDraggables()),
+ ngettext(" out of %d mesh handle"," out of %d mesh handles",n_tot),
+ ngettext(" on %d selected object"," on %d selected objects",n_obj),NULL);
+ rc->_message_context->setF(Inkscape::NORMAL_MESSAGE,message,drag->singleSelectedDraggerNumDraggables(), n_tot, n_obj);
+ }
+ } else if (n_sel > 1) {
+ //TRANSLATORS: The plural refers to number of selected mesh handles. This is part of a compound message (part two indicates selected object count)
+ gchar * message =
+ g_strconcat(ngettext("<b>%d</b> mesh handle selected out of %d","<b>%d</b> mesh handles selected out of %d",n_sel),
+ //TRANSLATORS: Mind the space in front. (Refers to gradient handles selected). This is part of a compound message
+ ngettext(" on %d selected object"," on %d selected objects",n_obj),NULL);
+ rc->_message_context->setF(Inkscape::NORMAL_MESSAGE,message, n_sel, n_tot, n_obj);
+ } else if (n_sel == 0) {
+ rc->_message_context->setF(Inkscape::NORMAL_MESSAGE,
+ //TRANSLATORS: The plural refers to number of selected objects
+ ngettext("<b>No</b> mesh handles selected out of %d on %d selected object",
+ "<b>No</b> mesh handles selected out of %d on %d selected objects",n_obj), n_tot, n_obj);
+ }
+
+ // FIXME
+ // We need to update mesh gradient handles.
+ // Get gradient this drag belongs too..
+ // std::cout << "mesh_selection_changed: selection: objects: " << n_obj << std::endl;
+ // GSList *itemList = (GSList *) selection->itemList();
+ // while( itemList ) {
+
+ // SPItem *item = SP_ITEM( itemList->data );
+ // // std::cout << " item: " << SP_OBJECT(item)->getId() << std::endl;
+
+ // SPStyle *style = item->style;
+ // if (style && (style->fill.isPaintserver())) {
+
+ // SPPaintServer *server = item->style->getFillPaintServer();
+ // if ( SP_IS_MESHGRADIENT(server) ) {
+
+ // SPMeshGradient *mg = SP_MESHGRADIENT(server);
+
+ // uint rows = 0;//mg->array.patches.size();
+ // for ( uint i = 0; i < rows; ++i ) {
+ // uint columns = 0;//mg->array.patches[0].size();
+ // for ( uint j = 0; j < columns; ++j ) {
+ // }
+ // }
+ // }
+ // }
+ // itemList = itemList->next;
+ // }
+
+ // GList* dragger_ptr = drag->draggers; // Points to GrDragger class (group of GrDraggable)
+ // uint count = 0;
+ // while( dragger_ptr ) {
+
+ // std::cout << "mesh_selection_changed: dragger: " << ++count << std::endl;
+ // GSList* draggable_ptr = ((GrDragger *) dragger_ptr->data)->draggables;
+
+ // while( draggable_ptr ) {
+
+ // std::cout << "mesh_selection_changed: draggable: " << draggable_ptr << std::endl;
+ // GrDraggable *draggable = (GrDraggable *) draggable_ptr->data;
+
+ // gint point_type = draggable->point_type;
+ // gint point_i = draggable->point_i;
+ // bool fill_or_stroke = draggable->fill_or_stroke;
+
+ // if( point_type == POINT_MG_CORNER ) {
+
+ // //std::cout << "mesh_selection_changed: POINT_MG_CORNER: " << point_i << std::endl;
+ // // Now we must create or destroy corresponding handles.
+
+ // if( g_list_find( drag->selected, dragger_ptr->data ) ) {
+ // //std::cout << "gradient_selection_changed: Selected: " << point_i << std::endl;
+ // // Which meshes does this point belong to?
+
+ // } else {
+ // //std::cout << "mesh_selection_changed: Not Selected: " << point_i << std::endl;
+ // }
+ // }
+
+ // draggable_ptr = draggable_ptr->next;
+
+ // }
+
+ // dragger_ptr = dragger_ptr->next;
+ // }
+}
+
+
+static void
+mesh_subselection_changed (gpointer, gpointer data)
+{
+ mesh_selection_changed (NULL, data);
+}
+
+
+static void sp_mesh_context_setup(SPEventContext *ec)
+{
+ SPMeshContext *rc = SP_MESH_CONTEXT(ec);
+
+ if (((SPEventContextClass *) parent_class)->setup) {
+ ((SPEventContextClass *) parent_class)->setup(ec);
+ }
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ if (prefs->getBool("/tools/mesh/selcue", true)) {
+ ec->enableSelectionCue();
+ }
+
+ ec->enableGrDrag();
+ Inkscape::Selection *selection = sp_desktop_selection(ec->desktop);
+
+ rc->_message_context = new Inkscape::MessageContext(sp_desktop_message_stack(ec->desktop));
+
+ rc->selcon = new sigc::connection (selection->connectChanged( sigc::bind (sigc::ptr_fun(&mesh_selection_changed), rc)));
+ rc->subselcon = new sigc::connection (ec->desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(&mesh_subselection_changed), rc)));
+ mesh_selection_changed(selection, rc);
+}
+
+void
+sp_mesh_context_select_next (SPEventContext *event_context)
+{
+ GrDrag *drag = event_context->_grdrag;
+ g_assert (drag);
+
+ GrDragger *d = drag->select_next();
+
+ event_context->desktop->scroll_to_point(d->point, 1.0);
+}
+
+void
+sp_mesh_context_select_prev (SPEventContext *event_context)
+{
+ GrDrag *drag = event_context->_grdrag;
+ g_assert (drag);
+
+ GrDragger *d = drag->select_prev();
+
+ event_context->desktop->scroll_to_point(d->point, 1.0);
+}
+
+/**
+Returns true if mouse cursor over mesh edge.
+*/
+static bool
+sp_mesh_context_is_over_line (SPMeshContext *rc, SPItem *item, Geom::Point event_p)
+{
+ SPDesktop *desktop = SP_EVENT_CONTEXT (rc)->desktop;
+
+ //Translate mouse point into proper coord system
+ rc->mousepoint_doc = desktop->w2d(event_p);
+
+ SPCtrlCurve *curve = SP_CTRLCURVE(item);
+ Geom::BezierCurveN<3> b( curve->p0, curve->p1, curve->p2, curve->p3 );
+ Geom::Coord coord = b.nearestPoint( rc->mousepoint_doc ); // Coord == double
+ Geom::Point nearest = b( coord );
+
+ double dist_screen = Geom::L2 (rc->mousepoint_doc - nearest) * desktop->current_zoom();
+
+ double tolerance = (double) SP_EVENT_CONTEXT(rc)->tolerance;
+
+ bool close = (dist_screen < tolerance);
+
+ return close;
+}
+
+
+/**
+Split row/column near the mouse point.
+*/
+static void
+sp_mesh_context_split_near_point (SPMeshContext *rc, SPItem *item, Geom::Point mouse_p, guint32 /*etime*/)
+{
+
+#ifdef DEBUG_MESH
+ std::cout << "sp_mesh_context_split_near_point: entrance: " << mouse_p << std::endl;
+#endif
+
+ // item is the selected item. mouse_p the location in doc coordinates of where to add the stop
+
+ SPEventContext *ec = SP_EVENT_CONTEXT(rc);
+ SPDesktop *desktop = SP_EVENT_CONTEXT (rc)->desktop;
+
+ double tolerance = (double) ec->tolerance;
+
+ ec->get_drag()->addStopNearPoint (item, mouse_p, tolerance/desktop->current_zoom());
+
+ DocumentUndo::done(sp_desktop_document (desktop), SP_VERB_CONTEXT_MESH,
+ _("Split mesh row/column"));
+
+ ec->get_drag()->updateDraggers();
+}
+
+/**
+Wrapper for various mesh operations that require a list of selected corner nodes.
+ */
+static void
+sp_mesh_context_corner_operation (SPMeshContext *rc, MeshCornerOperation operation )
+{
+
+#ifdef DEBUG_MESH
+ std::cout << "sp_mesh_corner_operation: entrance: " << operation << std::endl;
+#endif
+
+ SPDocument *doc = NULL;
+ GrDrag *drag = rc->_grdrag;
+
+ std::map<SPMeshGradient*, std::vector<uint> > points;
+ std::map<SPMeshGradient*, SPItem*> items;
+
+ // Get list of selected draggers for each mesh.
+ // For all selected draggers
+ for (GList *i = drag->selected; i != NULL; i = i->next) {
+ GrDragger *dragger = (GrDragger *) i->data;
+ // For all draggables of dragger
+ for (GSList const* j = dragger->draggables; j != NULL; j = j->next) {
+ GrDraggable *d = (GrDraggable *) j->data;
+
+ // Only mesh corners
+ if( d->point_type != POINT_MG_CORNER ) continue;
+
+ // Find the gradient
+ SPMeshGradient *gradient = SP_MESHGRADIENT( getGradient (d->item, d->fill_or_stroke) );
+
+ // Collect points together for same gradient
+ points[gradient].push_back( d->point_i );
+ items[gradient] = d->item;
+ }
+ }
+
+ // Loop over meshes.
+ for( std::map<SPMeshGradient*, std::vector<uint> >::const_iterator iter = points.begin(); iter != points.end(); ++iter) {
+ SPMeshGradient *mg = SP_MESHGRADIENT( iter->first );
+ if( iter->second.size() > 0 ) {
+ uint noperation = 0;
+ switch (operation) {
+
+ case MG_CORNER_SIDE_TOGGLE:
+ // std::cout << "SIDE_TOGGLE" << std::endl;
+ noperation += mg->array.side_toggle( iter->second );
+ break;
+
+ case MG_CORNER_SIDE_ARC:
+ // std::cout << "SIDE_ARC" << std::endl;
+ noperation += mg->array.side_arc( iter->second );
+ break;
+
+ case MG_CORNER_TENSOR_TOGGLE:
+ // std::cout << "TENSOR_TOGGLE" << std::endl;
+ noperation += mg->array.tensor_toggle( iter->second );
+ break;
+
+ case MG_CORNER_COLOR_SMOOTH:
+ // std::cout << "COLOR_SMOOTH" << std::endl;
+ noperation += mg->array.color_smooth( iter->second );
+ break;
+
+ case MG_CORNER_COLOR_PICK:
+ // std::cout << "COLOR_PICK" << std::endl;
+ noperation += mg->array.color_pick( iter->second, items[iter->first] );
+ break;
+
+ default:
+ std::cout << "sp_mesh_corner_operation: unknown operation" << std::endl;
+ }
+
+ if( noperation > 0 ) {
+ mg->array.write( mg );
+ mg->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ doc = mg->document;
+
+ switch (operation) {
+
+ case MG_CORNER_SIDE_TOGGLE:
+ DocumentUndo::done(doc, SP_VERB_CONTEXT_MESH, _("Toggled mesh path type."));
+ break;
+
+ case MG_CORNER_SIDE_ARC:
+ DocumentUndo::done(doc, SP_VERB_CONTEXT_MESH, _("Approximated arc for mesh side."));
+ break;
+
+ case MG_CORNER_TENSOR_TOGGLE:
+ DocumentUndo::done(doc, SP_VERB_CONTEXT_MESH, _("Toggled mesh tensors."));
+ break;
+
+ case MG_CORNER_COLOR_SMOOTH:
+ DocumentUndo::done(doc, SP_VERB_CONTEXT_MESH, _("Smoothed mesh corner color."));
+ break;
+
+ case MG_CORNER_COLOR_PICK:
+ DocumentUndo::done(doc, SP_VERB_CONTEXT_MESH, _("Picked mesh corner color."));
+ break;
+
+ default:
+ std::cout << "sp_mesh_corner_operation: unknown operation" << std::endl;
+ }
+ }
+ }
+ }
+ drag->updateDraggers();
+
+}
+
+
+/**
+Handles all keyboard and mouse input for meshs.
+*/
+static gint
+sp_mesh_context_root_handler(SPEventContext *event_context, GdkEvent *event)
+{
+ // static int count = 0;
+ // std::cout << "sp_mesh_context_root_handler: " << count++ << std::endl;
+ static bool dragging;
+
+ SPDesktop *desktop = event_context->desktop;
+ Inkscape::Selection *selection = sp_desktop_selection (desktop);
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
+ SPMeshContext *rc = SP_MESH_CONTEXT(event_context);
+
+ event_context->tolerance = prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
+ double const nudge = prefs->getDoubleLimited("/options/nudgedistance/value", 2, 0, 1000, "px"); // in px
+
+ GrDrag *drag = event_context->_grdrag;
+ g_assert (drag);
+
+ gint ret = FALSE;
+ switch (event->type) {
+
+ case GDK_2BUTTON_PRESS:
+
+#ifdef DEBUG_MESH
+ std::cout << "sp_mesh_context_root_handler: GDK_2BUTTON_PRESS" << std::endl;
+#endif
+
+ // Double click:
+ // If over a mesh line, divide mesh row/column
+ // If not over a line, create new gradients for selected objects.
+
+ if ( event->button.button == 1 ) {
+
+ // Are we over a mesh line?
+ bool over_line = false;
+ SPCtrlCurve *line = NULL;
+ if (drag->lines) {
+ for (GSList *l = drag->lines; (l != NULL) && (!over_line); l = l->next) {
+ line = (SPCtrlCurve*) l->data;
+ over_line |= sp_mesh_context_is_over_line (rc, (SPItem*) line, Geom::Point(event->motion.x, event->motion.y));
+ }
+ }
+
+ if (over_line) {
+ // We take the first item in selection, because with doubleclick, the first click
+ // always resets selection to the single object under cursor
+ sp_mesh_context_split_near_point(rc, SP_ITEM(selection->itemList()->data), rc->mousepoint_doc, event->button.time);
+
+ } else {
+ // Create a new gradient with default coordinates.
+
+ for (GSList const* i = selection->itemList(); i != NULL; i = i->next) {
+
+ SPItem *item = SP_ITEM(i->data);
+ SPGradientType new_type = SP_GRADIENT_TYPE_MESH;
+ Inkscape::PaintTarget fsmode = (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE;
+
+#ifdef DEBUG_MESH
+ std::cout << "sp_mesh_context_root_handler: creating new mesh on: " << (fsmode == Inkscape::FOR_FILL ? "Fill" : "Stroke") << std::endl;
+#endif
+ SPGradient *vector = sp_gradient_vector_for_object(sp_desktop_document(desktop), desktop, item, fsmode);
+
+ SPGradient *priv = sp_item_set_gradient(item, vector, new_type, fsmode);
+ sp_gradient_reset_to_userspace(priv, item);
+ }
+
+ DocumentUndo::done(sp_desktop_document (desktop), SP_VERB_CONTEXT_MESH,
+ _("Create default mesh"));
+ }
+ ret = TRUE;
+ }
+ break;
+
+ case GDK_BUTTON_PRESS:
+
+#ifdef DEBUG_MESH
+ std::cout << "sp_mesh_context_root_handler: GDK_BUTTON_PRESS" << std::endl;
+#endif
+ // Button down
+ // If Shift key down: do rubber band selection
+ // Else set origin for drag. A drag creates a new gradient if one does not exist
+
+ if ( event->button.button == 1 && !event_context->space_panning ) {
+ Geom::Point button_w(event->button.x, event->button.y);
+
+ // save drag origin
+ event_context->xp = (gint) button_w[Geom::X];
+ event_context->yp = (gint) button_w[Geom::Y];
+ event_context->within_tolerance = true;
+
+ dragging = true;
+
+ Geom::Point button_dt = desktop->w2d(button_w);
+ if (event->button.state & GDK_SHIFT_MASK) {
+ Inkscape::Rubberband::get(desktop)->start(desktop, button_dt);
+ } else {
+ // remember clicked item, disregarding groups, honoring Alt; do nothing with Crtl to
+ // enable Ctrl+doubleclick of exactly the selected item(s)
+ if (!(event->button.state & GDK_CONTROL_MASK))
+ event_context->item_to_select = sp_event_context_find_item (desktop, button_w, event->button.state & GDK_MOD1_MASK, TRUE);
+
+ if (!selection->isEmpty()) {
+ SnapManager &m = desktop->namedview->snap_manager;
+ m.setup(desktop);
+ m.freeSnapReturnByRef(button_dt, Inkscape::SNAPSOURCE_NODE_HANDLE);
+ m.unSetup();
+ }
+ rc->origin = button_dt;
+ }
+
+ ret = TRUE;
+ }
+ break;
+
+ case GDK_MOTION_NOTIFY:
+
+ // Mouse move
+
+ if ( dragging
+ && ( event->motion.state & GDK_BUTTON1_MASK ) && !event_context->space_panning )
+ {
+
+#ifdef DEBUG_MESH
+ std::cout << "sp_mesh_context_root_handler: GDK_MOTION_NOTIFY: Dragging" << std::endl;
+#endif
+ if ( event_context->within_tolerance
+ && ( abs( (gint) event->motion.x - event_context->xp ) < event_context->tolerance )
+ && ( abs( (gint) event->motion.y - event_context->yp ) < event_context->tolerance ) ) {
+ break; // do not drag if we're within tolerance from origin
+ }
+ // Once the user has moved farther than tolerance from the original location
+ // (indicating they intend to draw, not click), then always process the
+ // motion notify coordinates as given (no snapping back to origin)
+ event_context->within_tolerance = false;
+
+ Geom::Point const motion_w(event->motion.x,
+ event->motion.y);
+ Geom::Point const motion_dt = event_context->desktop->w2d(motion_w);
+
+ if (Inkscape::Rubberband::get(desktop)->is_started()) {
+ Inkscape::Rubberband::get(desktop)->move(motion_dt);
+ event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Draw around</b> handles to select them"));
+ } else {
+ // Create new gradient with coordinates determined by drag.
+ sp_mesh_drag(*rc, motion_dt, event->motion.state, event->motion.time);
+ }
+ gobble_motion_events(GDK_BUTTON1_MASK);
+
+ ret = TRUE;
+
+ } else {
+ // Not dragging
+
+ // Do snapping
+ if (!drag->mouseOver() && !selection->isEmpty()) {
+ SnapManager &m = desktop->namedview->snap_manager;
+ m.setup(desktop);
+
+ Geom::Point const motion_w(event->motion.x, event->motion.y);
+ Geom::Point const motion_dt = event_context->desktop->w2d(motion_w);
+
+ m.preSnap(Inkscape::SnapCandidatePoint(motion_dt, Inkscape::SNAPSOURCE_OTHER_HANDLE));
+ m.unSetup();
+ }
+
+ // Highlight corner node corresponding to side or tensor node
+ if( drag->mouseOver() ) {
+ // MESH FIXME: Light up corresponding corner node corresponding to node we are over.
+ // See "pathflash" in ui/tools/node-tool.cpp for ideas.
+ // Use desktop->add_temporary_canvasitem( SPCanvasItem, milliseconds );
+ }
+
+ // Change cursor shape if over line
+ bool over_line = false;
+ if (drag->lines) {
+ for (GSList *l = drag->lines; l != NULL; l = l->next) {
+ over_line |= sp_mesh_context_is_over_line (rc, (SPItem*) l->data, Geom::Point(event->motion.x, event->motion.y));
+ }
+ }
+
+ if (rc->cursor_addnode && !over_line) {
+ event_context->cursor_shape = cursor_gradient_xpm;
+ sp_event_context_update_cursor(event_context);
+ rc->cursor_addnode = false;
+ } else if (!rc->cursor_addnode && over_line) {
+ event_context->cursor_shape = cursor_gradient_add_xpm;
+ sp_event_context_update_cursor(event_context);
+ rc->cursor_addnode = true;
+ }
+ }
+ break;
+
+ case GDK_BUTTON_RELEASE:
+
+#ifdef DEBUG_MESH
+ std::cout << "sp_mesh_context_root_handler: GDK_BUTTON_RELEASE" << std::endl;
+#endif
+
+ event_context->xp = event_context->yp = 0;
+ if ( event->button.button == 1 && !event_context->space_panning ) {
+
+ // Check if over line
+ bool over_line = false;
+ SPCtrlLine *line = NULL;
+ if (drag->lines) {
+ for (GSList *l = drag->lines; (l != NULL) && (!over_line); l = l->next) {
+ line = (SPCtrlLine*) l->data;
+ over_line = sp_mesh_context_is_over_line (rc, (SPItem*) line, Geom::Point(event->motion.x, event->motion.y));
+ if (over_line)
+ break;
+ }
+ }
+
+ if ( (event->button.state & GDK_CONTROL_MASK) && (event->button.state & GDK_MOD1_MASK ) ) {
+ if (over_line && line) {
+ sp_mesh_context_split_near_point(rc, line->item, rc->mousepoint_doc, 0);
+ ret = TRUE;
+ }
+
+ } else {
+
+ dragging = false;
+
+ // unless clicked with Ctrl (to enable Ctrl+doubleclick).
+ if (event->button.state & GDK_CONTROL_MASK) {
+ ret = TRUE;
+ break;
+ }
+
+ if (!event_context->within_tolerance) {
+ // we've been dragging, either do nothing (grdrag handles that),
+ // or rubberband-select if we have rubberband
+ Inkscape::Rubberband *r = Inkscape::Rubberband::get(desktop);
+ if (r->is_started() && !event_context->within_tolerance) {
+ // this was a rubberband drag
+ if (r->getMode() == RUBBERBAND_MODE_RECT) {
+ Geom::OptRect const b = r->getRectangle();
+ drag->selectRect(*b);
+ }
+ }
+
+ } else if (event_context->item_to_select) {
+ if (over_line && line) {
+ // Clicked on an existing mesh line, don't change selection. This stops
+ // possible change in selection during a double click with overlapping objects
+ }
+ else {
+ // no dragging, select clicked item if any
+ if (event->button.state & GDK_SHIFT_MASK) {
+ selection->toggle(event_context->item_to_select);
+ } else {
+ selection->set(event_context->item_to_select);
+ }
+ }
+ } else {
+ // click in an empty space; do the same as Esc
+ if (drag->selected) {
+ drag->deselectAll();
+ } else {
+ selection->clear();
+ }
+ }
+
+ event_context->item_to_select = NULL;
+ ret = TRUE;
+ }
+ Inkscape::Rubberband::get(desktop)->stop();
+ }
+ break;
+
+ case GDK_KEY_PRESS:
+
+#ifdef DEBUG_MESH
+ std::cout << "sp_mesh_context_root_handler: GDK_KEY_PRESS" << std::endl;
+#endif
+
+ // FIXME: tip
+ switch (get_group0_keyval (&event->key)) {
+ case GDK_KEY_Alt_L:
+ case GDK_KEY_Alt_R:
+ case GDK_KEY_Control_L:
+ case GDK_KEY_Control_R:
+ case GDK_KEY_Shift_L:
+ case GDK_KEY_Shift_R:
+ case GDK_KEY_Meta_L: // Meta is when you press Shift+Alt (at least on my machine)
+ case GDK_KEY_Meta_R:
+ sp_event_show_modifier_tip (event_context->defaultMessageContext(), event,
+ _("FIXME<b>Ctrl</b>: snap mesh angle"),
+ _("FIXME<b>Shift</b>: draw mesh around the starting point"),
+ NULL);
+ break;
+
+ case GDK_KEY_A:
+ case GDK_KEY_a:
+ if (MOD__CTRL_ONLY && drag->isNonEmpty()) {
+ drag->selectAll();
+ ret = TRUE;
+ }
+ break;
+
+ case GDK_KEY_Escape:
+ if (drag->selected) {
+ drag->deselectAll();
+ } else {
+ selection->clear();
+ }
+ ret = TRUE;
+ //TODO: make dragging escapable by Esc
+ break;
+
+ case GDK_KEY_Left: // move handle left
+ case GDK_KEY_KP_Left:
+ case GDK_KEY_KP_4:
+ if (!MOD__CTRL) { // not ctrl
+ gint mul = 1 + gobble_key_events(
+ get_group0_keyval(&event->key), 0); // with any mask
+ if (MOD__ALT) { // alt
+ if (MOD__SHIFT) drag->selected_move_screen(mul*-10, 0); // shift
+ else drag->selected_move_screen(mul*-1, 0); // no shift
+ }
+ else { // no alt
+ if (MOD__SHIFT) drag->selected_move(mul*-10*nudge, 0); // shift
+ else drag->selected_move(mul*-nudge, 0); // no shift
+ }
+ ret = TRUE;
+ }
+ break;
+ case GDK_KEY_Up: // move handle up
+ case GDK_KEY_KP_Up:
+ case GDK_KEY_KP_8:
+ if (!MOD__CTRL) { // not ctrl
+ gint mul = 1 + gobble_key_events(
+ get_group0_keyval(&event->key), 0); // with any mask
+ if (MOD__ALT) { // alt
+ if (MOD__SHIFT) drag->selected_move_screen(0, mul*10); // shift
+ else drag->selected_move_screen(0, mul*1); // no shift
+ }
+ else { // no alt
+ if (MOD__SHIFT) drag->selected_move(0, mul*10*nudge); // shift
+ else drag->selected_move(0, mul*nudge); // no shift
+ }
+ ret = TRUE;
+ }
+ break;
+
+ case GDK_KEY_Right: // move handle right
+ case GDK_KEY_KP_Right:
+ case GDK_KEY_KP_6:
+ if (!MOD__CTRL) { // not ctrl
+ gint mul = 1 + gobble_key_events(
+ get_group0_keyval(&event->key), 0); // with any mask
+ if (MOD__ALT) { // alt
+ if (MOD__SHIFT) drag->selected_move_screen(mul*10, 0); // shift
+ else drag->selected_move_screen(mul*1, 0); // no shift
+ }
+ else { // no alt
+ if (MOD__SHIFT) drag->selected_move(mul*10*nudge, 0); // shift
+ else drag->selected_move(mul*nudge, 0); // no shift
+ }
+ ret = TRUE;
+ }
+ break;
+
+ case GDK_KEY_Down: // move handle down
+ case GDK_KEY_KP_Down:
+ case GDK_KEY_KP_2:
+ if (!MOD__CTRL) { // not ctrl
+ gint mul = 1 + gobble_key_events(
+ get_group0_keyval(&event->key), 0); // with any mask
+ if (MOD__ALT) { // alt
+ if (MOD__SHIFT) drag->selected_move_screen(0, mul*-10); // shift
+ else drag->selected_move_screen(0, mul*-1); // no shift
+ }
+ else { // no alt
+ if (MOD__SHIFT) drag->selected_move(0, mul*-10*nudge); // shift
+ else drag->selected_move(0, mul*-nudge); // no shift
+ }
+ ret = TRUE;
+ }
+ break;
+
+ case GDK_KEY_Insert:
+ case GDK_KEY_KP_Insert:
+ // with any modifiers:
+ //sp_gradient_context_add_stops_between_selected_stops (rc);
+ std::cout << "Inserting stops between selected stops not implemented yet" << std::endl;
+ ret = TRUE;
+ break;
+
+ case GDK_KEY_Delete:
+ case GDK_KEY_KP_Delete:
+ case GDK_KEY_BackSpace:
+ if ( drag->selected ) {
+ std::cout << "Deleting mesh stops not implemented yet" << std::endl;
+ ret = TRUE;
+ }
+ break;
+
+ // Mesh Operations --------------------------------------------
+
+ case GDK_b: // Toggle mesh side between lineto and curveto.
+ case GDK_B:
+ if (MOD__ALT && drag->isNonEmpty() && drag->hasSelection()) {
+ sp_mesh_context_corner_operation ( rc, MG_CORNER_SIDE_TOGGLE );
+ ret = TRUE;
+ }
+ break;
+
+ case GDK_c: // Convert mesh side from generic Bezier to Bezier approximating arc,
+ case GDK_C: // preserving handle direction.
+ if (MOD__ALT && drag->isNonEmpty() && drag->hasSelection()) {
+ sp_mesh_context_corner_operation ( rc, MG_CORNER_SIDE_ARC );
+ ret = TRUE;
+ }
+ break;
+
+ case GDK_g: // Toggle mesh tensor points on/off
+ case GDK_G:
+ if (MOD__ALT && drag->isNonEmpty() && drag->hasSelection()) {
+ sp_mesh_context_corner_operation ( rc, MG_CORNER_TENSOR_TOGGLE );
+ ret = TRUE;
+ }
+ break;
+
+ case GDK_j: // Smooth corner color
+ case GDK_J:
+ if (MOD__ALT && drag->isNonEmpty() && drag->hasSelection()) {
+ sp_mesh_context_corner_operation ( rc, MG_CORNER_COLOR_SMOOTH );
+ ret = TRUE;
+ }
+ break;
+
+ case GDK_k: // Pick corner color
+ case GDK_K:
+ if (MOD__ALT && drag->isNonEmpty() && drag->hasSelection()) {
+ sp_mesh_context_corner_operation ( rc, MG_CORNER_COLOR_PICK );
+ ret = TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ case GDK_KEY_RELEASE:
+
+#ifdef DEBUG_MESH
+ std::cout << "sp_mesh_context_root_handler: GDK_KEY_RELEASE" << std::endl;
+#endif
+ switch (get_group0_keyval (&event->key)) {
+ case GDK_KEY_Alt_L:
+ case GDK_KEY_Alt_R:
+ case GDK_KEY_Control_L:
+ case GDK_KEY_Control_R:
+ case GDK_KEY_Shift_L:
+ case GDK_KEY_Shift_R:
+ case GDK_KEY_Meta_L: // Meta is when you press Shift+Alt
+ case GDK_KEY_Meta_R:
+ event_context->defaultMessageContext()->clear();
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!ret) {
+ if (((SPEventContextClass *) parent_class)->root_handler) {
+ ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
+ }
+ }
+
+ return ret;
+}
+
+
+static void sp_mesh_drag(SPMeshContext &rc, Geom::Point const pt, guint /*state*/, guint32 etime)
+{
+
+ SPDesktop *desktop = SP_EVENT_CONTEXT(&rc)->desktop;
+ Inkscape::Selection *selection = sp_desktop_selection(desktop);
+ SPDocument *document = sp_desktop_document(desktop);
+ SPEventContext *ec = SP_EVENT_CONTEXT(&rc);
+
+ if (!selection->isEmpty()) {
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ int type = SP_GRADIENT_TYPE_MESH;
+ Inkscape::PaintTarget fill_or_stroke = (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE;
+
+ SPGradient *vector;
+ if (ec->item_to_select) {
+ // pick color from the object where drag started
+ vector = sp_gradient_vector_for_object(document, desktop, ec->item_to_select, fill_or_stroke);
+ } else {
+ // Starting from empty space:
+ // Sort items so that the topmost comes last
+ GSList *items = g_slist_copy ((GSList *) selection->itemList());
+ items = g_slist_sort(items, (GCompareFunc) sp_item_repr_compare_position);
+ // take topmost
+ vector = sp_gradient_vector_for_object(document, desktop, SP_ITEM(g_slist_last(items)->data), fill_or_stroke);
+ g_slist_free (items);
+ }
+
+ // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs
+ SPCSSAttr *css = sp_repr_css_attr_new();
+ sp_repr_css_set_property(css, "fill-opacity", "1.0");
+
+ for (GSList const *i = selection->itemList(); i != NULL; i = i->next) {
+
+ //FIXME: see above
+ sp_repr_css_change_recursive(SP_OBJECT(i->data)->getRepr(), css, "style");
+
+ sp_item_set_gradient(SP_ITEM(i->data), vector, (SPGradientType) type, fill_or_stroke);
+
+ // We don't need to do anything. Mesh is already sized appropriately.
+
+ SP_OBJECT(i->data)->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ }
+ // if (ec->_grdrag) {
+ // ec->_grdrag->updateDraggers();
+ // // prevent regenerating draggers by selection modified signal, which sometimes
+ // // comes too late and thus destroys the knot which we will now grab:
+ // ec->_grdrag->local_change = true;
+ // // give the grab out-of-bounds values of xp/yp because we're already dragging
+ // // and therefore are already out of tolerance
+ // ec->_grdrag->grabKnot (SP_ITEM(selection->itemList()->data),
+ // type == SP_GRADIENT_TYPE_LINEAR? POINT_LG_END : POINT_RG_R1,
+ // -1, // ignore number (though it is always 1)
+ // fill_or_stroke, 99999, 99999, etime);
+ // }
+ // We did an undoable action, but SPDocumentUndo::done will be called by the knot when released
+
+ // status text; we do not track coords because this branch is run once, not all the time
+ // during drag
+ int n_objects = g_slist_length((GSList *) selection->itemList());
+ rc._message_context->setF(Inkscape::NORMAL_MESSAGE,
+ ngettext("<b>Gradient</b> for %d object; with <b>Ctrl</b> to snap angle",
+ "<b>Gradient</b> for %d objects; with <b>Ctrl</b> to snap angle", n_objects),
+ n_objects);
+ } else {
+ sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>objects</b> on which to create gradient."));
+ }
+
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/mesh-context.h b/src/mesh-context.h
new file mode 100644
index 000000000..b43a187e2
--- /dev/null
+++ b/src/mesh-context.h
@@ -0,0 +1,71 @@
+#ifndef __SP_MESH_CONTEXT_H__
+#define __SP_MESH_CONTEXT_H__
+
+/*
+ * Mesh drawing and editing tool
+ *
+ * Authors:
+ * bulia byak <buliabyak@users.sf.net>
+ * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
+ * Jon A. Cruz <jon@joncruz.org.
+ * Tavmjong Bah <tavmjong@free.fr>
+ *
+ * Copyright (C) 2012 Tavmjong Bah
+ * Copyright (C) 2007 Johan Engelen
+ * Copyright (C) 2005,2010 Authors
+ *
+ * Released under GNU GPL
+ */
+
+#include <stddef.h>
+#include <sigc++/sigc++.h>
+#include "event-context.h"
+
+#define SP_TYPE_MESH_CONTEXT (sp_mesh_context_get_type())
+#define SP_MESH_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SP_TYPE_MESH_CONTEXT, SPMeshContext))
+#define SP_MESH_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SP_TYPE_MESH_CONTEXT, SPMeshContextClass))
+#define SP_IS_MESH_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SP_TYPE_MESH_CONTEXT))
+#define SP_IS_MESH_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SP_TYPE_MESH_CONTEXT))
+
+class SPMeshContext;
+class SPMeshContextClass;
+
+struct SPMeshContext : public SPEventContext {
+
+ Geom::Point origin;
+
+ bool cursor_addnode;
+
+ bool node_added;
+
+ Geom::Point mousepoint_doc; // stores mousepoint when over_line in doc coords
+
+ Inkscape::MessageContext *_message_context;
+
+ sigc::connection *selcon;
+ sigc::connection *subselcon;
+};
+
+struct SPMeshContextClass {
+ SPEventContextClass parent_class;
+};
+
+/* Standard Gtk function */
+GType sp_mesh_context_get_type();
+
+void sp_mesh_context_select_next (SPEventContext *event_context);
+void sp_mesh_context_select_prev (SPEventContext *event_context);
+static void sp_mesh_context_split_near_point (SPMeshContext *rc, SPItem *item, Geom::Point mouse_p, guint32 /*etime*/);
+#endif
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/preferences-skeleton.h b/src/preferences-skeleton.h
index 85c895dcb..2cd391150 100644
--- a/src/preferences-skeleton.h
+++ b/src/preferences-skeleton.h
@@ -120,6 +120,7 @@ static char const preferences_skeleton[] =
" <eventcontext id=\"tweak\" selcue=\"0\" gradientdrag=\"0\" show_handles=\"0\" width=\"0.2\" force=\"0.2\" fidelity=\"0.5\" usepressure=\"1\" style=\"fill:red;stroke:none;\" usecurrent=\"0\"/>\n"
" <eventcontext id=\"spray\" selcue=\"1\" gradientdrag=\"0\" usepressure=\"1\" width=\"15\" population=\"70\" mode=\"1\" rotation_variation=\"0\" scale_variation=\"0\" standard_deviation=\"70\" mean=\"0\"/>\n"
" <eventcontext id=\"gradient\" selcue=\"1\"/>\n"
+" <eventcontext id=\"mesh\" selcue=\"1\"/>\n"
" <eventcontext id=\"zoom\" selcue=\"1\" gradientdrag=\"0\"/>\n"
" <eventcontext id=\"dropper\" selcue=\"1\" gradientdrag=\"1\" pick=\"1\" setalpha=\"1\"/>\n"
" <eventcontext id=\"select\" selcue=\"1\" gradientdrag=\"0\"/>\n"
diff --git a/src/sp-gradient-fns.h b/src/sp-gradient-fns.h
index f408affd1..e57877256 100644
--- a/src/sp-gradient-fns.h
+++ b/src/sp-gradient-fns.h
@@ -12,6 +12,7 @@
#include "sp-gradient-units.h"
class SPGradient;
+class SPMeshGradient;
SPGradientSpread sp_gradient_get_spread (SPGradient *gradient);
@@ -19,6 +20,8 @@ SPGradientSpread sp_gradient_get_spread (SPGradient *gradient);
void sp_gradient_repr_write_vector(SPGradient *gr);
void sp_gradient_repr_clear_vector(SPGradient *gr);
+void sp_meshgradient_repr_write(SPMeshGradient *mg);
+
cairo_pattern_t *sp_gradient_create_preview_pattern(SPGradient *gradient, double width);
/** Transforms to/from gradient position space in given environment */
diff --git a/src/sp-gradient.cpp b/src/sp-gradient.cpp
index 1a99485f6..d86b73e78 100644
--- a/src/sp-gradient.cpp
+++ b/src/sp-gradient.cpp
@@ -1,5 +1,6 @@
/** \file
- * SPGradient, SPStop, SPLinearGradient, SPRadialGradient.
+ * SPGradient, SPStop, SPLinearGradient, SPRadialGradient,
+ * SPMeshGradient, SPMeshRow, SPMeshPatch
*/
/*
* Authors:
@@ -8,11 +9,13 @@
* Jasper van de Gronde <th.v.d.gronde@hccnet.nl>
* Jon A. Cruz <jon@joncruz.org>
* Abhishek Sharma
+ * Tavmjong Bah <tavmjong@free.fr>
*
* Copyright (C) 1999-2002 Lauris Kaplinski
* Copyright (C) 2000-2001 Ximian, Inc.
* Copyright (C) 2004 David Turner
* Copyright (C) 2009 Jasper van de Gronde
+ * Copyright (C) 2011 Tavmjong Bah
*
* Released under GNU GPL, read the file 'COPYING' for more information
*
@@ -25,6 +28,8 @@
#include <2geom/transforms.h>
+#include <cairo.h>
+
#include <sigc++/functors/ptr_fun.h>
#include <sigc++/adaptors/bind.h>
@@ -39,6 +44,9 @@
#include "sp-gradient-reference.h"
#include "sp-linear-gradient.h"
#include "sp-radial-gradient.h"
+#include "sp-mesh-gradient.h"
+#include "sp-mesh-row.h"
+#include "sp-mesh-patch.h"
#include "sp-stop.h"
#include "streq.h"
#include "uri.h"
@@ -49,9 +57,10 @@
#define SP_MACROS_SILENT
#include "macros.h"
-/// Has to be power of 2
-#define NCOLORS NR_GRADIENT_VECTOR_LENGTH
+/// Has to be power of 2 Seems to be unused.
+//#define NCOLORS NR_GRADIENT_VECTOR_LENGTH
+// SPStop
static void sp_stop_class_init(SPStopClass *klass);
static void sp_stop_init(SPStop *stop);
@@ -61,6 +70,29 @@ static Inkscape::XML::Node *sp_stop_write(SPObject *object, Inkscape::XML::Docum
static SPObjectClass *stop_parent_class;
+
+// SPMeshRow
+static void sp_meshrow_class_init(SPMeshRowClass *klass);
+static void sp_meshrow_init(SPMeshRow *meshrow);
+
+static void sp_meshrow_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
+static void sp_meshrow_set(SPObject *object, unsigned key, gchar const *value);
+static Inkscape::XML::Node *sp_meshrow_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
+
+static SPObjectClass *meshrow_parent_class;
+
+
+// SPMeshPatch
+static void sp_meshpatch_class_init(SPMeshPatchClass *klass);
+static void sp_meshpatch_init(SPMeshPatch *meshpatch);
+
+static void sp_meshpatch_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
+static void sp_meshpatch_set(SPObject *object, unsigned key, gchar const *value);
+static Inkscape::XML::Node *sp_meshpatch_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
+
+static SPObjectClass *meshpatch_parent_class;
+
+
class SPGradientImpl
{
friend class SPGradient;
@@ -145,6 +177,7 @@ static void sp_stop_build(SPObject *object, SPDocument *document, Inkscape::XML:
object->readAttr( "stop-color" );
object->readAttr( "stop-opacity" );
object->readAttr( "style" );
+ object->readAttr( "path" ); // For mesh
}
/**
@@ -210,6 +243,18 @@ sp_stop_set(SPObject *object, unsigned key, gchar const *value)
object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
break;
}
+ case SP_PROP_STOP_PATH: {
+ if (value) {
+ stop->path_string = new Glib::ustring( value );
+ //Geom::PathVector pv = sp_svg_read_pathv(value);
+ //SPCurve *curve = new SPCurve(pv);
+ //if( curve ) {
+ // std::cout << "Got Curve" << std::endl;
+ //curve->unref();
+ //}
+ }
+ break;
+ }
default: {
if (((SPObjectClass *) stop_parent_class)->set)
(* ((SPObjectClass *) stop_parent_class)->set)(object, key, value);
@@ -265,6 +310,11 @@ bool SPGradient::hasStops() const
return has_stops;
}
+bool SPGradient::hasPatches() const
+{
+ return has_patches;
+}
+
bool SPGradient::isUnitsSet() const
{
return units_set;
@@ -321,6 +371,198 @@ sp_stop_get_rgba32(SPStop const *const stop)
}
/*
+ * Mesh Row
+ */
+
+/**
+ * Registers SPMeshRow class and returns its type.
+ */
+GType
+sp_meshrow_get_type()
+{
+ static GType type = 0;
+ if (!type) {
+ GTypeInfo info = {
+ sizeof(SPMeshRowClass),
+ NULL, NULL,
+ (GClassInitFunc) sp_meshrow_class_init,
+ NULL, NULL,
+ sizeof(SPMeshRow),
+ 16,
+ (GInstanceInitFunc) sp_meshrow_init,
+ NULL, /* value_table */
+ };
+ type = g_type_register_static(SP_TYPE_OBJECT, "SPMeshRow", &info, (GTypeFlags)0);
+ }
+ return type;
+}
+
+/**
+ * Callback to initialize SPMeshRow vtable.
+ */
+static void sp_meshrow_class_init(SPMeshRowClass *klass)
+{
+ SPObjectClass *sp_object_class = (SPObjectClass *) klass;
+
+ meshrow_parent_class = (SPObjectClass *) g_type_class_ref(SP_TYPE_OBJECT);
+
+ sp_object_class->build = sp_meshrow_build;
+ sp_object_class->set = sp_meshrow_set;
+ sp_object_class->write = sp_meshrow_write;
+}
+
+/**
+ * Callback to initialize SPMeshRow object.
+ */
+static void
+sp_meshrow_init(SPMeshRow *meshrow)
+{
+ // Do nothing
+}
+
+/**
+ * Virtual build: set meshrow attributes from its associated XML node.
+ */
+static void sp_meshrow_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
+{
+ if (((SPObjectClass *) meshrow_parent_class)->build)
+ (* ((SPObjectClass *) meshrow_parent_class)->build)(object, document, repr);
+
+ // No attributes
+}
+
+/**
+ * Virtual set: set attribute to value.
+ */
+static void
+sp_meshrow_set(SPObject *object, unsigned key, gchar const *value)
+{
+ // Do nothing
+}
+
+/**
+ * Virtual write: write object attributes to repr.
+ */
+static Inkscape::XML::Node *
+sp_meshrow_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
+{
+ //SPMeshRow *meshrow = SP_MESHROW(object);
+
+ if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
+ repr = xml_doc->createElement("svg:meshRow");
+ }
+
+ if (((SPObjectClass *) meshrow_parent_class)->write) {
+ (* ((SPObjectClass *) meshrow_parent_class)->write)(object, xml_doc, repr, flags);
+ }
+
+ return repr;
+}
+
+/*
+ * Mesh Patch
+ */
+
+/**
+ * Registers SPMeshPatch class and returns its type.
+ */
+GType
+sp_meshpatch_get_type()
+{
+ static GType type = 0;
+ if (!type) {
+ GTypeInfo info = {
+ sizeof(SPMeshPatchClass),
+ NULL, NULL,
+ (GClassInitFunc) sp_meshpatch_class_init,
+ NULL, NULL,
+ sizeof(SPMeshPatch),
+ 16,
+ (GInstanceInitFunc) sp_meshpatch_init,
+ NULL, /* value_table */
+ };
+ type = g_type_register_static(SP_TYPE_OBJECT, "SPMeshPatch", &info, (GTypeFlags)0);
+ }
+ return type;
+}
+
+/**
+ * Callback to initialize SPMeshPatch vtable.
+ */
+static void sp_meshpatch_class_init(SPMeshPatchClass *klass)
+{
+ SPObjectClass *sp_object_class = (SPObjectClass *) klass;
+
+ meshpatch_parent_class = (SPObjectClass *) g_type_class_ref(SP_TYPE_OBJECT);
+
+ sp_object_class->build = sp_meshpatch_build;
+ sp_object_class->set = sp_meshpatch_set;
+ sp_object_class->write = sp_meshpatch_write;
+}
+
+/**
+ * Callback to initialize SPMeshPatch object.
+ */
+static void
+sp_meshpatch_init(SPMeshPatch *meshpatch)
+{
+ // Do nothing
+}
+
+/**
+ * Virtual build: set meshpatch attributes from its associated XML node.
+ */
+static void sp_meshpatch_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
+{
+ if (((SPObjectClass *) meshpatch_parent_class)->build)
+ (* ((SPObjectClass *) meshpatch_parent_class)->build)(object, document, repr);
+
+ object->readAttr( "tensor" );
+}
+
+/**
+ * Virtual set: set attribute to value.
+ */
+static void
+sp_meshpatch_set(SPObject *object, unsigned key, gchar const *value)
+{
+ SPMeshPatch *patch = SP_MESHPATCH(object);
+
+ switch (key) {
+ case SP_ATTR_TENSOR: {
+ if (value) {
+ patch->tensor_string = new Glib::ustring( value );
+ // std::cout << "sp_meshpatch_set: Tensor string: " << patch->tensor_string->c_str() << std::endl;
+ }
+ break;
+ }
+ default: {
+ // Do nothing
+ }
+ }
+}
+
+/**
+ * Virtual write: write object attributes to repr.
+ */
+static Inkscape::XML::Node *
+sp_meshpatch_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
+{
+ //SPMeshPatch *meshpatch = SP_MESHPATCH(object);
+
+ if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
+ repr = xml_doc->createElement("svg:meshPatch");
+ }
+
+ if (((SPObjectClass *) meshpatch_parent_class)->write) {
+ (* ((SPObjectClass *) meshpatch_parent_class)->write)(object, xml_doc, repr, flags);
+ }
+
+ return repr;
+}
+
+
+/*
* Gradient
*/
@@ -333,6 +575,7 @@ GType SPGradient::getType()
{
static GType gradient_type = 0;
if (!gradient_type) {
+
GTypeInfo gradient_info = {
sizeof(SPGradientClass),
NULL, NULL,
@@ -647,11 +890,19 @@ void SPGradientImpl::modified(SPObject *object, guint flags)
SPGradient *gr = SP_GRADIENT(object);
if (flags & SP_OBJECT_CHILD_MODIFIED_FLAG) {
- gr->invalidateVector();
+ if( gr->get_type() != SP_GRADIENT_TYPE_MESH ) {
+ gr->invalidateVector();
+ } else {
+ gr->invalidateArray();
+ }
}
if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
- gr->ensureVector();
+ if( gr->get_type() != SP_GRADIENT_TYPE_MESH ) {
+ gr->ensureVector();
+ } else {
+ gr->ensureArray();
+ }
}
if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
@@ -788,6 +1039,19 @@ void SPGradient::ensureVector()
}
/**
+ * Forces the array to be built, if not present (i.e., changed).
+ *
+ * \pre SP_IS_GRADIENT(gradient).
+ */
+void SPGradient::ensureArray()
+{
+ //std::cout << "SPGradient::ensureArray()" << std::endl;
+ if ( !array.built ) {
+ rebuildArray();
+ }
+}
+
+/**
* Set units property of gradient and emit modified.
*/
void SPGradient::setUnits(SPGradientUnits units)
@@ -1004,6 +1268,20 @@ bool SPGradient::invalidateVector()
return ret;
}
+/** Return true if change made. */
+bool SPGradient::invalidateArray()
+{
+ bool ret = false;
+
+ if (array.built) {
+ array.built = false;
+ array.clear();
+ ret = true;
+ }
+
+ return ret;
+}
+
/** Creates normalized color vector */
void SPGradient::rebuildVector()
{
@@ -1102,6 +1380,46 @@ void SPGradient::rebuildVector()
vector.built = true;
}
+/** Creates normalized color mesh patch array */
+void SPGradient::rebuildArray()
+{
+ // std::cout << "SPGradient::rebuildArray()" << std::endl;
+
+ if( !SP_IS_MESHGRADIENT(this) ) {
+ g_warning( "SPGradient::rebuildArray() called for non-mesh gradient" );
+ return;
+ }
+
+ array.read( SP_MESHGRADIENT( this ) );
+
+ has_patches = false;
+ for ( SPObject *ro = firstChild() ; ro ; ro = ro->getNext() ) {
+ if (SP_IS_MESHROW(ro)) {
+ has_patches = true;
+ // std::cout << " Has Patches" << std::endl;
+ break;
+ }
+ }
+
+ // MESH_FIXME: TO PROPERLY COPY
+ SPGradient *reffed = ref->getObject();
+ if ( !hasPatches() && reffed ) {
+ std::cout << "SPGradient::rebuildArray(): reffed array NOT IMPLEMENTED!!!" << std::endl;
+ /* Copy array from referenced gradient */
+ array.built = true; // Prevent infinite recursion.
+ reffed->ensureArray();
+ // if (!reffed->array.nodes.empty()) {
+ // array.built = reffed->array.built;
+ // for( uint i = 0; i < reffed->array.nodes.size(); ++i ) {
+ // array.nodes[i].assign(reffed->array.nodes[i].begin(), reffed->array.nodes[i].end());
+
+ // // FILL ME
+ // }
+ // return;
+ // }
+ }
+}
+
Geom::Affine
sp_gradient_get_g2d_matrix(SPGradient const *gr, Geom::Affine const &ctm, Geom::Rect const &bbox)
{
@@ -1489,6 +1807,161 @@ sp_radialgradient_set_position(SPRadialGradient *rg,
rg->requestModified(SP_OBJECT_MODIFIED_FLAG);
}
+/*
+ * Mesh Gradient
+ */
+
+//#define MESH_DEBUG
+
+static void sp_meshgradient_class_init(SPMeshGradientClass *klass);
+static void sp_meshgradient_init(SPMeshGradient *mg);
+
+static void sp_meshgradient_build(SPObject *object,
+ SPDocument *document,
+ Inkscape::XML::Node *repr);
+static void sp_meshgradient_set(SPObject *object, unsigned key, gchar const *value);
+static Inkscape::XML::Node *sp_meshgradient_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr,
+ guint flags);
+static cairo_pattern_t *sp_meshgradient_create_pattern(SPPaintServer *ps, cairo_t *ct, Geom::OptRect const &bbox, double opacity);
+
+static SPGradientClass *mg_parent_class;
+
+/**
+ * Register SPMeshGradient class and return its type.
+ */
+GType
+sp_meshgradient_get_type()
+{
+ static GType type = 0;
+ if (!type) {
+ GTypeInfo info = {
+ sizeof(SPMeshGradientClass),
+ NULL, NULL,
+ (GClassInitFunc) sp_meshgradient_class_init,
+ NULL, NULL,
+ sizeof(SPMeshGradient),
+ 16,
+ (GInstanceInitFunc) sp_meshgradient_init,
+ NULL, /* value_table */
+ };
+ type = g_type_register_static(SP_TYPE_GRADIENT, "SPMeshGradient", &info, (GTypeFlags)0);
+ }
+ return type;
+}
+
+/**
+ * SPMeshGradient vtable initialization.
+ */
+static void sp_meshgradient_class_init(SPMeshGradientClass *klass)
+{
+#ifdef MESH_DEBUG
+ std::cout << "sp_meshgradient_class_init()" << std::endl;
+#endif
+ SPObjectClass *sp_object_class = (SPObjectClass *) klass;
+ SPPaintServerClass *ps_class = (SPPaintServerClass *) klass;
+
+ mg_parent_class = (SPGradientClass*)g_type_class_ref(SP_TYPE_GRADIENT);
+
+ sp_object_class->build = sp_meshgradient_build;
+ sp_object_class->set = sp_meshgradient_set;
+ sp_object_class->write = sp_meshgradient_write;
+
+ ps_class->pattern_new = sp_meshgradient_create_pattern;
+}
+
+/**
+ * Callback for SPMeshGradient object initialization.
+ */
+static void
+sp_meshgradient_init(SPMeshGradient *mg)
+{
+ // Start coordinate of mesh
+ mg->x.unset(SVGLength::NONE, 0.0, 0.0);
+ mg->y.unset(SVGLength::NONE, 0.0, 0.0);
+}
+
+/**
+ * Set mesh gradient attributes from associated repr.
+ */
+static void
+sp_meshgradient_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
+{
+ if (((SPObjectClass *) mg_parent_class)->build)
+ (* ((SPObjectClass *) mg_parent_class)->build)(object, document, repr);
+
+ // Start coordinate of mesh
+ object->readAttr( "x" );
+ object->readAttr( "y" );
+}
+
+/**
+ * Set mesh gradient attribute.
+ */
+static void
+sp_meshgradient_set(SPObject *object, unsigned key, gchar const *value)
+{
+ SPMeshGradient *mg = SP_MESHGRADIENT(object);
+
+ switch (key) {
+ case SP_ATTR_X:
+ if (!mg->x.read(value)) {
+ mg->x.unset(SVGLength::NONE, 0.0, 0.0);
+ }
+ object->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ break;
+ case SP_ATTR_Y:
+ if (!mg->y.read(value)) {
+ mg->y.unset(SVGLength::NONE, 0.0, 0.0);
+ }
+ object->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ break;
+ default:
+ if (((SPObjectClass *) mg_parent_class)->set)
+ ((SPObjectClass *) mg_parent_class)->set(object, key, value);
+ break;
+ }
+}
+
+/**
+ * Write mesh gradient attributes to associated repr.
+ */
+static Inkscape::XML::Node *
+sp_meshgradient_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
+{
+
+#ifdef MESH_DEBUG
+ std::cout << "sp_meshgradient_write() ***************************" << std::endl;
+#endif
+ SPMeshGradient *mg = SP_MESHGRADIENT(object);
+
+ if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
+ repr = xml_doc->createElement("svg:meshGradient");
+ }
+
+ if ((flags & SP_OBJECT_WRITE_ALL) || mg->x._set) sp_repr_set_svg_double(repr, "x", mg->x.computed);
+ if ((flags & SP_OBJECT_WRITE_ALL) || mg->y._set) sp_repr_set_svg_double(repr, "y", mg->y.computed);
+
+ if (((SPObjectClass *) mg_parent_class)->write)
+ (* ((SPObjectClass *) mg_parent_class)->write)(object, xml_doc, repr, flags);
+
+ return repr;
+}
+
+/**
+ * Directly set properties of mesh gradient and request modified.
+ */
+void
+sp_meshgradient_set_position(SPMeshGradient *mg, gdouble x, gdouble y)
+{
+ g_return_if_fail(mg != NULL);
+ g_return_if_fail(SP_IS_MESHGRADIENT(mg));
+
+ mg->x.set(SVGLength::NONE, x, x);
+ mg->y.set(SVGLength::NONE, y, y);
+
+ mg->requestModified(SP_OBJECT_MODIFIED_FLAG);
+}
+
/* CAIRO RENDERING STUFF */
static void
@@ -1571,6 +2044,126 @@ sp_radialgradient_create_pattern(SPPaintServer *ps,
}
static cairo_pattern_t *
+sp_meshgradient_create_pattern(SPPaintServer *ps,
+ cairo_t */* ct */,
+ Geom::OptRect const &bbox,
+ double opacity)
+{
+
+ using Geom::X;
+ using Geom::Y;
+
+#ifdef MESH_DEBUG
+ std::cout << "sp_meshgradient_create_pattern: (" << bbox->x0 << "," << bbox->y0 << ") (" << bbox->x1 << "," << bbox->y1 << ") " << opacity << std::endl;
+#endif
+ //SPMeshGradient *mg = SP_MESHGRADIENT(ps);
+ SPGradient *gr = SP_GRADIENT(ps);
+
+ gr->ensureArray();
+
+ SPMeshNodeArray* array = &(gr->array);
+
+ cairo_pattern_t *cp = NULL;
+
+#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 11, 4)
+
+ cp = cairo_pattern_create_mesh();
+
+ for( uint i = 0; i < array->patch_rows(); ++i ) {
+ for( uint j = 0; j < array->patch_columns(); ++j ) {
+
+ SPMeshPatchI patch( &(array->nodes), i, j );
+
+ cairo_mesh_pattern_begin_patch( cp );
+ cairo_mesh_pattern_move_to( cp, patch.getPoint( 0, 0 )[X], patch.getPoint( 0, 0 )[Y] );
+
+ for( uint k = 0; k < 4; ++k ) {
+#ifdef DEBUG_MESH
+ std::cout << i << " " << j << " "
+ << patch.getPathType( k ) << " (";
+ for( int p = 0; p < 4; ++p ) {
+ std::cout << patch.getPoint( k, p );
+ }
+ std::cout << ") "
+ << patch.getColor( k ).toString() << std::endl;
+#endif
+
+ switch ( patch.getPathType( k ) ) {
+ case 'l':
+ case 'L':
+ case 'z':
+ case 'Z':
+ cairo_mesh_pattern_line_to( cp,
+ patch.getPoint( k, 3 )[X],
+ patch.getPoint( k, 3 )[Y] );
+ break;
+ case 'c':
+ case 'C':
+ {
+ std::vector< Geom::Point > pts = patch.getPointsForSide( k );
+ cairo_mesh_pattern_curve_to( cp,
+ pts[1][X], pts[1][Y],
+ pts[2][X], pts[2][Y],
+ pts[3][X], pts[3][Y] );
+ break;
+ }
+ default:
+ // Shouldn't happen
+ std::cout << "sp_meshgradient_create_pattern: path error" << std::endl;
+ }
+
+ if( patch.tensorIsSet(k) ) {
+ // Tensor point defined relative to corner.
+ Geom::Point t = patch.getTensorPoint(k);
+ cairo_mesh_pattern_set_control_point( cp, k, t[X], t[Y] );
+ //std::cout << " sp_meshgradient_create_pattern: tensor " << k
+ // << " set to " << t << "." << std::endl;
+ } else {
+ // Geom::Point t = patch.coonsTensorPoint(k);
+ //std::cout << " sp_meshgradient_create_pattern: tensor " << k
+ // << " calculated as " << t << "." <<std::endl;
+ }
+
+ cairo_mesh_pattern_set_corner_color_rgba(
+ cp, k,
+ patch.getColor( k ).v.c[0],
+ patch.getColor( k ).v.c[1],
+ patch.getColor( k ).v.c[2],
+ patch.getOpacity( k ) * opacity );
+ }
+
+ cairo_mesh_pattern_end_patch( cp );
+ }
+ }
+
+ // set pattern matrix
+ Geom::Affine gs2user = gr->gradientTransform;
+ if (gr->getUnits() == SP_GRADIENT_UNITS_OBJECTBOUNDINGBOX) {
+ Geom::Affine bbox2user(bbox->width(), 0, 0, bbox->height(), bbox->left(), bbox->top());
+ gs2user *= bbox2user;
+ }
+ ink_cairo_pattern_set_matrix(cp, gs2user.inverse());
+
+#else
+ static bool shown = false;
+ if( !shown ) {
+ std::cout << "sp_meshgradient_create_pattern: needs cairo >= 1.11.4, using "
+ << cairo_version_string() << std::endl;
+ shown = true;
+ }
+#endif
+
+/*
+ cairo_pattern_t *cp = cairo_pattern_create_radial(
+ rg->fx.computed, rg->fy.computed, 0,
+ rg->cx.computed, rg->cy.computed, rg->r.computed);
+ sp_gradient_pattern_common_setup(cp, gr, bbox, opacity);
+*/
+
+ return cp;
+}
+
+static cairo_pattern_t *
sp_lineargradient_create_pattern(SPPaintServer *ps,
cairo_t */* ct */,
Geom::OptRect const &bbox,
@@ -1593,20 +2186,31 @@ sp_lineargradient_create_pattern(SPPaintServer *ps,
cairo_pattern_t *
sp_gradient_create_preview_pattern(SPGradient *gr, double width)
{
- gr->ensureVector();
+ cairo_pattern_t *pat = NULL;
- cairo_pattern_t *pat = cairo_pattern_create_linear(0, 0, width, 0);
+ if( gr->get_type() != SP_GRADIENT_TYPE_MESH ) {
- for (std::vector<SPGradientStop>::iterator i = gr->vector.stops.begin();
- i != gr->vector.stops.end(); ++i)
- {
- cairo_pattern_add_color_stop_rgba(pat, i->offset,
- i->color.v.c[0], i->color.v.c[1], i->color.v.c[2], i->opacity);
+ gr->ensureVector();
+
+ pat = cairo_pattern_create_linear(0, 0, width, 0);
+
+ for (std::vector<SPGradientStop>::iterator i = gr->vector.stops.begin();
+ i != gr->vector.stops.end(); ++i)
+ {
+ cairo_pattern_add_color_stop_rgba(pat, i->offset,
+ i->color.v.c[0], i->color.v.c[1], i->color.v.c[2], i->opacity);
+ }
}
return pat;
}
+void
+sp_meshgradient_repr_write(SPMeshGradient *mg)
+{
+ mg->array.write( mg );
+}
+
/*
Local Variables:
mode:c++
diff --git a/src/sp-gradient.h b/src/sp-gradient.h
index 9322b3f43..ba9c32d17 100644
--- a/src/sp-gradient.h
+++ b/src/sp-gradient.h
@@ -22,6 +22,7 @@
#include "sp-gradient-spread.h"
#include "sp-gradient-units.h"
#include "sp-gradient-vector.h"
+#include "sp-mesh-array.h"
#include <stddef.h>
#include <sigc++/connection.h>
@@ -38,7 +39,14 @@ class SPStop;
enum SPGradientType {
SP_GRADIENT_TYPE_UNKNOWN,
SP_GRADIENT_TYPE_LINEAR,
- SP_GRADIENT_TYPE_RADIAL
+ SP_GRADIENT_TYPE_RADIAL,
+ SP_GRADIENT_TYPE_MESH
+};
+
+enum SPGradientMeshType {
+ SP_GRADIENT_MESH_TYPE_UNKNOWN,
+ SP_GRADIENT_MESH_TYPE_NORMAL,
+ SP_GRADIENT_MESH_TYPE_CONICAL
};
enum SPGradientState {
@@ -57,6 +65,9 @@ enum GrPointType {
POINT_RG_FOCUS,
POINT_RG_MID1,
POINT_RG_MID2,
+ POINT_MG_CORNER,
+ POINT_MG_HANDLE,
+ POINT_MG_TENSOR,
// insert new point types here.
POINT_G_INVALID
@@ -108,8 +119,14 @@ private:
/** Gradient stops */
guint has_stops : 1;
+
+ /** Gradient patches */
+ guint has_patches : 1;
+
public:
+ /** Linear and Radial Gradients */
+
/** Composed vector */
SPGradientVector vector;
@@ -121,6 +138,15 @@ public:
int getStopCount() const;
+ /** Mesh Gradients **************/
+
+ /** Composed array (for mesh gradients) */
+ SPMeshNodeArray array;
+
+ bool hasPatches() const;
+
+
+ /** All Gradients **************/
bool isUnitsSet() const;
SPGradientUnits getUnits() const;
void setUnits(SPGradientUnits units);
@@ -143,6 +169,9 @@ public:
/** Forces vector to be built, if not present (i.e. changed) */
void ensureVector();
+ /** Forces array (mesh) to be built, if not present (i.e. changed) */
+ void ensureArray();
+
/** Ensures that color array is populated */
void ensureColors();
@@ -158,11 +187,13 @@ public:
private:
bool invalidateVector();
+ bool invalidateArray();
void rebuildVector();
+ void rebuildArray();
friend class SPGradientImpl;
- friend class SPLGPainter;
- friend class SPRGPainter;
+// friend class SPLGPainter;
+// friend class SPRGPainter;
};
/**
diff --git a/src/sp-mesh-array.cpp b/src/sp-mesh-array.cpp
new file mode 100644
index 000000000..cc439395b
--- /dev/null
+++ b/src/sp-mesh-array.cpp
@@ -0,0 +1,2508 @@
+/** \file
+ A group of classes and functions for manipulating mesh gradients.
+
+ A mesh is made up of an array of patches. Each patch has four sides and four corners. The sides can
+ be shared between two patches and the corners between up to four.
+
+ The order of the points for each side always goes from left to right or top to bottom.
+ For sides 2 and 3 the points must be reversed when used (as in calls to cairo functions).
+
+ Two patches: (C=corner, S=side, H=handle, T=tensor)
+
+ C0 H1 H2 C1 C0 H1 H2 C1
+ + ---------- + ---------- +
+ | S0 | S0 |
+ H1 | T0 T1 |H1 T0 T1 | H1
+ |S3 S1|S3 S1|
+ H2 | T3 T2 |H2 T3 T2 | H2
+ | S2 | S2 |
+ + ---------- + ---------- +
+ C3 H1 H2 C2 C3 H1 H2 C2
+
+ The mesh is stored internally as an array of nodes that includes the tensor nodes.
+
+ Note: This code uses tensor points which are not part of the SVG2 plan at the moment.
+ Including tensor points was motivated by a desire to experiment with their usefulness
+ in smoothing color transitions. There doesn't seem to be much advantage for that
+ purpose. However including them internally allows for storing all the points in
+ an array which simplifies things like inserting new rows or columns.
+*/
+
+/*
+ * Authors:
+ * Tavmjong Bah <tavmjong@free.fr>
+ *
+ * Copyrigt (C) 2012 Tavmjong Bah
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "sp-mesh-array.h"
+#include "sp-mesh-gradient.h"
+#include "sp-mesh-row.h"
+#include "sp-mesh-patch.h"
+#include "sp-stop.h"
+
+// For new mesh creation
+#include "preferences.h"
+#include "sp-ellipse.h"
+#include "sp-star.h"
+
+// For writing color/opacity to style
+#include "svg/css-ostringstream.h"
+
+// For color picking
+#include "display/drawing.h"
+#include "display/drawing-context.h"
+#include "display/cairo-utils.h"
+#include "document.h"
+#include "sp-root.h"
+
+// For default color
+#include "style.h"
+#include "svg/svg-color.h"
+
+
+// Includes bezier-curve.h, ray.h, crossing.h
+#include "2geom/line.h"
+
+#include "xml/repr.h"
+#include <cmath>
+#include <algorithm>
+
+enum { ROW, COL };
+
+void swap_p( Geom::Point *p1, Geom::Point *p2 ) {
+ Geom::Point temp = *p1;
+ *p1 = *p2;
+ *p2 = temp;
+};
+
+SPMeshPatchI::SPMeshPatchI( std::vector<std::vector< SPMeshNode* > > * n, int r, int c ) {
+
+ nodes = n;
+ row = r*3; // Convert from patch array to node array
+ col = c*3;
+
+ uint i = 0;
+ if( row != 0 ) i = 1;
+ for( ; i < 4; ++i ) {
+ if( nodes->size() < row+i+1 ) {
+ std::vector< SPMeshNode* > row;
+ nodes->push_back( row );
+ }
+
+ uint j = 0;
+ if( col != 0 ) j = 1;
+ for( ; j < 4; ++j ) {
+ if( (*nodes)[row+i].size() < col+j+1 ){
+ SPMeshNode* node = new SPMeshNode;
+ // Ensure all nodes know their type.
+ node->node_type = MG_NODE_TYPE_HANDLE;
+ if( (i == 0 || i == 3) && (j == 0 || j == 3 ) ) node->node_type = MG_NODE_TYPE_CORNER;
+ if( (i == 1 || i == 2) && (j == 1 || j == 2 ) ) node->node_type = MG_NODE_TYPE_TENSOR;
+ (*nodes)[row+i].push_back( node );
+ }
+ }
+ }
+}
+
+/**
+ Returns point for side in proper order for patch
+*/
+Geom::Point SPMeshPatchI::getPoint( uint s, uint pt ) {
+
+ assert( s < 4 );
+ assert( pt < 4 );
+
+ Geom::Point p;
+ switch ( s ) {
+ case 0:
+ p = (*nodes)[ row ][ col+pt ]->p;
+ break;
+ case 1:
+ p = (*nodes)[ row+pt ][ col+3 ]->p;
+ break;
+ case 2:
+ p = (*nodes)[ row+3 ][ col+3-pt ]->p;
+ break;
+ case 3:
+ p = (*nodes)[ row+3-pt ][ col ]->p;
+ break;
+ }
+ return p;
+
+};
+
+/**
+ Returns vector of points for a side in proper order for a patch (clockwise order).
+*/
+std::vector< Geom::Point > SPMeshPatchI::getPointsForSide( uint i ) {
+
+ assert( i < 4 );
+
+ std::vector< Geom::Point> points;
+ points.push_back( getPoint( i, 0 ) );
+ points.push_back( getPoint( i, 1 ) );
+ points.push_back( getPoint( i, 2 ) );
+ points.push_back( getPoint( i, 3 ) );
+ return points;
+};
+
+
+/**
+ Set point for side in proper order for patch
+*/
+void SPMeshPatchI::setPoint( uint s, uint pt, Geom::Point p, bool set ) {
+
+ assert( s < 4 );
+ assert( pt < 4 );
+
+ NodeType node_type = MG_NODE_TYPE_CORNER;
+ if( pt == 1 || pt == 2 ) node_type = MG_NODE_TYPE_HANDLE;
+
+ // std::cout << "SPMeshPatchI::setPoint: s: " << s
+ // << " pt: " << pt
+ // << " p: " << p
+ // << " node_type: " << node_type
+ // << " set: " << set
+ // << " row: " << row
+ // << " col: " << col << std::endl;
+ switch ( s ) {
+ case 0:
+ (*nodes)[ row ][ col+pt ]->p = p;
+ (*nodes)[ row ][ col+pt ]->set = set;
+ (*nodes)[ row ][ col+pt ]->node_type = node_type;
+ break;
+ case 1:
+ (*nodes)[ row+pt ][ col+3 ]->p = p;
+ (*nodes)[ row+pt ][ col+3 ]->set = set;
+ (*nodes)[ row+pt ][ col+3 ]->node_type = node_type;
+ break;
+ case 2:
+ (*nodes)[ row+3 ][ col+3-pt ]->p = p;
+ (*nodes)[ row+3 ][ col+3-pt ]->set = set;
+ (*nodes)[ row+3 ][ col+3-pt ]->node_type = node_type;
+ break;
+ case 3:
+ (*nodes)[ row+3-pt ][ col ]->p = p;
+ (*nodes)[ row+3-pt ][ col ]->set = set;
+ (*nodes)[ row+3-pt ][ col ]->node_type = node_type;
+ break;
+ }
+
+};
+
+/**
+ Get path type for side (stored in handle nodes).
+*/
+gchar SPMeshPatchI::getPathType( uint s ) {
+
+ assert( s < 4 );
+
+ gchar type = 'x';
+
+ switch ( s ) {
+ case 0:
+ type = (*nodes)[ row ][ col+1 ]->path_type;
+ break;
+ case 1:
+ type = (*nodes)[ row+1 ][ col+3 ]->path_type;
+ break;
+ case 2:
+ type = (*nodes)[ row+3 ][ col+2 ]->path_type;
+ break;
+ case 3:
+ type = (*nodes)[ row+2 ][ col ]->path_type;
+ break;
+ }
+
+ return type;
+};
+
+/**
+ Set path type for side (stored in handle nodes).
+*/
+void SPMeshPatchI::setPathType( uint s, gchar t ) {
+
+ assert( s < 4 );
+
+ switch ( s ) {
+ case 0:
+ (*nodes)[ row ][ col+1 ]->path_type = t;
+ (*nodes)[ row ][ col+2 ]->path_type = t;
+ break;
+ case 1:
+ (*nodes)[ row+1 ][ col+3 ]->path_type = t;
+ (*nodes)[ row+2 ][ col+3 ]->path_type = t;
+ break;
+ case 2:
+ (*nodes)[ row+3 ][ col+1 ]->path_type = t;
+ (*nodes)[ row+3 ][ col+2 ]->path_type = t;
+ break;
+ case 3:
+ (*nodes)[ row+1 ][ col ]->path_type = t;
+ (*nodes)[ row+2 ][ col ]->path_type = t;
+ break;
+ }
+
+};
+
+/**
+ Set tensor control point for "corner" i.
+ */
+void SPMeshPatchI::setTensorPoint( uint i, Geom::Point p ) {
+
+ assert( i < 4 );
+ switch ( i ) {
+ case 0:
+ (*nodes)[ row + 1 ][ col + 1 ]->p = p;
+ (*nodes)[ row + 1 ][ col + 1 ]->set = true;
+ (*nodes)[ row + 1 ][ col + 1 ]->node_type = MG_NODE_TYPE_TENSOR;
+ break;
+ case 1:
+ (*nodes)[ row + 1 ][ col + 2 ]->p = p;
+ (*nodes)[ row + 1 ][ col + 2 ]->set = true;
+ (*nodes)[ row + 1 ][ col + 2 ]->node_type = MG_NODE_TYPE_TENSOR;
+ break;
+ case 2:
+ (*nodes)[ row + 2 ][ col + 2 ]->p = p;
+ (*nodes)[ row + 2 ][ col + 2 ]->set = true;
+ (*nodes)[ row + 2 ][ col + 2 ]->node_type = MG_NODE_TYPE_TENSOR;
+ break;
+ case 3:
+ (*nodes)[ row + 2 ][ col + 1 ]->p = p;
+ (*nodes)[ row + 2 ][ col + 1 ]->set = true;
+ (*nodes)[ row + 2 ][ col + 1 ]->node_type = MG_NODE_TYPE_TENSOR;
+ break;
+ }
+}
+
+/**
+ Return if any tensor control point is set.
+ */
+bool SPMeshPatchI::tensorIsSet() {
+ for( uint i = 0; i < 4; ++i ) {
+ if( tensorIsSet( i ) ) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ Return if tensor control point for "corner" i is set.
+ */
+bool SPMeshPatchI::tensorIsSet( uint i ) {
+
+ assert( i < 4 );
+
+ bool set = false;
+ switch ( i ) {
+ case 0:
+ set = (*nodes)[ row + 1 ][ col + 1 ]->set;
+ break;
+ case 1:
+ set = (*nodes)[ row + 1 ][ col + 2 ]->set;
+ break;
+ case 2:
+ set = (*nodes)[ row + 2 ][ col + 2 ]->set;
+ break;
+ case 3:
+ set = (*nodes)[ row + 2 ][ col + 1 ]->set;
+ break;
+ }
+ return set;
+}
+
+/**
+ Return tensor control point for "corner" i.
+ If not sest, returns calculated (Coons) point.
+ */
+Geom::Point SPMeshPatchI::getTensorPoint( uint k ) {
+
+ assert( k < 4 );
+
+ uint i, j;
+
+
+ switch ( k ) {
+ case 0:
+ i = 1; j = 1;
+ break;
+ case 1:
+ i = 1; j = 2;
+ break;
+ case 2:
+ i = 2; j = 2;
+ break;
+ case 3:
+ i = 2; j = 1;
+ break;
+ }
+
+ Geom::Point p;
+ if( (*nodes)[ row + i ][ col + j ]->set ) {
+ p = (*nodes)[ row + i ][ col + j ]->p;
+ } else {
+ p = coonsTensorPoint( k );
+ }
+ return p;
+
+}
+
+/**
+ Find default tensor point (equivalent point to Coons Patch).
+ Formulas defined in PDF spec.
+ Equivalent to 1/3 of side length from corner for square patch.
+ */
+Geom::Point SPMeshPatchI::coonsTensorPoint( uint i ) {
+
+ Geom::Point t;
+ Geom::Point p[4][4]; // Points in PDF notation
+
+ p[0][0] = getPoint( 0, 0 );
+ p[0][1] = getPoint( 0, 1 );
+ p[0][2] = getPoint( 0, 2 );
+ p[0][3] = getPoint( 0, 3 );
+ p[1][0] = getPoint( 3, 2 );
+ p[1][3] = getPoint( 1, 1 );
+ p[2][0] = getPoint( 3, 1 );
+ p[2][3] = getPoint( 1, 2 );
+ p[3][0] = getPoint( 2, 3 );
+ p[3][1] = getPoint( 2, 2 );
+ p[3][2] = getPoint( 2, 1 );
+ p[3][3] = getPoint( 2, 0 );
+
+ switch ( i ) {
+ case 0:
+ t = ( -4.0 * p[0][0] +
+ 6.0 * ( p[0][1] + p[1][0] ) +
+ -2.0 * ( p[0][3] + p[3][0] ) +
+ 3.0 * ( p[3][1] + p[1][3] ) +
+ -1.0 * p[3][3] ) / 9.0;
+ break;
+
+ case 1:
+ t = ( -4.0 * p[0][3] +
+ 6.0 * ( p[0][2] + p[1][3] ) +
+ -2.0 * ( p[0][0] + p[3][3] ) +
+ 3.0 * ( p[3][2] + p[1][0] ) +
+ -1.0 * p[3][0] ) / 9.0;
+ break;
+
+ case 2:
+ t = ( -4.0 * p[3][3] +
+ 6.0 * ( p[3][2] + p[2][3] ) +
+ -2.0 * ( p[3][0] + p[0][3] ) +
+ 3.0 * ( p[0][2] + p[2][0] ) +
+ -1.0 * p[0][0] ) / 9.0;
+ break;
+
+ case 3:
+ t = ( -4.0 * p[3][0] +
+ 6.0 * ( p[3][1] + p[2][0] ) +
+ -2.0 * ( p[3][3] + p[0][0] ) +
+ 3.0 * ( p[0][1] + p[2][3] ) +
+ -1.0 * p[0][3] ) / 9.0;
+ break;
+
+ default:
+
+ g_warning( "Impossible!" );
+
+ }
+ return t;
+}
+
+/**
+ Update default values for handle and tensor nodes.
+*/
+void SPMeshPatchI::updateNodes() {
+
+ // std::cout << "SPMeshPatchI::updateNodes: " << row << "," << col << std::endl;
+ // Handles first (tensors require update handles).
+ for( uint i = 0; i < 4; ++i ) {
+ for( uint j = 0; j < 4; ++j ) {
+ if( (*nodes)[ row + i ][ col + j ]->set == false ) {
+
+ if( (*nodes)[ row + i ][ col + j ]->node_type == MG_NODE_TYPE_HANDLE ) {
+
+ // If a handle is not set it is because the side is a line.
+ // Set node points 1/3 of the way between corners.
+
+ if( i == 0 || i == 3 ) {
+ Geom::Point p0 = ( (*nodes)[ row + i ][ col ]->p );
+ Geom::Point p3 = ( (*nodes)[ row + i ][ col + 3 ]->p );
+ Geom::Point dp = (p3 - p0)/3.0;
+ if( j == 2 ) dp *= 2.0;
+ (*nodes)[ row + i ][ col + j ]->p = p0 + dp;
+ }
+
+ if( j == 0 || j == 3 ) {
+ Geom::Point p0 = ( (*nodes)[ row ][ col + j ]->p );
+ Geom::Point p3 = ( (*nodes)[ row + 3 ][ col + j ]->p );
+ Geom::Point dp = (p3 - p0)/3.0;
+ if( i == 2 ) dp *= 2.0;
+ (*nodes)[ row + i ][ col + j ]->p = p0 + dp;
+ }
+ }
+ }
+ }
+ }
+
+ // Update tensor nodes
+ for( uint i = 1; i < 3; ++i ) {
+ for( uint j = 1; j < 3; ++j ) {
+ if( (*nodes)[ row + i ][ col + j ]->set == false ) {
+
+ (*nodes)[ row + i ][ col + j ]->node_type = MG_NODE_TYPE_TENSOR;
+
+ uint t = 0;
+ if( i == 1 && j == 2 ) t = 1;
+ if( i == 2 && j == 2 ) t = 2;
+ if( i == 2 && j == 1 ) t = 3;
+ (*nodes)[ row + i ][ col + j ]->p = coonsTensorPoint( t );
+ // std::cout << "Update node: " << i << ", " << j << " " << coonsTensorPoint( t ) << std::endl;
+
+ }
+ }
+ }
+}
+
+/**
+ Return color for corner of patch.
+*/
+SPColor SPMeshPatchI::getColor( uint i ) {
+
+ assert( i < 4 );
+
+ SPColor color;
+ switch ( i ) {
+ case 0:
+ color = (*nodes)[ row ][ col ]->color;
+ break;
+ case 1:
+ color = (*nodes)[ row ][ col+3 ]->color;
+ break;
+ case 2:
+ color = (*nodes)[ row+3 ][ col+3 ]->color;
+ break;
+ case 3:
+ color = (*nodes)[ row+3 ][ col ]->color;
+ break;
+
+ }
+
+ return color;
+
+};
+
+/**
+ Set color for corner of patch.
+*/
+void SPMeshPatchI::setColor( uint i, SPColor color ) {
+
+ assert( i < 4 );
+
+ switch ( i ) {
+ case 0:
+ (*nodes)[ row ][ col ]->color = color;
+ break;
+ case 1:
+ (*nodes)[ row ][ col+3 ]->color = color;
+ break;
+ case 2:
+ (*nodes)[ row+3 ][ col+3 ]->color = color;
+ break;
+ case 3:
+ (*nodes)[ row+3 ][ col ]->color = color;
+ break;
+ }
+};
+
+/**
+ Return opacity for corner of patch.
+*/
+gdouble SPMeshPatchI::getOpacity( uint i ) {
+
+ assert( i < 4 );
+
+ gdouble opacity;
+ switch ( i ) {
+ case 0:
+ opacity = (*nodes)[ row ][ col ]->opacity;
+ break;
+ case 1:
+ opacity = (*nodes)[ row ][ col+3 ]->opacity;
+ break;
+ case 2:
+ opacity = (*nodes)[ row+3 ][ col+3 ]->opacity;
+ break;
+ case 3:
+ opacity = (*nodes)[ row+3 ][ col ]->opacity;
+ break;
+ }
+
+ return opacity;
+};
+
+
+/**
+ Set opacity for corner of patch.
+*/
+void SPMeshPatchI::setOpacity( uint i, gdouble opacity ) {
+
+ assert( i < 4 );
+
+ switch ( i ) {
+ case 0:
+ (*nodes)[ row ][ col ]->opacity = opacity;
+ break;
+ case 1:
+ (*nodes)[ row ][ col+3 ]->opacity = opacity;
+ break;
+ case 2:
+ (*nodes)[ row+3 ][ col+3 ]->opacity = opacity;
+ break;
+ case 3:
+ (*nodes)[ row+3 ][ col ]->opacity = opacity;
+ break;
+
+ }
+
+};
+
+
+SPMeshNodeArray::SPMeshNodeArray( SPMeshGradient *mg ) {
+
+ read( mg );
+
+};
+
+void SPMeshNodeArray::read( SPMeshGradient *mg_in ) {
+
+ mg = mg_in;
+
+ clear();
+
+ Geom::Point current_p( mg->x.computed, mg->y.computed );
+ // std::cout << "SPMeshNodeArray::read: p: " << current_p << std::endl;
+
+ uint max_column = 0;
+ uint irow = 0; // Corresponds to top of patch being read in.
+ for ( SPObject *ro = mg->firstChild() ; ro ; ro = ro->getNext() ) {
+
+ if (SP_IS_MESHROW(ro)) {
+
+ uint icolumn = 0; // Corresponds to left of patch being read in.
+ for ( SPObject *po = ro->firstChild() ; po ; po = po->getNext() ) {
+
+ if (SP_IS_MESHPATCH(po)) {
+
+ SPMeshPatch *patch = SP_MESHPATCH(po);
+
+ // std::cout << "SPMeshNodeArray::read: row size: " << nodes.size() << std::endl;
+ SPMeshPatchI new_patch( &nodes, irow, icolumn ); // Adds new nodes.
+ // std::cout << " after: " << nodes.size() << std::endl;
+
+ gint istop = 0;
+
+ // Only 'top' side defined for first row.
+ if( irow != 0 ) ++istop;
+
+ for ( SPObject *so = po->firstChild() ; so ; so = so->getNext() ) {
+ if (SP_IS_STOP(so)) {
+
+ if( istop > 3 ) {
+ // std::cout << " Mesh Gradient: Too many stops: " << istop << std::endl;
+ break;
+ }
+
+ SPStop *stop = SP_STOP(so);
+
+ // Handle top of first row.
+ if( istop == 0 && icolumn == 0 ) {
+ // First patch in mesh.
+ new_patch.setPoint( 0, 0, current_p );
+ }
+ // First point is always already defined by previous side (stop).
+ current_p = new_patch.getPoint( istop, 0 );
+
+ // If side closes patch, then we read one less point.
+ bool closed = false;
+ if( icolumn == 0 && istop == 3 ) closed = true;
+ if( icolumn > 0 && istop == 2 ) closed = true;
+
+
+ // Copy path and then replace commas by spaces so we can use stringstream to parse
+ std::string path_string = *(stop->path_string);
+ std::replace(path_string.begin(),path_string.end(),',',' ');
+
+ // std::cout << " path_string: " << path_string << std::endl;
+ // std::cout << " current_p: " << current_p << std::endl;
+
+ std::stringstream os( path_string );
+
+ // Determine type of path
+ char path_type;
+ os >> path_type;
+ new_patch.setPathType( istop, path_type );
+
+ gdouble x, y;
+ Geom::Point p, dp;
+ uint max;
+ switch ( path_type ) {
+ case 'l':
+ if( !closed ) {
+ os >> x >> y;
+ if( !os.fail() ) {
+ dp = Geom::Point( x, y );
+ new_patch.setPoint( istop, 3, current_p + dp );
+ } else {
+ std::cout << "Failed to read l" << std::endl;
+ }
+ }
+ // To facilitate some side operations, set handles to 1/3 and
+ // 2/3 distance between corner points but flag as unset.
+ p = new_patch.getPoint( istop, 3 );
+ dp = (p - current_p)/3.0; // Calculate since may not be set if closed.
+ // std::cout << " istop: " << istop
+ // << " dp: " << dp
+ // << " p: " << p
+ // << " current_p: " << current_p
+ // << std::endl;
+ new_patch.setPoint( istop, 1, current_p + dp, false );
+ new_patch.setPoint( istop, 2, current_p + 2.0 * dp, false );
+ break;
+ case 'L':
+ if( !closed ) {
+ os >> x >> y;
+ if( !os.fail() ) {
+ p = Geom::Point( x, y );
+ new_patch.setPoint( istop, 3, p );
+ } else {
+ std::cout << "Failed to read L" << std::endl;
+ }
+ }
+ // To facilitate some side operations, set handles to 1/3 and
+ // 2/3 distance between corner points but flag as unset.
+ p = new_patch.getPoint( istop, 3 );
+ dp = (p - current_p)/3.0;
+ new_patch.setPoint( istop, 1, current_p + dp, false );
+ new_patch.setPoint( istop, 2, current_p + 2.0 * dp, false );
+ break;
+ case 'c':
+ max = 4;
+ if( closed ) max = 3;
+ for( uint i = 1; i < max; ++i ) {
+ os >> x >> y;
+ if( !os.fail() ) {
+ p = Geom::Point( x, y );
+ p += current_p;
+ new_patch.setPoint( istop, i, p );
+ } else {
+ std::cout << "Failed to read c: " << i << std::endl;
+ }
+ }
+ break;
+ case 'C':
+ max = 4;
+ if( closed ) max = 3;
+ for( uint i = 1; i < max; ++i ) {
+ os >> x >> y;
+ if( !os.fail() ) {
+ p = Geom::Point( x, y );
+ new_patch.setPoint( istop, i, p );
+ } else {
+ std::cout << "Failed to read C: " << i << std::endl;
+ }
+ }
+ break;
+ default:
+ // should not reach
+ std::cout << "Path Error: unhandled path type: " << path_type << std::endl;
+ }
+ current_p = new_patch.getPoint( istop, 3 );
+
+ // Color
+ if( (istop == 0 && irow == 0 && icolumn > 0) || (istop == 1 && irow > 0 ) ) {
+ // skip
+ } else {
+ SPColor color = stop->getEffectiveColor();
+ double opacity = stop->opacity;
+ new_patch.setColor( istop, color );
+ new_patch.setOpacity( istop, opacity );
+ }
+
+ }
+ ++istop;
+ } // Loop over stops
+
+ // Read in tensor string after stops since tensor nodes defined relative to corner nodes.
+
+ // Copy string and then replace commas by spaces so we can use stringstream to parse XXXX
+ if( patch->tensor_string ) {
+ std::string tensor_string = *(patch->tensor_string);
+ std::replace(tensor_string.begin(),tensor_string.end(),',',' ');
+
+ // std::cout << " tensor_string: " << tensor_string << std::endl;
+
+ std::stringstream os( tensor_string );
+ for( uint i = 0; i < 4; ++i ) {
+ double x = 0.0;
+ double y = 0.0;
+ os >> x >> y;
+ if( !os.fail() ) {
+ new_patch.setTensorPoint( i, new_patch.getPoint( i, 0 ) + Geom::Point( x, y ) );
+ } else {
+ std::cout << "Failed to read p: " << i << std::endl;
+ break;
+ }
+ }
+ }
+ }
+
+ ++icolumn;
+ if( max_column < icolumn ) max_column = icolumn;
+ }
+ }
+ ++irow;
+ }
+
+ // Insure we have a true array.
+ for( uint i = 0; i < nodes.size(); ++i ) {
+ nodes[ i ].resize( max_column * 3 + 1 );
+ }
+
+ // Set node edge.
+ for( uint i = 0; i < nodes.size(); ++i ) {
+ for( uint j = 0; j < nodes[i].size(); ++j ) {
+ nodes[i][j]->node_edge = MG_NODE_EDGE_NONE;
+ if( i == 0 ) nodes[i][j]->node_edge |= MG_NODE_EDGE_TOP;
+ if( i == nodes.size() - 1 ) nodes[i][j]->node_edge |= MG_NODE_EDGE_BOTTOM;
+ if( j == 0 ) nodes[i][j]->node_edge |= MG_NODE_EDGE_RIGHT;
+ if( j == nodes[i].size() - 1 ) nodes[i][j]->node_edge |= MG_NODE_EDGE_LEFT;
+ }
+ }
+
+ // std::cout << "SPMeshNodeArray::Read: result:" << std::endl;
+ // print();
+
+ drag_valid = false;
+ built = true;
+
+};
+
+/**
+ Write repr using our array.
+*/
+void SPMeshNodeArray::write( SPMeshGradient *mg ) {
+
+ // std::cout << "SPMeshNodeArray::write: entrance:" << std::endl;
+ // print();
+ using Geom::X;
+ using Geom::Y;
+
+ // First we must delete reprs for old mesh rows and patches.
+ GSList *descendant_reprs = NULL;
+ GSList *descendant_objects = NULL;
+ for ( SPObject *row = mg->firstChild(); row; row = row->getNext() ) {
+ descendant_reprs = g_slist_prepend (descendant_reprs, row->getRepr());
+ descendant_objects = g_slist_prepend (descendant_objects, row );
+ for ( SPObject *patch = row->firstChild(); patch; patch = patch->getNext() ) {
+ descendant_reprs = g_slist_prepend (descendant_reprs, patch->getRepr());
+ descendant_objects = g_slist_prepend (descendant_objects, patch );
+ for ( SPObject *stop = patch->firstChild(); stop; stop = stop->getNext() ) {
+ descendant_reprs = g_slist_prepend (descendant_reprs, stop->getRepr());
+ descendant_objects = g_slist_prepend (descendant_objects, stop );
+ }
+ }
+ }
+
+ for ( GSList *i = descendant_objects; i != NULL; i = i->next ) {
+ SPObject *descendant = SP_OBJECT (i->data);
+ descendant->deleteObject();
+ }
+
+ for ( GSList *i = descendant_reprs; i != NULL; i = i->next ) {
+ Inkscape::XML::Node *repr = (Inkscape::XML::Node *) i->data;
+ sp_repr_unparent( repr );
+ }
+
+
+ // Now we build new reprs
+ Inkscape::XML::Node *mesh = mg->getRepr();
+
+ SPMeshNodeArray* array = &(mg->array);
+ SPMeshPatchI patch0( &(array->nodes), 0, 0 );
+ Geom::Point current_p = patch0.getPoint( 0, 0 ); // Side 0, point 0
+
+ sp_repr_set_svg_double( mesh, "x", current_p[X] );
+ sp_repr_set_svg_double( mesh, "y", current_p[Y] );
+
+ Geom::Point current_p2( mg->x.computed, mg->y.computed );
+
+ Inkscape::XML::Document *xml_doc = mesh->document();
+ uint rows = array->patch_rows();
+ for( uint i = 0; i < rows; ++i ) {
+
+ // Write row
+ Inkscape::XML::Node *row = xml_doc->createElement("svg:meshRow");
+ mesh->appendChild( row ); // No attributes
+
+ uint columns = array->patch_columns();
+ for( uint j = 0; j < columns; ++j ) {
+
+ // Write patch
+ Inkscape::XML::Node *patch = xml_doc->createElement("svg:meshPatch");
+
+ SPMeshPatchI patchi( &(array->nodes), i, j );
+
+ // Add tensor
+ if( patchi.tensorIsSet() ) {
+
+ std::stringstream is;
+
+ for( uint k = 0; k < 4; ++k ) {
+ Geom::Point p = patchi.getTensorPoint( k ) - patchi.getPoint( k, 0 );
+ is << p[X] << "," << p[Y];
+ if( k < 3 ) is << " ";
+ }
+
+ patch->setAttribute("tensor", is.str().c_str());
+ // std::cout << " SPMeshNodeArray::write: tensor: " << is.str() << std::endl;
+ }
+
+ row->appendChild( patch );
+
+ // Write sides
+ for( uint k = 0; k < 4; ++k ) {
+
+ // Only first row has top stop
+ if( k == 0 && i != 0 ) continue;
+
+ // Only first column has left stop
+ if( k == 3 && j != 0 ) continue;
+
+ Inkscape::XML::Node *stop = xml_doc->createElement("svg:stop");
+
+ // Add path
+ std::stringstream is;
+ char path_type = patchi.getPathType( k );
+ is << path_type;
+
+ std::vector< Geom::Point> p = patchi.getPointsForSide( k );
+ current_p = patchi.getPoint( k, 0 );
+
+ switch ( path_type ) {
+ case 'l':
+ is << " "
+ << ( p[3][X] - current_p[X] ) << ","
+ << ( p[3][Y] - current_p[Y] );
+ break;
+ case 'L':
+ is << " "
+ << p[3][X] << ","
+ << p[3][Y];
+ break;
+ case 'c':
+ is << " "
+ << ( p[1][X] - current_p[X] ) << ","
+ << ( p[1][Y] - current_p[Y] ) << " "
+ << ( p[2][X] - current_p[X] ) << ","
+ << ( p[2][Y] - current_p[Y] ) << " "
+ << ( p[3][X] - current_p[X] ) << ","
+ << ( p[3][Y] - current_p[Y] );
+ break;
+ case 'C':
+ is << " "
+ << p[1][X] << ","
+ << p[1][Y] << " "
+ << p[2][X] << ","
+ << p[2][Y] << " "
+ << p[3][X] << ","
+ << p[3][Y];
+ break;
+ case 'z':
+ case 'Z':
+ std::cout << "sp_meshgradient_repr_write: bad path type" << path_type << std::endl;
+ break;
+ default:
+ std::cout << "sp_meshgradient_repr_write: unhandled path type" << path_type << std::endl;
+ }
+ stop->setAttribute("path", is.str().c_str());
+ // std::cout << "SPMeshNodeArray::write: path: " << is.str().c_str() << std::endl;
+ // Add stop-color
+ if( ( k == 0 && i == 0 && j == 0 ) ||
+ ( k == 1 && i == 0 ) ||
+ ( k == 2 ) ||
+ ( k == 3 && j == 0 ) ) {
+
+ // Why are we setting attribute and not style?
+ //stop->setAttribute("stop-color", patchi.getColor(k).toString().c_str() );
+ //stop->setAttribute("stop-opacity", patchi.getOpacity(k) );
+
+ Inkscape::CSSOStringStream os;
+ os << "stop-color:" << patchi.getColor(k).toString() << ";stop-opacity:" << patchi.getOpacity(k);
+ stop->setAttribute("style", os.str().c_str());
+ }
+ patch->appendChild( stop );
+ }
+ }
+ }
+}
+
+/**
+ Find default color based on color of first stop in "vector" gradient.
+ This should be rewritten if dependence on "vector" is removed.
+*/
+SPColor default_color( SPItem *item ) {
+
+ // Set initial color to the color of the object before adding the mesh.
+ // This is a bit tricky as at the moment, a "vector" gradient is created
+ // before reaching here, replacing the original solid color. But the first
+ // stop will be that of the original object color.
+ SPColor color( 0.5, 0.0, 0.5 );
+ if ( item->style ) {
+ SPStyle const &style = *(item->style);
+ SPIPaint const &paint = ( style.fill ); // Could pick between style.fill/style.stroke
+ if ( paint.isColor() ) {
+ color = paint.value.color;
+ } else if ( paint.isPaintserver() ) {
+ SPObject const *server = style.getFillPaintServer();
+ if ( SP_IS_GRADIENT(server) ) {
+ SPGradient *vector = SP_GRADIENT( server )->getVector();
+ SPStop *firstStop = (vector) ?
+ vector->getFirstStop() : SP_GRADIENT( server )->getFirstStop();
+ if ( firstStop ) {
+ if (firstStop->currentColor) {
+ Glib::ustring str = firstStop->getStyleProperty("color", NULL);
+ if( !str.empty() ) {
+ guint32 rgb = sp_svg_read_color( str.c_str(), 0 );
+ color = SPColor( rgb );
+ }
+ } else {
+ color = firstStop->specified_color;
+ }
+ }
+ }
+ }
+ } else {
+ std::cout << " SPMeshNodeArray: No style" << std::endl;
+ }
+
+ return color;
+}
+
+/**
+ Create a default mesh.
+*/
+void SPMeshNodeArray::create( SPMeshGradient *mg, SPItem *item, Geom::OptRect bbox ) {
+
+ // std::cout << "SPMeshNodeArray::create: Entrance" << std::endl;
+
+ if( !bbox ) {
+ // Set default size to bounding box if size not given.
+ std::cout << "SPMeshNodeArray::create(): bbox empty" << std::endl;
+ Geom::OptRect bbox = item->geometricBounds();
+ }
+ if( !bbox ) {
+ std::cout << "SPMeshNodeArray::create: ERROR: No bounding box!" << std::endl;
+ return;
+ }
+
+ Geom::Coord const width = bbox->dimensions()[Geom::X];
+ Geom::Coord const height = bbox->dimensions()[Geom::Y];
+ Geom::Point center = bbox->midpoint();
+
+ // Must keep repr and array in sync. We have two choices:
+ // Build the repr first and the "read" it.
+ // Construct the array and the "write" it.
+ // We'll do the second.
+
+ // Remove any existing mesh. We could chose to simply scale an existing mesh...
+ //clear();
+
+ // We get called twice when a new mesh is created...WHY?
+ // return if we've already constructed the mesh.
+ if( nodes.size() != 0 ) return;
+
+ // Get default color
+ SPColor color = default_color( item );
+
+ // Get preferences
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ uint prows = prefs->getInt("/tools/mesh/mesh_rows", 1);
+ uint pcols = prefs->getInt("/tools/mesh/mesh_cols", 1);
+
+ SPGradientMeshType mesh_type =
+ (SPGradientMeshType) prefs->getInt("/tools/mesh/mesh_type", SP_GRADIENT_MESH_TYPE_NORMAL);
+
+ if( mesh_type == SP_GRADIENT_MESH_TYPE_CONICAL ) {
+
+ // Conical gradient.. for any shape/path using geometric bounding box.
+
+ gdouble rx = width/2.0;
+ gdouble ry = height/2.0;
+
+ // Start and end angles
+ gdouble start = 0.0;
+ gdouble end = 2.0 * M_PI;
+
+ if ( SP_IS_STAR( item ) ) {
+ // But if it is a star... use star parameters!
+ SPStar* star = SP_STAR( item );
+ center = star->center;
+ rx = star->r[0];
+ ry = star->r[0];
+ start = star->arg[0];
+ end = start + 2.0 * M_PI;
+ }
+
+ if ( SP_IS_ARC( item ) ) {
+ // For arcs use set start/stop
+ SPArc* arc = SP_ARC( item );
+ center[Geom::X] = arc->cx.computed;
+ center[Geom::Y] = arc->cy.computed;
+ rx = arc->rx.computed;
+ ry = arc->ry.computed;
+ start = arc->start;
+ end = arc->end;
+ }
+
+ // std::cout << " start: " << start << " end: " << end << std::endl;
+
+ // IS THIS NECESSARY?
+ Inkscape::XML::Node *repr = mg->getRepr();
+ sp_repr_set_svg_double( repr, "x", center[Geom::X] + rx * cos(start) );
+ sp_repr_set_svg_double( repr, "y", center[Geom::Y] + ry * sin(start) );
+
+ uint sections = pcols;
+
+ // If less sections, arc approximation error too great. (Check!)
+ if( sections < 4 ) sections = 4;
+
+ double arc = (end - start) / (double)sections;
+
+ // See: http://en.wikipedia.org/wiki/B%C3%A9zier_curve
+ gdouble kappa = 4.0/3.0 * tan(arc/4.0);
+ gdouble lenx = rx * kappa;
+ gdouble leny = ry * kappa;
+
+ gdouble s = start;
+ for( uint i = 0; i < sections; ++i ) {
+
+ SPMeshPatchI patch( &nodes, 0, i );
+
+ gdouble x0 = center[Geom::X] + rx * cos(s);
+ gdouble y0 = center[Geom::Y] + ry * sin(s);
+ gdouble x1 = x0 - lenx * sin(s);
+ gdouble y1 = y0 + leny * cos(s);
+
+ s += arc;
+ gdouble x3 = center[Geom::X] + rx * cos(s);
+ gdouble y3 = center[Geom::Y] + ry * sin(s);
+ gdouble x2 = x3 + lenx * sin(s);
+ gdouble y2 = y3 - leny * cos(s);
+
+ patch.setPoint( 0, 0, Geom::Point( x0, y0 ) );
+ patch.setPoint( 0, 1, Geom::Point( x1, y1 ) );
+ patch.setPoint( 0, 2, Geom::Point( x2, y2 ) );
+ patch.setPoint( 0, 3, Geom::Point( x3, y3 ) );
+
+ patch.setPoint( 2, 0, center );
+ patch.setPoint( 3, 0, center );
+
+ for( uint k = 0; k < 4; ++k ) {
+ patch.setPathType( k, 'l' );
+ patch.setColor( k, color );
+ patch.setOpacity( k, 1.0 );
+ }
+ patch.setPathType( 0, 'c' );
+
+ // Set handle and tensor nodes.
+ patch.updateNodes();
+
+ }
+
+ split_row( 0, prows );
+
+ } else {
+
+ // Normal grid meshes
+
+ if( SP_IS_ARC( item ) ) {
+
+ // std::cout << "We've got ourselves an arc!" << std::endl;
+
+ SPArc* arc = SP_ARC( item );
+ center[Geom::X] = arc->cx.computed;
+ center[Geom::Y] = arc->cy.computed;
+ gdouble rx = arc->rx.computed;
+ gdouble ry = arc->ry.computed;
+
+ gdouble s = -3.0/2.0 * M_PI_2;
+
+ Inkscape::XML::Node *repr = mg->getRepr();
+ sp_repr_set_svg_double( repr, "x", center[Geom::X] + rx * cos(s) );
+ sp_repr_set_svg_double( repr, "y", center[Geom::Y] + ry * sin(s) );
+
+ gdouble lenx = rx * 4*tan(M_PI_2/4)/3;
+ gdouble leny = ry * 4*tan(M_PI_2/4)/3;
+
+ SPMeshPatchI patch( &nodes, 0, 0 );
+ for( uint i = 0; i < 4; ++i ) {
+
+ gdouble x0 = center[Geom::X] + rx * cos(s);
+ gdouble y0 = center[Geom::Y] + ry * sin(s);
+ gdouble x1 = x0 + lenx * cos(s + M_PI_2);
+ gdouble y1 = y0 + leny * sin(s + M_PI_2);
+
+ s += M_PI_2;
+ gdouble x3 = center[Geom::X] + rx * cos(s);
+ gdouble y3 = center[Geom::Y] + ry * sin(s);
+ gdouble x2 = x3 + lenx * cos(s - M_PI_2);
+ gdouble y2 = y3 + leny * sin(s - M_PI_2);
+
+ Geom::Point p1( x1, y1 );
+ Geom::Point p2( x2, y2 );
+ Geom::Point p3( x3, y3 );
+ patch.setPoint( i, 1, p1 );
+ patch.setPoint( i, 2, p2 );
+ patch.setPoint( i, 3, p3 );
+
+ patch.setPathType( i, 'c' );
+
+ patch.setColor( i, color );
+ patch.setOpacity( i, 1.0 );
+ }
+
+ // Fill out tensor points
+ patch.updateNodes();
+
+ split_row( 0, prows );
+ split_column( 0, pcols );
+
+ // END Arc
+
+ } else if ( SP_IS_STAR( item ) ) {
+
+ // Do simplest thing... assume star is not rounded or randomized.
+ // (It should be easy to handle the rounded/randomized cases by making
+ // the appropriate star class function public.)
+ SPStar* star = SP_STAR( item );
+ uint sides = star->sides;
+
+ // std::cout << "We've got ourselves an star! Sides: " << sides << std::endl;
+
+ Geom::Point p0 = sp_star_get_xy( star, SP_STAR_POINT_KNOT1, 0 );
+ Inkscape::XML::Node *repr = mg->getRepr();
+ sp_repr_set_svg_double( repr, "x", p0[Geom::X] );
+ sp_repr_set_svg_double( repr, "y", p0[Geom::Y] );
+
+ for( uint i = 0; i < sides; ++i ) {
+
+ if( star->flatsided ) {
+
+ SPMeshPatchI patch( &nodes, 0, i );
+
+ patch.setPoint( 0, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT1, i ) );
+ uint ii = i+1;
+ if( ii == sides ) ii = 0;
+ patch.setPoint( 1, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT1, ii ) );
+ patch.setPoint( 2, 0, star->center );
+ patch.setPoint( 3, 0, star->center );
+
+ for( uint s = 0; s < 4; ++s ) {
+ patch.setPathType( s, 'l' );
+ patch.setColor( s, color );
+ patch.setOpacity( s, 1.0 );
+ }
+
+ // Set handle and tensor nodes.
+ patch.updateNodes();
+
+ } else {
+
+ SPMeshPatchI patch0( &nodes, 0, 2*i );
+
+ patch0.setPoint( 0, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT1, i ) );
+ patch0.setPoint( 1, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT2, i ) );
+ patch0.setPoint( 2, 0, star->center );
+ patch0.setPoint( 3, 0, star->center );
+
+ uint ii = i+1;
+ if( ii == sides ) ii = 0;
+
+ SPMeshPatchI patch1( &nodes, 0, 2*i+1 );
+
+ patch1.setPoint( 0, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT2, i ) );
+ patch1.setPoint( 1, 0, sp_star_get_xy( star, SP_STAR_POINT_KNOT1, ii ) );
+ patch1.setPoint( 2, 0, star->center );
+ patch1.setPoint( 3, 0, star->center );
+
+ for( uint s = 0; s < 4; ++s ) {
+ patch0.setPathType( s, 'l' );
+ patch0.setColor( s, color );
+ patch0.setOpacity( s, 1.0 );
+ patch1.setPathType( s, 'l' );
+ patch1.setColor( s, color );
+ patch1.setOpacity( s, 1.0 );
+ }
+
+ // Set handle and tensor nodes.
+ patch0.updateNodes();
+ patch1.updateNodes();
+
+ }
+ }
+
+ //print();
+
+ split_row( 0, prows );
+ //split_column( 0, pcols );
+
+ } else {
+
+ // Generic
+
+ Inkscape::XML::Node *repr = mg->getRepr();
+ sp_repr_set_svg_double(repr, "x", bbox->min()[Geom::X]);
+ sp_repr_set_svg_double(repr, "y", bbox->min()[Geom::Y]);
+
+ // Get node array size
+ uint nrows = prows * 3 + 1;
+ uint ncols = pcols * 3 + 1;
+
+ gdouble dx = width / (gdouble)(ncols-1.0);
+ gdouble dy = height / (gdouble)(nrows-1.0);
+
+ Geom::Point p0( mg->x.computed, mg->y.computed );
+
+ for( uint i = 0; i < nrows; ++i ) {
+ std::vector< SPMeshNode* > row;
+ for( uint j = 0; j < ncols; ++j ) {
+ SPMeshNode* node = new SPMeshNode;
+ node->p = p0 + Geom::Point( j * dx, i * dy );
+
+ node->node_edge = MG_NODE_EDGE_NONE;
+ if( i == 0 ) node->node_edge |= MG_NODE_EDGE_TOP;
+ if( i == nrows -1 ) node->node_edge |= MG_NODE_EDGE_BOTTOM;
+ if( j == 0 ) node->node_edge |= MG_NODE_EDGE_LEFT;
+ if( j == ncols -1 ) node->node_edge |= MG_NODE_EDGE_RIGHT;
+
+ if( i%3 == 0 ) {
+
+ if( j%3 == 0) {
+ // Corner
+ node->node_type = MG_NODE_TYPE_CORNER;
+ node->set = true;
+ node->color = color;
+ node->opacity = 1.0;
+
+ } else {
+ // Side
+ node->node_type = MG_NODE_TYPE_HANDLE;
+ node->set = true;
+ node->path_type = 'c';
+ }
+
+ } else {
+
+ if( j%3 == 0) {
+ // Side
+ node->node_type = MG_NODE_TYPE_HANDLE;
+ node->set = true;
+ node->path_type = 'c';
+ } else {
+ // Tensor
+ node->node_type = MG_NODE_TYPE_TENSOR;
+ node->set = false;
+ }
+
+ }
+
+ row.push_back( node );
+ }
+ nodes.push_back( row );
+ }
+ // End normal
+ }
+
+ } // If conical
+
+ //print();
+
+ // Write repr
+ write( mg );
+}
+
+
+/**
+ Clear mesh gradient.
+*/
+void SPMeshNodeArray::clear() {
+
+ for( uint i = 0; i < nodes.size(); ++i ) {
+ for( uint j = 0; j < nodes[i].size(); ++j ) {
+ if( nodes[i][j] ) {
+ delete nodes[i][j];
+ }
+ }
+ for( uint i = 0; i < nodes.size(); ++i ) {
+ nodes[i].clear();
+ }
+ nodes.clear();
+ }
+};
+
+
+/**
+ Print mesh gradient (for debugging).
+*/
+void SPMeshNodeArray::print() {
+ for( uint i = 0; i < nodes.size(); ++i ) {
+ std::cout << "New node row:" << std::endl;
+ for( uint j = 0; j < nodes[i].size(); ++j ) {
+ if( nodes[i][j] ) {
+ std::cout.width(4);
+ std::cout << " Node: " << i << "," << j << ": "
+ << nodes[i][j]->p
+ << " Node type: " << nodes[i][j]->node_type
+ << " Node edge: " << nodes[i][j]->node_edge
+ << " Set: " << nodes[i][j]->set
+ << " Path type: " << nodes[i][j]->path_type
+ << std::endl;
+ } else {
+ std::cout << "Error: missing mesh node." << std::endl;
+ }
+ } // Loop over patches
+ } // Loop over rows
+};
+
+
+/**
+ Number of patch rows.
+*/
+uint SPMeshNodeArray::patch_rows() {
+
+ return nodes.size()/3;
+}
+
+/**
+ Number of patch columns.
+*/
+uint SPMeshNodeArray::patch_columns() {
+
+ return nodes[0].size()/3;
+}
+
+/**
+ Inputs:
+ i, j: Corner draggable indices.
+ Returns:
+ true if corners adjacent.
+ n[] is array of nodes in top/bottom or left/right order.
+*/
+bool SPMeshNodeArray::adjacent_corners( uint i, uint j, SPMeshNode* n[4] ) {
+
+ // This works as all corners have indices and they
+ // are numbered in order by row and column (and
+ // the node array is rectangular).
+
+ bool adjacent = false;
+
+ uint c1 = i;
+ uint c2 = j;
+ if( j < i ) {
+ c1 = j;
+ c2 = i;
+ }
+
+ // Number of corners in a row of patches.
+ uint ncorners = patch_columns() + 1;
+
+ uint crow1 = c1 / ncorners;
+ uint crow2 = c2 / ncorners;
+ uint ccol1 = c1 % ncorners;
+ uint ccol2 = c2 % ncorners;
+
+ uint nrow = crow1 * 3;
+ uint ncol = ccol1 * 3;
+
+ // std::cout << " i: " << i
+ // << " j: " << j
+ // << " ncorners: " << ncorners
+ // << " c1: " << c1
+ // << " crow1: " << crow1
+ // << " ccol1: " << ccol1
+ // << " c2: " << c2
+ // << " crow2: " << crow2
+ // << " ccol2: " << ccol2
+ // << " nrow: " << nrow
+ // << " ncol: " << ncol
+ // << std::endl;
+
+ // Check for horizontal neighbors
+ if ( crow1 == crow2 && (ccol2 - ccol1) == 1 ) {
+ adjacent = true;
+ for( uint k = 0; k < 4; ++k ) {
+ n[k] = nodes[nrow][ncol+k];
+ }
+ }
+
+ // Check for vertical neighbors
+ if ( ccol1 == ccol2 && (crow2 - crow1) == 1 ) {
+ adjacent = true;
+ for( uint k = 0; k < 4; ++k ) {
+ n[k] = nodes[nrow+k][ncol];
+ }
+ }
+
+ return adjacent;
+}
+
+/**
+ Toggle sides between lineto and curve to if both corners selected.
+ Input is a list of selected corner draggable indices.
+*/
+uint SPMeshNodeArray::side_toggle( std::vector<uint> corners ) {
+
+ uint toggled = 0;
+
+ if( corners.size() < 2 ) return 0;
+
+ for( uint i = 0; i < corners.size()-1; ++i ) {
+ for( uint j = i+1; j < corners.size(); ++j ) {
+
+ SPMeshNode* n[4];
+ if( adjacent_corners( corners[i], corners[j], n ) ) {
+
+ gchar path_type = n[1]->path_type;
+ switch (path_type)
+ {
+ case 'L':
+ n[1]->path_type = 'C';
+ n[2]->path_type = 'C';
+ n[1]->set = true;
+ n[2]->set = true;
+ break;
+
+ case 'l':
+ n[1]->path_type = 'c';
+ n[2]->path_type = 'c';
+ n[1]->set = true;
+ n[2]->set = true;
+ break;
+
+ case 'C': {
+ n[1]->path_type = 'L';
+ n[2]->path_type = 'L';
+ n[1]->set = false;
+ n[2]->set = false;
+ // 'L' acts as if handles are 1/3 of path length from corners.
+ Geom::Point dp = (n[3]->p - n[0]->p)/3.0;
+ n[1]->p = n[0]->p + dp;
+ n[2]->p = n[3]->p - dp;
+ break;
+ }
+ case 'c': {
+ n[1]->path_type = 'l';
+ n[2]->path_type = 'l';
+ n[1]->set = false;
+ n[2]->set = false;
+ // 'l' acts as if handles are 1/3 of path length from corners.
+ Geom::Point dp = (n[3]->p - n[0]->p)/3.0;
+ n[1]->p = n[0]->p + dp;
+ n[2]->p = n[3]->p - dp;
+ // std::cout << "Toggle sides: "
+ // << n[0]->p << " "
+ // << n[1]->p << " "
+ // << n[2]->p << " "
+ // << n[3]->p << " "
+ // << dp << std::endl;
+ break;
+ }
+ default:
+ std::cout << "Toggle sides: Invalid path type: " << path_type << std::endl;
+ }
+ ++toggled;
+ }
+ }
+ }
+ if( toggled > 0 ) built = false;
+ return toggled;
+}
+
+/**
+ * Converts generic Beziers to Beziers approximating elliptical arcs, preserving handle direction.
+ * There are infinite possible solutions. The solution chosen here is to generate a section of an
+ * ellipse that is centered on the intersection of the two lines passing through the two nodes but
+ * parallel to the other node's handle direction. This is the section of an ellipse that
+ * corresponds to a quarter of a circle squished and then skewed.
+ */
+uint SPMeshNodeArray::side_arc( std::vector<uint> corners ) {
+
+ if( corners.size() < 2 ) return 0;
+
+ uint arced = 0;
+ for( uint i = 0; i < corners.size()-1; ++i ) {
+ for( uint j = i+1; j < corners.size(); ++j ) {
+
+ SPMeshNode* n[4];
+ if( adjacent_corners( corners[i], corners[j], n ) ) {
+
+ gchar path_type = n[1]->path_type;
+ switch (path_type)
+ {
+ case 'L':
+ case 'l':
+ std::cout << "SPMeshNodeArray::arc_sides: Can't convert straight lines to arcs.";
+ break;
+
+ case 'C':
+ case 'c': {
+
+ Geom::Ray ray1( n[0]->p, n[1]->p );
+ Geom::Ray ray2( n[3]->p, n[2]->p );
+ if( !are_parallel( (Geom::Line)ray1, (Geom::Line)ray2 ) ) {
+
+ Geom::OptCrossing crossing = intersection( ray1, ray2 );
+
+ if( crossing ) {
+
+ Geom::Point intersection = ray1.pointAt( (*crossing).ta );
+
+ const double f = 4.0/3.0 * tan( M_PI/2.0/4.0 );
+
+ Geom::Point h1 = intersection - n[0]->p;
+ Geom::Point h2 = intersection - n[3]->p;
+
+ n[1]->p = n[0]->p + f*h1;
+ n[2]->p = n[3]->p + f*h2;
+ ++arced;
+
+ } else {
+ std::cout << "SPMeshNodeArray::arc_sides: No crossing, can't turn into arc." << std::endl;
+ }
+ } else {
+ std::cout << "SPMeshNodeArray::arc_sides: Handles parallel, can't turn into arc." << std::endl;
+ }
+ break;
+ }
+ default:
+ std::cout << "SPMeshNodeArray::arc_sides: Invalid path type: " << n[1]->path_type << std::endl;
+ }
+ }
+ }
+ }
+ if( arced > 0 ) built = false;
+ return arced;
+}
+
+/**
+ Toggle sides between lineto and curve to if both corners selected.
+ Input is a list of selected corner draggable indices.
+*/
+uint SPMeshNodeArray::tensor_toggle( std::vector<uint> corners ) {
+
+ // std::cout << "SPMeshNodeArray::tensor_toggle" << std::endl;
+
+ if( corners.size() < 4 ) return 0;
+
+ uint toggled = 0;
+
+ // Number of corners in a row of patches.
+ uint ncorners = patch_columns() + 1;
+
+ for( uint i = 0; i < corners.size()-3; ++i ) {
+ for( uint j = i+1; j < corners.size()-2; ++j ) {
+ for( uint k = j+1; k < corners.size()-1; ++k ) {
+ for( uint l = k+1; l < corners.size(); ++l ) {
+
+ uint c[4];
+ c[0] = corners[i];
+ c[1] = corners[j];
+ c[2] = corners[k];
+ c[3] = corners[l];
+ std::sort( c, c+4 );
+
+ // Check we have four corners of one patch selected
+ if( c[1]-c[0] == 1 &&
+ c[3]-c[2] == 1 &&
+ c[2]-c[0] == ncorners &&
+ c[3]-c[1] == ncorners &&
+ c[0] % ncorners < ncorners - 1 ) {
+
+ // Patch
+ uint prow = c[0] / ncorners;
+ uint pcol = c[0] % ncorners;
+
+ // Upper left node of patch
+ uint irow = prow * 3;
+ uint jcol = pcol * 3;
+
+ // std::cout << "tensor::toggle: "
+ // << c[0] << ", "
+ // << c[1] << ", "
+ // << c[2] << ", "
+ // << c[3] << std::endl;
+
+ // std::cout << "tensor::toggle: "
+ // << " irow: " << irow
+ // << " jcol: " << jcol
+ // << " prow: " << prow
+ // << " pcol: " << pcol
+ // << std::endl;
+
+ SPMeshPatchI patch( &nodes, prow, pcol );
+ patch.updateNodes();
+
+ if( patch.tensorIsSet() ) {
+ // Unset tensor points
+ nodes[irow+1][jcol+1]->set = false;
+ nodes[irow+1][jcol+2]->set = false;
+ nodes[irow+2][jcol+1]->set = false;
+ nodes[irow+2][jcol+2]->set = false;
+ } else {
+ // Set tensor points
+ nodes[irow+1][jcol+1]->set = true;
+ nodes[irow+1][jcol+2]->set = true;
+ nodes[irow+2][jcol+1]->set = true;
+ nodes[irow+2][jcol+2]->set = true;
+ }
+
+ ++toggled;
+ }
+ }
+ }
+ }
+ }
+ if( toggled > 0 ) built = false;
+ return toggled;
+}
+
+/**
+ Atempts to smooth color transitions across corners.
+ Input is a list of selected corner draggable indices.
+*/
+uint SPMeshNodeArray::color_smooth( std::vector<uint> corners ) {
+
+ // std::cout << "SPMeshNodeArray::color_smooth" << std::endl;
+
+ uint smoothed = 0;
+
+ // Number of corners in a row of patches.
+ uint ncorners = patch_columns() + 1;
+
+ // Number of node rows and columns
+ uint ncols = patch_columns() * 3 + 1;
+ uint nrows = patch_rows() * 3 + 1;
+
+ for( uint i = 0; i < corners.size(); ++i ) {
+
+ uint corner = corners[i];
+ // std::cout << "SPMeshNodeArray::color_smooth: " << i << " " << corner << std::endl;
+
+ // Node row & col
+ uint nrow = (corner / ncorners) * 3;
+ uint ncol = (corner % ncorners) * 3;
+
+ SPMeshNode* n[7];
+ for( uint s = 0; s < 2; ++s ) {
+
+ bool smooth = false;
+
+ // Find neighboring nodes
+ if( s == 0 ) {
+
+ // Horizontal
+ if( ncol > 2 && ncol+3 < ncols) {
+ for( uint j = 0; j < 7; ++j ) {
+ n[j] = nodes[ nrow ][ ncol - 3 + j ];
+ }
+ smooth = true;
+ }
+
+ } else {
+
+ // Vertical
+ if( nrow > 2 && nrow+3 < nrows) {
+ for( uint j = 0; j < 7; ++j ) {
+ n[j] = nodes[ nrow - 3 + j ][ ncol ];
+ }
+ smooth = true;
+ }
+ }
+
+ if( smooth ) {
+
+ // Let the smoothing begin
+ // std::cout << " checking: " << ncol << " " << nrow << std::endl;
+
+ // Get initial slopes using closest handles.
+ double slope[2][3];
+ double slope_ave[3];
+ double slope_diff[3];
+
+ // Color of corners
+ SPColor color0 = n[0]->color;
+ SPColor color3 = n[3]->color;
+ SPColor color6 = n[6]->color;
+
+ // Distance nodes from selected corner
+ Geom::Point d[7];
+ for( uint k = 0; k < 7; ++k ) {
+ d[k]= n[k]->p - n[3]->p;
+ // std::cout << " d[" << k << "]: " << d[k].length() << std::endl;
+ }
+
+ double sdm = -1.0; // Slope Diff Max
+ uint cdm = 0; // Color Diff Max (Which color has the maximum difference in slopes)
+ for( uint c = 0; c < 3; ++c ) {
+ if( d[2].length() != 0.0 ) {
+ slope[0][c] = (color3.v.c[c] - color0.v.c[c]) / d[2].length();
+ }
+ if( d[4].length() != 0.0 ) {
+ slope[1][c] = (color6.v.c[c] - color3.v.c[c]) / d[4].length();
+ }
+ slope_ave[c] = (slope[0][c]+slope[1][c]) / 2.0;
+ slope_diff[c] = (slope[0][c]-slope[1][c]);
+ // std::cout << " color: " << c << " :"
+ // << color0.v.c[c] << " "
+ // << color3.v.c[c] << " "
+ // << color6.v.c[c]
+ // << " slope: "
+ // << slope[0][c] << " "
+ // << slope[1][c]
+ // << " slope_ave: " << slope_ave[c]
+ // << " slope_diff: " << slope_diff[c]
+ // << std::endl;
+
+ // Find color with maximum difference
+ if( std::abs( slope_diff[c] ) > sdm ) {
+ sdm = std::abs( slope_diff[c] );
+ cdm = c;
+ }
+ }
+ // std::cout << " cdm: " << cdm << std::endl;
+
+ // Find new handle positions:
+ double length_left = d[0].length();
+ double length_right = d[6].length();
+ if( slope_ave[ cdm ] != 0.0 ) {
+ length_left = std::abs( (color3.v.c[cdm] - color0.v.c[cdm]) / slope_ave[ cdm ] );
+ length_right = std::abs( (color6.v.c[cdm] - color3.v.c[cdm]) / slope_ave[ cdm ] );
+ }
+
+ // Move closest handle a maximum of mid point... but don't shorten
+ double max = 0.8;
+ if( length_left > max * d[0].length() && length_left > d[2].length() ) {
+ std::cout << " Can't smooth left side" << std::endl;
+ length_left = std::max( max * d[0].length(), d[2].length() );
+ }
+ if( length_right > max * d[6].length() && length_right > d[4].length() ) {
+ std::cout << " Can't smooth right side" << std::endl;
+ length_right = std::max( max * d[6].length(), d[4].length() );
+ }
+
+ if( d[2].length() != 0.0 ) d[2] *= length_left/d[2].length();
+ if( d[4].length() != 0.0 ) d[4] *= length_right/d[4].length();
+
+ // std::cout << " length_left: " << length_left
+ // << " d[0]: " << d[0].length()
+ // << " length_right: " << length_right
+ // << " d[6]: " << d[6].length()
+ // << std::endl;
+
+ n[2]->p = n[3]->p + d[2];
+ n[4]->p = n[3]->p + d[4];
+
+ ++smoothed;
+ }
+ }
+
+ }
+
+ if( smoothed > 0 ) built = false;
+ return smoothed;
+}
+
+/**
+ Pick color from background for selected corners.
+*/
+uint SPMeshNodeArray::color_pick( std::vector<uint> icorners, SPItem* item ) {
+
+ // std::cout << "SPMeshNodeArray::color_pick" << std::endl;
+
+ uint picked = 0;
+
+ // Code inspired from clone tracing
+
+ // Setup...
+
+ // We need a copy of the drawing so we can hide the mesh.
+ Inkscape::Drawing *pick_drawing = new Inkscape::Drawing();
+ unsigned pick_visionkey = SPItem::display_key_new(1);
+
+ SPDocument *pick_doc = mg->document;
+
+ pick_drawing->setRoot(pick_doc->getRoot()->invoke_show(*pick_drawing, pick_visionkey, SP_ITEM_SHOW_DISPLAY));
+
+ item->invoke_hide(pick_visionkey);
+
+ pick_doc->getRoot()->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+ pick_doc->ensureUpToDate();
+
+ //gdouble pick_zoom = 1.0; // zoom;
+ //pick_drawing->root()->setTransform(Geom::Scale(pick_zoom));
+ pick_drawing->update();
+
+ // std::cout << " transform: " << std::endl;
+ // std::cout << item->transform << std::endl;
+ // std::cout << " i2doc: " << std::endl;
+ // std::cout << item->i2doc_affine() << std::endl;
+ // std::cout << " i2dt: " << std::endl;
+ // std::cout << item->i2dt_affine() << std::endl;
+ // std::cout << " dt2i: " << std::endl;
+ // std::cout << item->dt2i_affine() << std::endl;
+ SPGradient* gr = SP_GRADIENT( mg );
+ // if( gr->gradientTransform_set ) {
+ // std::cout << " gradient transform set: " << std::endl;
+ // std::cout << gr->gradientTransform << std::endl;
+ // } else {
+ // std::cout << " gradient transform not set! " << std::endl;
+ // }
+
+ // Do picking
+ for( uint i = 0; i < icorners.size(); ++i ) {
+
+ uint corner = icorners[i];
+
+ SPMeshNode* n = corners[ corner ];
+
+ // Region to average over
+ Geom::Point p = n->p;
+ // std::cout << " p: " << p << std::endl;
+ p *= gr->gradientTransform;
+ // std::cout << " p: " << p << std::endl;
+
+ // If on edge, move inward
+ uint cols = patch_columns()+1;
+ uint rows = patch_rows()+1;
+ uint col = corner % cols;
+ uint row = corner / cols;
+ uint ncol = col * 3;
+ uint nrow = row * 3;
+
+ double size = 3.0;
+
+ // Top edge
+ if( row == 0 ) {
+ Geom::Point dp = nodes[nrow+1][ncol]->p - p;
+ p += unit_vector( dp ) * size;
+ }
+ // Right edge
+ if( col == cols-1 ) {
+ Geom::Point dp = nodes[nrow][ncol-1]->p - p;
+ p += unit_vector( dp ) * size;
+ }
+ // Bottom edge
+ if( row == rows-1 ) {
+ Geom::Point dp = nodes[nrow-1][ncol]->p - p;
+ p += unit_vector( dp ) * size;
+ }
+ // Left edge
+ if( col == 0 ) {
+ Geom::Point dp = nodes[nrow][ncol+1]->p - p;
+ p += unit_vector( dp ) * size;
+ }
+
+ Geom::Rect box( p[Geom::X]-size/2.0, p[Geom::Y]-size/2.0,
+ p[Geom::X]+size/2.0, p[Geom::Y]+size/2.0 );
+
+ /* Item integer bbox in points */
+ Geom::IntRect ibox = box.roundOutwards();
+
+ /* Find visible area */
+ cairo_surface_t *s = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ibox.width(), ibox.height());
+ Inkscape::DrawingContext ct(s, ibox.min());
+
+ /* Render copy and pick color */
+ pick_drawing->render(ct, ibox);
+ double R = 0, G = 0, B = 0, A = 0;
+ ink_cairo_surface_average_color(s, R, G, B, A);
+ cairo_surface_destroy(s);
+
+ // std::cout << " p: " << p
+ // << " box: " << ibox
+ // << " R: " << R
+ // << " G: " << G
+ // << " B: " << B
+ // << std::endl;
+ n->color.set( R, G, B );
+ }
+
+ pick_doc->getRoot()->invoke_hide(pick_visionkey);
+ delete pick_drawing;
+
+ if( picked > 0 ) built = false;
+ return picked;
+}
+
+/**
+ Moves handles in response to a corner node move.
+ p_old: orignal position of moved corner node.
+ corner: the corner node moved (draggable index, i.e. point_i).
+ selected: list of all corners selected (draggable indices).
+ op: how other corners should be moved.
+*/
+void SPMeshNodeArray::update_handles( uint corner, std::vector< uint > selected, Geom::Point p_old, MeshNodeOperation op ) {
+
+ assert( drag_valid );
+
+ // std::cout << "SPMeshNodeArray::update_handles: "
+ // << " corner: " << corner
+ // << " op: " << op
+ // << std::endl;
+
+ // Find number of patch rows and columns
+ uint mrow = patch_rows();
+ uint mcol = patch_columns();
+
+ // Number of corners in a row of patches.
+ uint ncorners = mcol + 1;
+
+ // Find corner row/column
+ uint crow = corner / ncorners;
+ uint ccol = corner % ncorners;
+
+ // Find node row/column
+ uint nrow = crow * 3;
+ uint ncol = ccol * 3;
+
+ // std::cout << " mrow: " << mrow
+ // << " mcol: " << mcol
+ // << " crow: " << crow
+ // << " ccol: " << ccol
+ // << " ncorners: " << ncorners
+ // << " nrow: " << nrow
+ // << " ncol: " << ncol
+ // << std::endl;
+
+ // New corner mesh coordinate.
+ Geom::Point p_new = nodes[nrow][ncol]->p;
+
+ // Corner point move dpg in mesh coordinate system.
+ Geom::Point dp = p_new - p_old;
+
+ // std::cout << " p_old: " << p_old << std::endl;
+ // std::cout << " p_new: " << p_new << std::endl;
+ // std::cout << " dp: " << dp << std::endl;
+
+ // STEP 1: ONLY DO DIRECT MOVE
+ bool patch[4];
+ patch[0] = patch[1] = patch[2] = patch[3] = false;
+ if( ccol > 0 && crow > 0 ) patch[0] = true;
+ if( ccol < mcol && crow > 0 ) patch[1] = true;
+ if( ccol < mcol && crow < mrow ) patch[2] = true;
+ if( ccol > 0 && crow < mrow ) patch[3] = true;
+
+ // std::cout << patch[0] << " "
+ // << patch[1] << " "
+ // << patch[2] << " "
+ // << patch[3] << std::endl;
+
+ // Move handles
+ if( patch[0] || patch[1] ) {
+ if( nodes[nrow-1][ncol]->path_type == 'l' ||
+ nodes[nrow-1][ncol]->path_type == 'L' ) {
+ Geom::Point s = (nodes[nrow-3][ncol]->p - nodes[nrow][ncol]->p)/3.0;
+ nodes[nrow-1][ncol ]->p = nodes[nrow][ncol]->p + s;
+ nodes[nrow-2][ncol ]->p = nodes[nrow-3][ncol]->p - s;
+ } else {
+ nodes[nrow-1][ncol ]->p += dp;
+ }
+ }
+
+ if( patch[1] || patch[2] ) {
+ if( nodes[nrow ][ncol+1]->path_type == 'l' ||
+ nodes[nrow ][ncol+1]->path_type == 'L' ) {
+ Geom::Point s = (nodes[nrow][ncol+3]->p - nodes[nrow][ncol]->p)/3.0;
+ nodes[nrow ][ncol+1]->p = nodes[nrow][ncol]->p + s;
+ nodes[nrow ][ncol+2]->p = nodes[nrow][ncol+3]->p - s;
+ } else {
+ nodes[nrow ][ncol+1]->p += dp;
+ }
+ }
+
+ if( patch[2] || patch[3] ) {
+ if( nodes[nrow+1][ncol ]->path_type == 'l' ||
+ nodes[nrow+1][ncol ]->path_type == 'L' ) {
+ Geom::Point s = (nodes[nrow+3][ncol]->p - nodes[nrow][ncol]->p)/3.0;
+ nodes[nrow+1][ncol ]->p = nodes[nrow][ncol]->p + s;
+ nodes[nrow+2][ncol ]->p = nodes[nrow+3][ncol]->p - s;
+ } else {
+ nodes[nrow+1][ncol ]->p += dp;
+ }
+ }
+
+ if( patch[3] || patch[0] ) {
+ if( nodes[nrow ][ncol-1]->path_type == 'l' ||
+ nodes[nrow ][ncol-1]->path_type == 'L' ) {
+ Geom::Point s = (nodes[nrow][ncol-3]->p - nodes[nrow][ncol]->p)/3.0;
+ nodes[nrow ][ncol-1]->p = nodes[nrow][ncol]->p + s;
+ nodes[nrow ][ncol-2]->p = nodes[nrow][ncol-3]->p - s;
+ } else {
+ nodes[nrow ][ncol-1]->p += dp;
+ }
+ }
+
+
+ // Move tensors
+ if( patch[0] ) nodes[nrow-1][ncol-1]->p += dp;
+ if( patch[1] ) nodes[nrow-1][ncol+1]->p += dp;
+ if( patch[2] ) nodes[nrow+1][ncol+1]->p += dp;
+ if( patch[3] ) nodes[nrow+1][ncol-1]->p += dp;
+
+ // // Check if neighboring corners are selected.
+
+ // bool do_scale = false;
+
+ // bool do_scale_xp = do_scale;
+ // bool do_scale_xn = do_scale;
+ // bool do_scale_yp = do_scale;
+ // bool do_scale_yn = do_scale;
+
+ // if( ccol < mcol+1 ) {
+ // if( std::find( sc.begin(), sc.end(), point_i + 1 ) != sc.end() ) {
+ // do_scale_xp = false;
+ // std::cout << " Not scaling x+" << std::endl;
+ // }
+ // }
+
+ // if( ccol > 0 ) {
+ // if( std::find( sc.begin(), sc.end(), point_i - 1 ) != sc.end() ) {
+ // do_scale_xn = false;
+ // std::cout << " Not scaling x-" << std::endl;
+ // }
+ // }
+
+ // if( crow < mrow+1 ) {
+ // if( std::find( sc.begin(), sc.end(), point_i + ncorners ) != sc.end() ) {
+ // do_scale_yp = false;
+ // std::cout << " Not scaling y+" << std::endl;
+ // }
+ // }
+
+ // if( crow > 0 ) {
+ // if( std::find( sc.begin(), sc.end(), point_i - ncorners ) != sc.end() ) {
+ // do_scale_yn = false;
+ // std::cout << " Not scaling y-" << std::endl;
+ // }
+ // }
+
+ // // We have four patches to adjust...
+ // for ( uint k = 0; k < 4; ++k ) {
+
+ // bool do_scale_x = do_scale;
+ // bool do_scale_y = do_scale;
+
+ // SPMeshNode* pnodes[4][4];
+
+ // // Load up matrix
+ // switch (k) {
+
+ // case 0:
+ // if( crow < mrow+1 && ccol < mcol+1 ) {
+ // // Bottom right patch
+
+ // do_scale_x = do_scale_xp;
+ // do_scale_y = do_scale_yp;
+
+ // for( uint i = 0; i < 4; ++i ) {
+ // for( uint j = 0; j< 4; ++j ) {
+ // pnodes[i][j] = mg->array.nodes[nrow+i][nrow+j];
+ // }
+ // }
+ // }
+ // break;
+
+ // case 1:
+ // if( crow < mrow+1 && ccol > 0 ) {
+ // // Bottom left patch (note x, y swapped)
+
+ // do_scale_y = do_scale_xn;
+ // do_scale_x = do_scale_yp;
+
+ // for( uint i = 0; i < 4; ++i ) {
+ // for( uint j = 0; j< 4; ++j ) {
+ // pnodes[j][i] = mg->array.nodes[nrow+i][nrow-j];
+ // }
+ // }
+ // }
+ // break;
+
+ // case 2:
+ // if( crow > 0 && ccol > 0 ) {
+ // // Top left patch
+
+ // do_scale_x = do_scale_xn;
+ // do_scale_y = do_scale_yn;
+
+ // for( uint i = 0; i < 4; ++i ) {
+ // for( uint j = 0; j< 4; ++j ) {
+ // pnodes[i][j] = mg->array.nodes[nrow-i][nrow-j];
+ // }
+ // }
+ // }
+ // break;
+
+ // case 3:
+ // if( crow > 0 && ccol < mcol+1 ) {
+ // // Top right patch (note x, y swapped)
+
+ // do_scale_y = do_scale_xp;
+ // do_scale_x = do_scale_yn;
+
+ // for( uint i = 0; i < 4; ++i ) {
+ // for( uint j = 0; j< 4; ++j ) {
+ // pnodes[j][i] = mg->array.nodes[nrow-i][nrow+j];
+ // }
+ // }
+ // }
+ // break;
+ // }
+
+ // // Now we must move points in both x and y.
+ // // There are upto six points to move: P01, P02, P11, P12, P21, P22.
+ // // (The points P10, P20 will be moved in another branch of the loop.
+ // // The points P03, P13, P23, P33, P32, P31, P30 are not moved.)
+ // //
+ // // P00 P01 P02 P03
+ // // P10 P11 P12 P13
+ // // P20 P21 P22 P23
+ // // P30 P31 P32 P33
+ // //
+ // // The goal is to preserve the direction of the handle!
+
+
+ // Geom::Point dsx_new = pnodes[0][3]->p - pnodes[0][0]->p; // New side x
+ // Geom::Point dsy_new = pnodes[3][0]->p - pnodes[0][0]->p; // New side y
+ // Geom::Point dsx_old = pnodes[0][3]->p - pcg_old; // Old side x
+ // Geom::Point dsy_old = pnodes[3][0]->p - pcg_old; // Old side y
+
+
+ // double scale_factor_x = 1.0;
+ // if( dsx_old.length() != 0.0 ) scale_factor_x = dsx_new.length()/dsx_old.length();
+
+ // double scale_factor_y = 1.0;
+ // if( dsy_old.length() != 0.0 ) scale_factor_y = dsy_new.length()/dsy_old.length();
+
+
+ // if( do_scalex && do_scaley ) {
+
+ // // We have six point to move.
+
+ // // P01
+ // Geom::Point dp01 = pnodes[0][1] - pcg_old;
+ // dp01 *= scale_factor_x;
+ // pnodes[0][1] = pnodes[0][0] + dp01;
+
+ // // P02
+ // Geom::Point dp02 = pnodes[0][2] - pnodes[0][3];
+ // dp02 *= scale_factor_x;
+ // pnodes[0][2] = pnodes[0][3] + dp02;
+
+ // // P11
+ // Geom::Point dp11 = pnodes[1][1] - pcg_old;
+ // dp11 *= scale_factor_x;
+ // pnodes[1][1] = pnodes[0][0] + dp11;
+
+
+
+ // // P21
+ // Geom::Point dp21 = pnodes[2][1] - pnodes[3][0];
+ // dp21 *= scale_factor_x;
+ // dp21 *= scale_factor_y;
+ // pnodes[2][1] = pnodes[3][0] + dp21;
+
+
+ // Geom::Point dsx1 = pnodes[0][1]->p -
+
+
+
+}
+
+
+// Defined in gradient-chemistry.cpp
+guint32 average_color(guint32 c1, guint32 c2, gdouble p);
+
+/**
+ Split a row into n equal parts.
+*/
+void SPMeshNodeArray::split_row( uint row, uint n ) {
+
+ double nn = n;
+ if( n > 1 ) split_row( row, (nn-1)/nn );
+ if( n > 2 ) split_row( row, n-1 );
+}
+
+/**
+ Split a column into n equal parts.
+*/
+void SPMeshNodeArray::split_column( uint col, uint n ) {
+
+ double nn = n;
+ if( n > 1 ) split_column( col, (nn-1)/nn );
+ if( n > 2 ) split_column( col, n-1 );
+}
+
+/**
+ Split a row into two rows at coord (fraction of row height).
+*/
+void SPMeshNodeArray::split_row( uint row, double coord ) {
+
+ // std::cout << "Splitting row: " << row << " at " << coord << std::endl;
+ // print();
+ assert( coord >= 0.0 && coord <= 1.0 );
+ assert( row < patch_rows() );
+
+ built = false;
+
+ // First step is to ensure that handle and tensor points are up-to-date if they are not set.
+ // (We can't do this on the fly as we overwrite the necessary points to do the calculation
+ // during the update.)
+ for( uint j = 0; j < patch_columns(); ++ j ) {
+ SPMeshPatchI patch( &nodes, row, j );
+ patch.updateNodes();
+ }
+
+ // Add three new rows of empty nodes
+ for( uint i = 0; i < 3; ++i ) {
+ std::vector< SPMeshNode* > new_row;
+ for( uint j = 0; j < nodes[0].size(); ++j ) {
+ SPMeshNode* new_node = new SPMeshNode;
+ new_row.push_back( new_node );
+ }
+ nodes.insert( nodes.begin()+3*(row+1), new_row );
+ }
+
+ uint i = 3 * row; // Convert from patch row to node row
+ for( uint j = 0; j < nodes[i].size(); ++j ) {
+
+ // std::cout << "Splitting row: column: " << j << std::endl;
+
+ Geom::Point p[4];
+ for( uint k = 0; k < 4; ++k ) {
+ uint n = k;
+ if( k == 3 ) n = 6; // Bottom patch row has been shifted by new rows
+ p[k] = nodes[i+n][j]->p;
+ // std::cout << p[k] << std::endl;
+ }
+
+ Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] );
+
+ std::pair<Geom::BezierCurveN<3>, Geom::BezierCurveN<3> > b_new =
+ b.subdivide( coord );
+
+ // Update points
+ for( uint n = 0; n < 4; ++n ) {
+ nodes[i+n ][j]->p = b_new.first[n];
+ nodes[i+n+3][j]->p = b_new.second[n];
+ // std::cout << b_new.first[n] << " " << b_new.second[n] << std::endl;
+ }
+
+ if( nodes[i][j]->node_type == MG_NODE_TYPE_CORNER ) {
+ // We are splitting a side
+
+ // Path type stored in handles.
+ gchar path_type = nodes[i+1][j]->path_type;
+ nodes[i+4][j]->path_type = path_type;
+ nodes[i+5][j]->path_type = path_type;
+ bool set = nodes[i+1][j]->set;
+ nodes[i+4][j]->set = set;
+ nodes[i+5][j]->set = set;
+ nodes[i+4][j]->node_type = MG_NODE_TYPE_HANDLE;
+ nodes[i+5][j]->node_type = MG_NODE_TYPE_HANDLE;
+
+ // Color stored in corners
+ guint c0 = nodes[i ][j]->color.toRGBA32( 1.0 );
+ guint c1 = nodes[i+6][j]->color.toRGBA32( 1.0 );
+ gdouble o0 = nodes[i ][j]->opacity;
+ gdouble o1 = nodes[i+6][j]->opacity;
+ guint cnew = average_color( c0, c1, coord );
+ gdouble onew = o0 * (1.0 - coord) + o1 * coord;
+ nodes[i+3][j]->color.set( cnew );
+ nodes[i+3][j]->opacity = onew;
+ nodes[i+3][j]->node_type = MG_NODE_TYPE_CORNER;
+ nodes[i+3][j]->set = true;
+
+ } else {
+ // We are splitting a middle
+
+ bool set = nodes[i+1][j]->set || nodes[i+1][j]->set;
+ nodes[i+4][j]->set = set;
+ nodes[i+5][j]->set = set;
+ nodes[i+4][j]->node_type = MG_NODE_TYPE_TENSOR;
+ nodes[i+5][j]->node_type = MG_NODE_TYPE_TENSOR;
+
+ // Path type, if different, choose l -> L -> c -> C.
+ gchar path_type0 = nodes[i ][j]->path_type;
+ gchar path_type1 = nodes[i+6][j]->path_type;
+ gchar path_type = 'l';
+ if( path_type0 == 'L' || path_type1 == 'L') path_type = 'L';
+ if( path_type0 == 'c' || path_type1 == 'c') path_type = 'c';
+ if( path_type0 == 'C' || path_type1 == 'C') path_type = 'C';
+ nodes[i+3][j]->path_type = path_type;
+ nodes[i+3][j]->node_type = MG_NODE_TYPE_HANDLE;
+ if( path_type == 'c' || path_type == 'C' ) nodes[i+3][j]->set = true;
+
+ }
+
+ nodes[i+3][j]->node_edge = MG_NODE_EDGE_NONE;
+ nodes[i+4][j]->node_edge = MG_NODE_EDGE_NONE;
+ nodes[i+5][j]->node_edge = MG_NODE_EDGE_NONE;;
+ if( j == 0 ) {
+ nodes[i+3][j]->node_edge |= MG_NODE_EDGE_LEFT;
+ nodes[i+4][j]->node_edge |= MG_NODE_EDGE_LEFT;
+ nodes[i+5][j]->node_edge |= MG_NODE_EDGE_LEFT;
+ }
+ if( j == nodes[i].size() - 1 ) {
+ nodes[i+3][j]->node_edge |= MG_NODE_EDGE_RIGHT;
+ nodes[i+4][j]->node_edge |= MG_NODE_EDGE_RIGHT;
+ nodes[i+5][j]->node_edge |= MG_NODE_EDGE_RIGHT;
+ }
+ }
+
+ // std::cout << "Splitting row: result:" << std::endl;
+ // print();
+}
+
+
+
+/**
+ Split a column into two columns at coord (fraction of column width).
+*/
+void SPMeshNodeArray::split_column( uint col, double coord ) {
+
+ // std::cout << "Splitting column: " << col << " at " << coord << std::endl;
+ // print();
+ assert( coord >= 0.0 && coord <= 1.0 );
+ assert( col < patch_columns() );
+
+ built = false;
+
+ // First step is to ensure that handle and tensor points are up-to-date if they are not set.
+ // (We can't do this on the fly as we overwrite the necessary points to do the calculation
+ // during the update.)
+ for( uint i = 0; i < patch_rows(); ++ i ) {
+ SPMeshPatchI patch( &nodes, i, col );
+ patch.updateNodes();
+ }
+
+ uint j = 3 * col; // Convert from patch column to node column
+ for( uint i = 0; i < nodes.size(); ++i ) {
+
+ // std::cout << "Splitting column: row: " << i << std::endl;
+
+ Geom::Point p[4];
+ for( uint k = 0; k < 4; ++k ) {
+ p[k] = nodes[i][j+k]->p;
+ }
+
+ Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] );
+
+ std::pair<Geom::BezierCurveN<3>, Geom::BezierCurveN<3> > b_new =
+ b.subdivide( coord );
+
+ // Add three new nodes
+ for( uint n = 0; n < 3; ++n ) {
+ SPMeshNode* new_node = new SPMeshNode;
+ nodes[i].insert( nodes[i].begin()+j+3, new_node );
+ }
+
+ // Update points
+ for( uint n = 0; n < 4; ++n ) {
+ nodes[i][j+n]->p = b_new.first[n];
+ nodes[i][j+n+3]->p = b_new.second[n];
+ }
+
+ if( nodes[i][j]->node_type == MG_NODE_TYPE_CORNER ) {
+ // We are splitting a side
+
+ // Path type stored in handles.
+ gchar path_type = nodes[i][j+1]->path_type;
+ nodes[i][j+4]->path_type = path_type;
+ nodes[i][j+5]->path_type = path_type;
+ bool set = nodes[i][j+1]->set;
+ nodes[i][j+4]->set = set;
+ nodes[i][j+5]->set = set;
+ nodes[i][j+4]->node_type = MG_NODE_TYPE_HANDLE;
+ nodes[i][j+5]->node_type = MG_NODE_TYPE_HANDLE;
+
+ // Color stored in corners
+ guint c0 = nodes[i][j ]->color.toRGBA32( 1.0 );
+ guint c1 = nodes[i][j+6]->color.toRGBA32( 1.0 );
+ gdouble o0 = nodes[i][j ]->opacity;
+ gdouble o1 = nodes[i][j+6]->opacity;
+ guint cnew = average_color( c0, c1, coord );
+ gdouble onew = o0 * (1.0 - coord) + o1 * coord;
+ nodes[i][j+3]->color.set( cnew );
+ nodes[i][j+3]->opacity = onew;
+ nodes[i][j+3]->node_type = MG_NODE_TYPE_CORNER;
+ nodes[i][j+3]->set = true;
+
+ } else {
+ // We are splitting a middle
+
+ bool set = nodes[i][j+1]->set || nodes[i][j+2]->set;
+ nodes[i][j+4]->set = set;
+ nodes[i][j+5]->set = set;
+ nodes[i][j+4]->node_type = MG_NODE_TYPE_TENSOR;
+ nodes[i][j+5]->node_type = MG_NODE_TYPE_TENSOR;
+
+ // Path type, if different, choose l -> L -> c -> C.
+ gchar path_type0 = nodes[i][j ]->path_type;
+ gchar path_type1 = nodes[i][j+6]->path_type;
+ gchar path_type = 'l';
+ if( path_type0 == 'L' || path_type1 == 'L') path_type = 'L';
+ if( path_type0 == 'c' || path_type1 == 'c') path_type = 'c';
+ if( path_type0 == 'C' || path_type1 == 'C') path_type = 'C';
+ nodes[i][j+3]->path_type = path_type;
+ nodes[i][j+3]->node_type = MG_NODE_TYPE_HANDLE;
+ if( path_type == 'c' || path_type == 'C' ) nodes[i][j+3]->set = true;
+
+ }
+
+ nodes[i][j+3]->node_edge = MG_NODE_EDGE_NONE;
+ nodes[i][j+4]->node_edge = MG_NODE_EDGE_NONE;
+ nodes[i][j+5]->node_edge = MG_NODE_EDGE_NONE;;
+ if( i == 0 ) {
+ nodes[i][j+3]->node_edge |= MG_NODE_EDGE_TOP;
+ nodes[i][j+4]->node_edge |= MG_NODE_EDGE_TOP;
+ nodes[i][j+5]->node_edge |= MG_NODE_EDGE_TOP;
+ }
+ if( i == nodes.size() - 1 ) {
+ nodes[i][j+3]->node_edge |= MG_NODE_EDGE_BOTTOM;
+ nodes[i][j+4]->node_edge |= MG_NODE_EDGE_BOTTOM;
+ nodes[i][j+5]->node_edge |= MG_NODE_EDGE_BOTTOM;
+ }
+
+ }
+
+ // std::cout << "Splitting col: result:" << std::endl;
+ // print();
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/sp-mesh-array.h b/src/sp-mesh-array.h
new file mode 100644
index 000000000..1771f3d1f
--- /dev/null
+++ b/src/sp-mesh-array.h
@@ -0,0 +1,197 @@
+#ifndef SEEN_SP_MESH_ARRAY_H
+#define SEEN_SP_MESH_ARRAY_H
+/*
+ * Authors:
+ * Tavmjong Bah <tavmjong@free.fr>
+ *
+ * Copyrigt (C) 2012 Tavmjong Bah
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+/**
+ A group of classes and functions for manipulating mesh gradients.
+
+ A mesh is made up of an array of patches. Each patch has four sides and four corners. The sides can
+ be shared between two patches and the corners between up to four.
+
+ The order of the points for each side always goes from left to right or top to bottom.
+ For sides 2 and 3 the points must be reversed when used (as in calls to cairo functions).
+
+ Two patches: (C=corner, S=side, H=handle, T=tensor)
+
+ C0 H1 H2 C1 C0 H1 H2 C1
+ + ---------- + ---------- +
+ | S0 | S0 |
+ H1 | T0 T1 |H1 T0 T1 | H1
+ |S3 S1|S3 S1|
+ H2 | T3 T2 |H2 T3 T2 | H2
+ | S2 | S2 |
+ + ---------- + ---------- +
+ C3 H1 H2 C2 C3 H1 H2 C2
+
+ The mesh is stored internally as an array of nodes that includes the tensor nodes.
+
+ Note: This code uses tensor points which are not part of the SVG2 plan at the moment.
+ Including tensor points was motivated by a desire to experiment with their usefulness
+ in smoothing color transitions. There doesn't seem to be much advantage for that
+ purpose. However including them internally allows for storing all the points in
+ an array which simplifies things like inserting new rows or columns.
+*/
+
+#include <gdk/gdk.h>
+#include <glibmm/ustring.h>
+#include <2geom/point.h>
+#include "color.h"
+
+// For color picking
+#include "sp-item.h"
+
+enum NodeType {
+ MG_NODE_TYPE_UNKNOWN,
+ MG_NODE_TYPE_CORNER,
+ MG_NODE_TYPE_HANDLE,
+ MG_NODE_TYPE_TENSOR
+};
+
+// Is a node along an edge?
+enum NodeEdge {
+ MG_NODE_EDGE_NONE,
+ MG_NODE_EDGE_TOP = 1,
+ MG_NODE_EDGE_LEFT = 2,
+ MG_NODE_EDGE_BOTTOM = 4,
+ MG_NODE_EDGE_RIGHT = 8
+};
+
+enum MeshCornerOperation {
+ MG_CORNER_SIDE_TOGGLE,
+ MG_CORNER_SIDE_ARC,
+ MG_CORNER_TENSOR_TOGGLE,
+ MG_CORNER_COLOR_SMOOTH,
+ MG_CORNER_COLOR_PICK
+};
+
+enum MeshNodeOperation {
+ MG_NODE_NO_SCALE,
+ MG_NODE_SCALE,
+ MG_NODE_SCALE_HANDLE
+};
+
+
+class SPMeshNode {
+public:
+ SPMeshNode() {
+ node_type = MG_NODE_TYPE_UNKNOWN;
+ node_edge = MG_NODE_EDGE_NONE;
+ set = false;
+ draggable = -1;
+ path_type = 'u';
+ opacity = 0.0;
+ }
+ NodeType node_type;
+ uint node_edge;
+ bool set;
+ Geom::Point p;
+ uint draggable; // index of on-screen node
+ gchar path_type;
+ SPColor color;
+ gdouble opacity;
+};
+
+
+// I for Internal to distinguish it from the Object class
+// This is a convenience class...
+class SPMeshPatchI {
+
+private:
+ std::vector<std::vector< SPMeshNode* > > *nodes;
+ int row;
+ int col;
+
+public:
+ SPMeshPatchI( std::vector<std::vector< SPMeshNode* > > *n, int r, int c );
+ Geom::Point getPoint( uint side, uint point );
+ std::vector< Geom::Point > getPointsForSide( uint i );
+ void setPoint( uint side, uint point, Geom::Point p, bool set = true );
+ gchar getPathType( uint i );
+ void setPathType( uint, gchar t );
+ Geom::Point getTensorPoint( uint i );
+ void setTensorPoint( uint i, Geom::Point p );
+ bool tensorIsSet();
+ bool tensorIsSet( uint i );
+ Geom::Point coonsTensorPoint( uint i );
+ void updateNodes();
+ SPColor getColor( uint i );
+ void setColor( uint i, SPColor c );
+ gdouble getOpacity( uint i );
+ void setOpacity( uint i, gdouble o );
+};
+
+struct SPMeshGradient;
+
+// An array of mesh nodes.
+class SPMeshNodeArray {
+
+// Should be private
+public:
+ SPMeshGradient *mg;
+ std::vector< std::vector< SPMeshNode* > > nodes;
+
+public:
+ // Draggables to nodes
+ bool drag_valid;
+ std::vector< SPMeshNode* > corners;
+ std::vector< SPMeshNode* > handles;
+ std::vector< SPMeshNode* > tensors;
+
+public:
+
+ friend class SPMeshPatchI;
+
+ SPMeshNodeArray() { built = false; mg = NULL; drag_valid = false; };
+ SPMeshNodeArray( SPMeshGradient *mg );
+ ~SPMeshNodeArray() { clear(); };
+ bool built;
+
+ void read( SPMeshGradient *mg );
+ void write( SPMeshGradient *mg );
+ void create( SPMeshGradient *mg, SPItem *item, Geom::OptRect bbox );
+ void clear();
+ void print();
+
+ // Get size of patch
+ uint patch_rows();
+ uint patch_columns();
+
+ SPMeshNode * node( uint i, uint j ) { return nodes[i][j]; }
+
+ // Operations on corners
+ bool adjacent_corners( uint i, uint j, SPMeshNode* n[4] );
+ uint side_toggle( std::vector< uint > );
+ uint side_arc( std::vector< uint > );
+ uint tensor_toggle( std::vector< uint > );
+ uint color_smooth( std::vector< uint > );
+ uint color_pick( std::vector< uint >, SPItem* );
+
+ // Update other nodes in response to a node move.
+ void update_handles( uint corner, std::vector< uint > selected_corners, Geom::Point old_p, MeshNodeOperation op );
+
+ void split_row( uint i, uint n );
+ void split_column( uint j, uint n );
+ void split_row( uint i, double coord );
+ void split_column( uint j, double coord );
+};
+
+#endif /* !SEEN_SP_MESH_ARRAY_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ c-basic-offset:2
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/sp-mesh-gradient-fns.h b/src/sp-mesh-gradient-fns.h
new file mode 100644
index 000000000..18f66128a
--- /dev/null
+++ b/src/sp-mesh-gradient-fns.h
@@ -0,0 +1,41 @@
+#ifndef SP_MESH_GRADIENT_FNS_H
+#define SP_MESH_GRADIENT_FNS_H
+
+/** \file
+ * Macros and fn definitions related to mesh gradients.
+ */
+
+#include <glib-object.h>
+#include <glib.h>
+
+namespace Inkscape {
+namespace XML {
+class Node;
+}
+}
+
+class SPMeshGradient;
+
+#define SP_TYPE_MESHGRADIENT (sp_meshgradient_get_type())
+#define SP_MESHGRADIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SP_TYPE_MESHGRADIENT, SPMeshGradient))
+#define SP_MESHGRADIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SP_TYPE_MESHGRADIENT, SPMeshGradientClass))
+#define SP_IS_MESHGRADIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SP_TYPE_MESHGRADIENT))
+#define SP_IS_MESHGRADIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SP_TYPE_MESHGRADIENT))
+
+
+GType sp_meshgradient_get_type();
+
+void sp_meshgradient_set_position(SPMeshGradient *mg, gdouble x, gdouble y );
+
+#endif /* !SP_MESH_GRADIENT_FNS_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/sp-mesh-gradient.h b/src/sp-mesh-gradient.h
new file mode 100644
index 000000000..44d6c0e4c
--- /dev/null
+++ b/src/sp-mesh-gradient.h
@@ -0,0 +1,35 @@
+#ifndef SP_MESH_GRADIENT_H
+#define SP_MESH_GRADIENT_H
+
+/** \file
+ * SPMeshGradient: SVG <meshgradient> implementation.
+ */
+
+#include "svg/svg-length.h"
+#include "sp-gradient.h"
+#include "sp-mesh-gradient-fns.h"
+
+/** Mesh gradient. */
+struct SPMeshGradient : public SPGradient {
+ SVGLength x; // Upper left corner of mesh
+ SVGLength y; // Upper right corner of mesh
+};
+
+/// The SPMeshGradient vtable.
+struct SPMeshGradientClass {
+ SPGradientClass parent_class;
+};
+
+
+#endif /* !SP_MESH_GRADIENT_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/sp-mesh-patch-fns.h b/src/sp-mesh-patch-fns.h
new file mode 100644
index 000000000..37fb5a70a
--- /dev/null
+++ b/src/sp-mesh-patch-fns.h
@@ -0,0 +1,39 @@
+#ifndef SP_MESH_PATCH_FNS_H
+#define SP_MESH_PATCH_FNS_H
+
+/** \file
+ * Macros and fn definitions related to mesh patchs.
+ */
+
+#include <glib-object.h>
+
+namespace Inkscape {
+namespace XML {
+class Node;
+}
+}
+
+class SPMeshPatch;
+
+#define SP_TYPE_MESHPATCH (sp_meshpatch_get_type())
+#define SP_MESHPATCH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SP_TYPE_MESHPATCH, SPMeshPatch))
+#define SP_MESHPATCH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SP_TYPE_MESHPATCH, SPMeshPatchClass))
+#define SP_IS_MESHPATCH(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SP_TYPE_MESHPATCH))
+#define SP_IS_MESHPATCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SP_TYPE_MESHPATCH))
+
+
+GType sp_meshpatch_get_type();
+
+
+#endif /* !SP_MESH_PATCH_FNS_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/sp-mesh-patch.cpp b/src/sp-mesh-patch.cpp
new file mode 100644
index 000000000..ff1a18a01
--- /dev/null
+++ b/src/sp-mesh-patch.cpp
@@ -0,0 +1,63 @@
+/** @file
+ * @gradient meshpatch class.
+ */
+/* Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * bulia byak
+ * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
+ * Jon A. Cruz <jon@joncruz.org>
+ * Tavmjong Bah <tavjong@free.fr>
+ *
+ * Copyright (C) 1999,2005 authors
+ * Copyright (C) 2010 Jon A. Cruz
+ * Copyright (C) 2012 Tavmjong Bah
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+#include "sp-mesh-patch.h"
+#include "style.h"
+
+SPMeshPatch* SPMeshPatch::getNextMeshPatch()
+{
+ SPMeshPatch *result = 0;
+
+ for (SPObject* obj = getNext(); obj && !result; obj = obj->getNext()) {
+ if (SP_IS_MESHPATCH(obj)) {
+ result = SP_MESHPATCH(obj);
+ }
+ }
+
+ return result;
+}
+
+SPMeshPatch* SPMeshPatch::getPrevMeshPatch()
+{
+ SPMeshPatch *result = 0;
+
+ for (SPObject* obj = getPrev(); obj; obj = obj->getPrev()) {
+ // The closest previous SPObject that is an SPMeshPatch *should* be ourself.
+ if (SP_IS_MESHPATCH(obj)) {
+ SPMeshPatch* meshpatch = SP_MESHPATCH(obj);
+ // Sanity check to ensure we have a proper sibling structure.
+ if (meshpatch->getNextMeshPatch() == this) {
+ result = meshpatch;
+ } else {
+ g_warning("SPMeshPatch previous/next relationship broken");
+ }
+ break;
+ }
+ }
+
+ return result;
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/sp-mesh-patch.h b/src/sp-mesh-patch.h
new file mode 100644
index 000000000..b56a0b95f
--- /dev/null
+++ b/src/sp-mesh-patch.h
@@ -0,0 +1,59 @@
+#ifndef SEEN_SP_MESHPATCH_H
+#define SEEN_SP_MESHPATCH_H
+
+/** \file
+ * SPMeshPatch: SVG <meshpatch> implementation.
+ */
+/*
+ * Authors: Tavmjong Bah
+ *
+ * Copyright (C) 2012 Tavmjong Bah
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glib.h>
+#include <glibmm/ustring.h>
+//#include "svg/svg-length.h"
+#include "sp-object.h"
+
+class SPObjectClass;
+
+struct SPMeshPatch;
+struct SPMeshPatchClass;
+
+#define SP_TYPE_MESHPATCH (sp_meshpatch_get_type())
+#define SP_MESHPATCH(o) (G_TYPE_CHECK_INSTANCE_CAST((o), SP_TYPE_MESHPATCH, SPMeshPatch))
+#define SP_MESHPATCH_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), SP_TYPE_MESHPATCH, SPMeshPatchClass))
+#define SP_IS_MESHPATCH(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), SP_TYPE_MESHPATCH))
+#define SP_IS_MESHPATCH_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), SP_TYPE_MESHPATCH))
+
+GType sp_meshpatch_get_type();
+
+/** Gradient MeshPatch. */
+struct SPMeshPatch : public SPObject {
+
+ SPMeshPatch* getNextMeshPatch();
+ SPMeshPatch* getPrevMeshPatch();
+ Glib::ustring * tensor_string;
+ //SVGLength tx[4]; // Tensor points
+ //SVGLength ty[4]; // Tensor points
+};
+
+/// The SPMeshPatch vtable.
+struct SPMeshPatchClass {
+ SPObjectClass parent_class;
+};
+
+#endif /* !SEEN_SP_MESHPATCH_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/sp-mesh-row-fns.h b/src/sp-mesh-row-fns.h
new file mode 100644
index 000000000..5d85fdfea
--- /dev/null
+++ b/src/sp-mesh-row-fns.h
@@ -0,0 +1,39 @@
+#ifndef SP_MESH_ROW_FNS_H
+#define SP_MESH_ROW_FNS_H
+
+/** \file
+ * Macros and fn definitions related to mesh rows.
+ */
+
+#include <glib-object.h>
+
+namespace Inkscape {
+namespace XML {
+class Node;
+}
+}
+
+class SPMeshRow;
+
+#define SP_TYPE_MESHROW (sp_meshrow_get_type())
+#define SP_MESHROW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SP_TYPE_MESHROW, SPMeshRow))
+#define SP_MESHROW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SP_TYPE_MESHROW, SPMeshRowClass))
+#define SP_IS_MESHROW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SP_TYPE_MESHROW))
+#define SP_IS_MESHROW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SP_TYPE_MESHROW))
+
+
+GType sp_meshrow_get_type();
+
+
+#endif /* !SP_MESH_ROW_FNS_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/sp-mesh-row.cpp b/src/sp-mesh-row.cpp
new file mode 100644
index 000000000..bc0c59776
--- /dev/null
+++ b/src/sp-mesh-row.cpp
@@ -0,0 +1,63 @@
+/** @file
+ * @gradient meshpatch class.
+ */
+/* Authors:
+ * Lauris Kaplinski <lauris@kaplinski.com>
+ * bulia byak
+ * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
+ * Jon A. Cruz <jon@joncruz.org>
+ * Tavmjong Bah <tavjong@free.fr>
+ *
+ * Copyright (C) 1999,2005 authors
+ * Copyright (C) 2010 Jon A. Cruz
+ * Copyright (C) 2012 Tavmjong Bah
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+#include "sp-mesh-row.h"
+#include "style.h"
+
+SPMeshRow* SPMeshRow::getNextMeshRow()
+{
+ SPMeshRow *result = 0;
+
+ for (SPObject* obj = getNext(); obj && !result; obj = obj->getNext()) {
+ if (SP_IS_MESHROW(obj)) {
+ result = SP_MESHROW(obj);
+ }
+ }
+
+ return result;
+}
+
+SPMeshRow* SPMeshRow::getPrevMeshRow()
+{
+ SPMeshRow *result = 0;
+
+ for (SPObject* obj = getPrev(); obj; obj = obj->getPrev()) {
+ // The closest previous SPObject that is an SPMeshRow *should* be ourself.
+ if (SP_IS_MESHROW(obj)) {
+ SPMeshRow* meshrow = SP_MESHROW(obj);
+ // Sanity check to ensure we have a proper sibling structure.
+ if (meshrow->getNextMeshRow() == this) {
+ result = meshrow;
+ } else {
+ g_warning("SPMeshRow previous/next relationship broken");
+ }
+ break;
+ }
+ }
+
+ return result;
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/sp-mesh-row.h b/src/sp-mesh-row.h
new file mode 100644
index 000000000..53e311bef
--- /dev/null
+++ b/src/sp-mesh-row.h
@@ -0,0 +1,53 @@
+#ifndef SEEN_SP_MESHROW_H
+#define SEEN_SP_MESHROW_H
+
+/** \file
+ * SPMeshRow: SVG <meshRow> implementation.
+ */
+/*
+ * Authors: Tavmjong Bah
+ * Copyright (C) 2012 Tavmjong Bah
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glib.h>
+#include "sp-object.h"
+
+class SPObjectClass;
+
+struct SPMeshRow;
+struct SPMeshRowClass;
+
+#define SP_TYPE_MESHROW (sp_meshrow_get_type())
+#define SP_MESHROW(o) (G_TYPE_CHECK_INSTANCE_CAST((o), SP_TYPE_MESHROW, SPMeshRow))
+#define SP_MESHROW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), SP_TYPE_MESHROW, SPMeshRowClass))
+#define SP_IS_MESHROW(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), SP_TYPE_MESHROW))
+#define SP_IS_MESHROW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), SP_TYPE_MESHROW))
+
+GType sp_meshrow_get_type();
+
+/** Gradient MeshRow. */
+struct SPMeshRow : public SPObject {
+
+ SPMeshRow* getNextMeshRow();
+ SPMeshRow* getPrevMeshRow();
+};
+
+/// The SPMeshRow vtable.
+struct SPMeshRowClass {
+ SPObjectClass parent_class;
+};
+
+#endif /* !SEEN_SP_MESHROW_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/sp-object-repr.cpp b/src/sp-object-repr.cpp
index b40017e65..16e64ff49 100644
--- a/src/sp-object-repr.cpp
+++ b/src/sp-object-repr.cpp
@@ -17,6 +17,9 @@
#include "sp-root.h"
#include "sp-image.h"
#include "sp-linear-gradient-fns.h"
+#include "sp-mesh-gradient-fns.h"
+#include "sp-mesh-row-fns.h"
+#include "sp-mesh-patch-fns.h"
#include "sp-path.h"
#include "sp-radial-gradient-fns.h"
#include "sp-rect.h"
@@ -186,6 +189,9 @@ populate_dtables()
{ "svg:linearGradient", SP_TYPE_LINEARGRADIENT },
{ "svg:marker", SP_TYPE_MARKER },
{ "svg:mask", SP_TYPE_MASK },
+ { "svg:meshGradient", SP_TYPE_MESHGRADIENT },
+ { "svg:meshRow", SP_TYPE_MESHROW },
+ { "svg:meshPatch", SP_TYPE_MESHPATCH },
{ "svg:metadata", SP_TYPE_METADATA },
{ "svg:path", SP_TYPE_PATH },
{ "svg:pattern", SP_TYPE_PATTERN },
diff --git a/src/sp-stop.h b/src/sp-stop.h
index ec6c4525f..c3b1e2753 100644
--- a/src/sp-stop.h
+++ b/src/sp-stop.h
@@ -43,6 +43,8 @@ struct SPStop : public SPObject {
/// \todo fixme: Implement SPSVGNumber or something similar.
gfloat opacity;
+ Glib::ustring * path_string;
+ //SPCurve path;
static SPColor readStopColor( Glib::ustring const &styleStr, guint32 dfl = 0 );
@@ -50,6 +52,7 @@ struct SPStop : public SPObject {
SPStop* getPrevStop();
SPColor getEffectiveColor() const;
+
};
/// The SPStop vtable.
diff --git a/src/tools-switch.cpp b/src/tools-switch.cpp
index 42eaf4474..cbf269387 100644
--- a/src/tools-switch.cpp
+++ b/src/tools-switch.cpp
@@ -49,6 +49,7 @@
#include "sp-text.h"
#include "sp-flowtext.h"
#include "gradient-context.h"
+#include "mesh-context.h"
#include "zoom-context.h"
#include "measure-context.h"
#include "dropper-context.h"
@@ -75,6 +76,7 @@ static char const *const tool_names[] = {
"/tools/calligraphic",
"/tools/text",
"/tools/gradient",
+ "/tools/mesh",
"/tools/zoom",
"/tools/measure",
"/tools/dropper",
@@ -204,7 +206,14 @@ tools_switch(SPDesktop *dt, int num)
inkscape_eventcontext_set(sp_desktop_event_context(dt));
dt->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Drag</b> or <b>double click</b> to create a gradient on selected objects, <b>drag handles</b> to adjust gradients."));
break;
+ case TOOLS_MESH:
+ dt->set_event_context(SP_TYPE_MESH_CONTEXT, tool_names[num]);
+ dt->activate_guides(false);
+ inkscape_eventcontext_set(sp_desktop_event_context(dt));
+ dt->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Drag</b> or <b>double click</b> to create a mesh on selected objects, <b>drag handles</b> to adjust meshes."));
+ break;
case TOOLS_ZOOM:
+ std::cout << "tools-switch.cpp: TOOLS_ZOOM" << std::endl;
dt->set_event_context(SP_TYPE_ZOOM_CONTEXT, tool_names[num]);
dt->activate_guides(false);
inkscape_eventcontext_set(sp_desktop_event_context(dt));
diff --git a/src/tools-switch.h b/src/tools-switch.h
index 9765def92..77fe370c6 100644
--- a/src/tools-switch.h
+++ b/src/tools-switch.h
@@ -34,6 +34,7 @@ enum {
TOOLS_CALLIGRAPHIC,
TOOLS_TEXT,
TOOLS_GRADIENT,
+ TOOLS_MESH,
TOOLS_ZOOM,
TOOLS_MEASURE,
TOOLS_DROPPER,
diff --git a/src/verbs.cpp b/src/verbs.cpp
index f2ff61293..96a6b76a1 100644
--- a/src/verbs.cpp
+++ b/src/verbs.cpp
@@ -1509,6 +1509,9 @@ void ContextVerb::perform(SPAction *action, void *data)
case SP_VERB_CONTEXT_GRADIENT:
tools_switch(dt, TOOLS_GRADIENT);
break;
+ case SP_VERB_CONTEXT_MESH:
+ tools_switch(dt, TOOLS_MESH);
+ break;
case SP_VERB_CONTEXT_ZOOM:
tools_switch(dt, TOOLS_ZOOM);
break;
@@ -1587,6 +1590,10 @@ void ContextVerb::perform(SPAction *action, void *data)
prefs->setInt("/dialogs/preferences/page", PREFS_PAGE_TOOLS_GRADIENT);
dt->_dlg_mgr->showDialog("InkscapePreferences");
break;
+ case SP_VERB_CONTEXT_MESH_PREFS:
+ prefs->setInt("/dialogs/preferences/page", PREFS_PAGE_TOOLS_GRADIENT);
+ dt->_dlg_mgr->showDialog("InkscapePreferences");
+ break;
case SP_VERB_CONTEXT_ZOOM_PREFS:
prefs->setInt("/dialogs/preferences/page", PREFS_PAGE_TOOLS_ZOOM);
dt->_dlg_mgr->showDialog("InkscapePreferences");
@@ -2512,6 +2519,8 @@ Verb *Verb::_base_verbs[] = {
N_("Create and edit text objects"), INKSCAPE_ICON("draw-text")),
new ContextVerb(SP_VERB_CONTEXT_GRADIENT, "ToolGradient", N_("Gradient"),
N_("Create and edit gradients"), INKSCAPE_ICON("color-gradient")),
+ new ContextVerb(SP_VERB_CONTEXT_MESH, "ToolMesh", N_("Mesh"),
+ N_("Create and edit meshes"), INKSCAPE_ICON("mesh-gradient")),
new ContextVerb(SP_VERB_CONTEXT_ZOOM, "ToolZoom", N_("Zoom"),
N_("Zoom in or out"), INKSCAPE_ICON("zoom")),
new ContextVerb(SP_VERB_CONTEXT_MEASURE, "ToolMeasure", NC_("Measurement tool", "Measure"),
@@ -2557,6 +2566,8 @@ Verb *Verb::_base_verbs[] = {
N_("Open Preferences for the Text tool"), NULL),
new ContextVerb(SP_VERB_CONTEXT_GRADIENT_PREFS, "GradientPrefs", N_("Gradient Preferences"),
N_("Open Preferences for the Gradient tool"), NULL),
+ new ContextVerb(SP_VERB_CONTEXT_MESH_PREFS, "Mesh_Prefs", N_("Mesh Preferences"),
+ N_("Open Preferences for the Mesh tool"), NULL),
new ContextVerb(SP_VERB_CONTEXT_ZOOM_PREFS, "ZoomPrefs", N_("Zoom Preferences"),
N_("Open Preferences for the Zoom tool"), NULL),
new ContextVerb(SP_VERB_CONTEXT_MEASURE_PREFS, "MeasurePrefs", N_("Measure Preferences"),
diff --git a/src/verbs.h b/src/verbs.h
index 1a9efdb81..8d65db642 100644
--- a/src/verbs.h
+++ b/src/verbs.h
@@ -178,6 +178,7 @@ enum {
SP_VERB_CONTEXT_CALLIGRAPHIC,
SP_VERB_CONTEXT_TEXT,
SP_VERB_CONTEXT_GRADIENT,
+ SP_VERB_CONTEXT_MESH,
SP_VERB_CONTEXT_ZOOM,
SP_VERB_CONTEXT_MEASURE,
SP_VERB_CONTEXT_DROPPER,
@@ -201,6 +202,7 @@ enum {
SP_VERB_CONTEXT_CALLIGRAPHIC_PREFS,
SP_VERB_CONTEXT_TEXT_PREFS,
SP_VERB_CONTEXT_GRADIENT_PREFS,
+ SP_VERB_CONTEXT_MESH_PREFS,
SP_VERB_CONTEXT_ZOOM_PREFS,
SP_VERB_CONTEXT_MEASURE_PREFS,
SP_VERB_CONTEXT_DROPPER_PREFS,
diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt
index 8ae6441c3..38180bd69 100644
--- a/src/widgets/CMakeLists.txt
+++ b/src/widgets/CMakeLists.txt
@@ -9,6 +9,7 @@ set(widgets_SRC
erasor-toolbar.cpp
lpe-toolbar.cpp
measure-toolbar.cpp
+ mesh-toolbar.cpp
node-toolbar.cpp
paintbucket-toolbar.cpp
pencil-toolbar.cpp
@@ -65,6 +66,7 @@ set(widgets_SRC
erasor-toolbar.h
lpe-toolbar.h
measure-toolbar.h
+ mesh-toolbar.h
node-toolbar.h
paintbucket-toolbar.h
pencil-toolbar.h
diff --git a/src/widgets/Makefile_insert b/src/widgets/Makefile_insert
index 439b5c3d4..7d6b413bd 100644
--- a/src/widgets/Makefile_insert
+++ b/src/widgets/Makefile_insert
@@ -42,6 +42,8 @@ ink_common_sources += \
widgets/lpe-toolbar.h \
widgets/measure-toolbar.cpp \
widgets/measure-toolbar.h \
+ widgets/mesh-toolbar.cpp \
+ widgets/mesh-toolbar.h \
widgets/node-toolbar.cpp \
widgets/node-toolbar.h \
widgets/paint-selector.cpp \
diff --git a/src/widgets/gradient-toolbar.h b/src/widgets/gradient-toolbar.h
index 938f45dc6..980a41a83 100644
--- a/src/widgets/gradient-toolbar.h
+++ b/src/widgets/gradient-toolbar.h
@@ -17,4 +17,4 @@ struct SPDesktop;
void sp_gradient_toolbox_prep(SPDesktop * /*desktop*/, GtkActionGroup* mainActions, GObject* holder);
-#endif /* !SEEN_SELECT_TOOLBAR_H */
+#endif /* !SEEN_GRADIENT_TOOLBAR_H */
diff --git a/src/widgets/mesh-toolbar.cpp b/src/widgets/mesh-toolbar.cpp
new file mode 100644
index 000000000..7c37d757c
--- /dev/null
+++ b/src/widgets/mesh-toolbar.cpp
@@ -0,0 +1,373 @@
+/*
+ * Gradient aux toolbar
+ *
+ * Authors:
+ * bulia byak <bulia@dr.com>
+ * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
+ * Abhishek Sharma
+ * Tavmjong Bah <tavjong@free.fr>
+ *
+ * Copyright (C) 2012 Tavmjong Bah
+ * Copyright (C) 2007 Johan Engelen
+ * Copyright (C) 2005 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+// REVIEW THESE AT END OF REWRITE
+#include "toolbox.h"
+#include "mesh-toolbar.h"
+
+#include "ui/widget/color-preview.h"
+#include "verbs.h"
+
+#include "macros.h"
+#include "widgets/button.h"
+#include "widgets/widget-sizes.h"
+#include "widgets/spw-utilities.h"
+#include "widgets/spinbutton-events.h"
+#include "widgets/gradient-vector.h"
+#include "widgets/gradient-image.h"
+#include "style.h"
+
+#include "preferences.h"
+#include "document-private.h"
+#include "document-undo.h"
+#include "desktop.h"
+#include "desktop-handles.h"
+#include <glibmm/i18n.h>
+
+#include "gradient-context.h"
+#include "gradient-drag.h"
+#include "sp-mesh-gradient.h"
+#include "gradient-chemistry.h"
+#include "gradient-selector.h"
+#include "selection.h"
+#include "ui/icon-names.h"
+
+#include "../ege-adjustment-action.h"
+#include "../ege-output-action.h"
+#include "../ege-select-one-action.h"
+#include "../ink-action.h"
+#include "../ink-comboboxentry-action.h"
+
+#include "sp-stop.h"
+#include "svg/css-ostringstream.h"
+#include "svg/svg-color.h"
+#include "desktop-style.h"
+#include "gradient-context.h"
+
+#include "toolbox.h"
+
+using Inkscape::DocumentUndo;
+using Inkscape::UI::ToolboxFactory;
+using Inkscape::UI::PrefPusher;
+
+static gboolean blocked = FALSE;
+
+//########################
+//## Mesh ##
+//########################
+
+/*
+ * Core function, setup all the widgets whenever something changes on the desktop
+ */
+static void ms_tb_selection_changed(Inkscape::Selection * /*selection*/, gpointer data)
+{
+ // DOES NOTHING AT MOMENT
+
+ // std::cout << "ms_tb_selection_changed" << std::endl;
+
+ // if (blocked)
+ // return;
+
+ // GtkWidget *widget = GTK_WIDGET(data);
+
+ // SPDesktop *desktop = static_cast<SPDesktop *>(g_object_get_data(G_OBJECT(widget), "desktop"));
+ // if (!desktop) {
+ // return;
+ // }
+
+ // Inkscape::Selection *selection = sp_desktop_selection(desktop); // take from desktop, not from args
+ // if (selection) {
+ // SPEventContext *ev = sp_desktop_event_context(desktop);
+ // GrDrag *drag = NULL;
+ // if (ev) {
+ // drag = ev->get_drag();
+ // // Hide/show handles?
+ // }
+
+ // }
+}
+
+
+static void ms_tb_selection_modified(Inkscape::Selection *selection, guint /*flags*/, gpointer data)
+{
+ ms_tb_selection_changed(selection, data);
+}
+
+static void ms_drag_selection_changed(gpointer /*dragger*/, gpointer data)
+{
+ ms_tb_selection_changed(NULL, data);
+
+}
+
+static void ms_defs_release(SPObject * defs, GtkWidget *widget)
+{
+ ms_tb_selection_changed(NULL, (gpointer) widget);
+}
+
+static void ms_defs_modified(SPObject * /*defs*/, guint /*flags*/, GtkWidget *widget)
+{
+ ms_tb_selection_changed(NULL, (gpointer) widget);
+}
+
+static void ms_disconnect_sigc(GObject * /*obj*/, sigc::connection *connection) {
+ connection->disconnect();
+ delete connection;
+}
+
+
+/*
+ * Callback functions for user actions
+ */
+
+static void ms_new_type_changed( EgeSelectOneAction *act, GObject * /*tbl*/ )
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ gint typemode = ege_select_one_action_get_active( act ) == 0 ? SP_GRADIENT_MESH_TYPE_NORMAL : SP_GRADIENT_MESH_TYPE_CONICAL;
+ prefs->setInt("/tools/mesh/mesh_type", typemode);
+}
+
+static void ms_new_fillstroke_changed( EgeSelectOneAction *act, GObject * /*tbl*/ )
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ Inkscape::PaintTarget fsmode = (ege_select_one_action_get_active( act ) == 0) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE;
+ prefs->setInt("/tools/gradient/newfillorstroke", (fsmode == Inkscape::FOR_FILL) ? 1 : 0);
+}
+
+static void ms_row_changed (GtkAdjustment *adj, GObject *tbl ) {
+
+ if (blocked) {
+ return;
+ }
+
+ blocked = TRUE;
+
+ int rows = gtk_adjustment_get_value(adj);
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
+ prefs->setInt("/tools/mesh/mesh_rows", rows);
+
+ blocked = FALSE;
+}
+
+static void ms_col_changed (GtkAdjustment *adj, GObject *tbl ) {
+
+ if (blocked) {
+ return;
+ }
+
+ blocked = TRUE;
+
+ int cols = gtk_adjustment_get_value(adj);
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+
+ prefs->setInt("/tools/mesh/mesh_cols", cols);
+
+ blocked = FALSE;
+}
+
+static void ms_edit_fillstroke_changed( EgeSelectOneAction *act, GObject * /*tbl*/ )
+{
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ Inkscape::PaintTarget fsmode = (ege_select_one_action_get_active( act ) == 0) ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE;
+ prefs->setInt("/tools/gradient/editfillorstroke", (fsmode == Inkscape::FOR_FILL) ? 1 : 0);
+}
+
+/**
+ * Mesh auxiliary toolbar construction and setup.
+ *
+ */
+void sp_mesh_toolbox_prep(SPDesktop * desktop, GtkActionGroup* mainActions, GObject* holder)
+{
+ Inkscape::IconSize secondarySize = ToolboxFactory::prefToSize("/toolbox/secondary", 1);
+
+ EgeAdjustmentAction* eact = 0;
+
+ /* New mesh: normal or conical */
+ {
+ GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
+
+ GtkTreeIter iter;
+ gtk_list_store_append( model, &iter );
+ gtk_list_store_set( model, &iter,
+ 0, _("normal"), 1, _("Create mesh gradient"), 2, INKSCAPE_ICON("paint-gradient-mesh"), -1 );
+
+ gtk_list_store_append( model, &iter );
+ gtk_list_store_set( model, &iter,
+ 0, _("conical"), 1, _("Create conical gradient"), 2, INKSCAPE_ICON("paint-gradient-conical"), -1 );
+
+ EgeSelectOneAction* act = ege_select_one_action_new( "MeshNewTypeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
+ g_object_set( act, "short_label", _("New:"), NULL );
+ gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
+ g_object_set_data( holder, "mesh_new_type_action", act );
+
+ ege_select_one_action_set_appearance( act, "full" );
+ ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
+ g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
+ ege_select_one_action_set_icon_column( act, 2 );
+ ege_select_one_action_set_tooltip_column( act, 1 );
+
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ gint mode = prefs->getInt("/tools/mesh/mesh_type", SP_GRADIENT_MESH_TYPE_NORMAL) != SP_GRADIENT_MESH_TYPE_NORMAL;
+ ege_select_one_action_set_active( act, mode );
+ g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(ms_new_type_changed), holder );
+ }
+
+ /* New gradient on fill or stroke*/
+ {
+ GtkListStore* model = gtk_list_store_new( 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING );
+
+ GtkTreeIter iter;
+ gtk_list_store_append( model, &iter );
+ gtk_list_store_set( model, &iter,
+ 0, _("fill"), 1, _("Create gradient in the fill"), 2, INKSCAPE_ICON("object-fill"), -1 );
+
+ gtk_list_store_append( model, &iter );
+ gtk_list_store_set( model, &iter,
+ 0, _("stroke"), 1, _("Create gradient in the stroke"), 2, INKSCAPE_ICON("object-stroke"), -1 );
+
+ EgeSelectOneAction* act = ege_select_one_action_new( "MeshNewFillStrokeAction", (""), (""), NULL, GTK_TREE_MODEL(model) );
+ g_object_set( act, "short_label", _("on:"), NULL );
+ gtk_action_group_add_action( mainActions, GTK_ACTION(act) );
+ g_object_set_data( holder, "mesh_new_fillstroke_action", act );
+
+ ege_select_one_action_set_appearance( act, "full" );
+ ege_select_one_action_set_radio_action_type( act, INK_RADIO_ACTION_TYPE );
+ g_object_set( G_OBJECT(act), "icon-property", "iconId", NULL );
+ ege_select_one_action_set_icon_column( act, 2 );
+ ege_select_one_action_set_tooltip_column( act, 1 );
+
+ /// @todo Convert to boolean?
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ bool fillstrokemode = !prefs->getBool("/tools/gradient/newfillorstroke", true);
+ ege_select_one_action_set_active( act, fillstrokemode );
+ g_signal_connect_after( G_OBJECT(act), "changed", G_CALLBACK(ms_new_fillstroke_changed), holder );
+ }
+
+ /* Number of mesh rows */
+ {
+ gchar const* labels[] = {};
+ gdouble values[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ eact = create_adjustment_action( "MeshRowAction",
+ _("Rows"), _("Rows:"), _("Number of rows in new mesh"),
+ "/tools/mesh/mesh_rows", 1,
+ GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
+ 1, 20, 1, 1,
+ labels, values, G_N_ELEMENTS(labels),
+ ms_row_changed,
+ 1.0, 0 );
+ gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
+ gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
+ }
+
+ /* Number of mesh columns */
+ {
+ gchar const* labels[] = {};
+ gdouble values[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+ eact = create_adjustment_action( "MeshColumnAction",
+ _("Columns"), _("Columns:"), _("Number of columns in new mesh"),
+ "/tools/mesh/mesh_cols", 1,
+ GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
+ 1, 20, 1, 1,
+ labels, values, G_N_ELEMENTS(labels),
+ ms_col_changed,
+ 1.0, 0 );
+ gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
+ gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
+ }
+
+ /* Edit fill mesh */
+ {
+ InkToggleAction* act = ink_toggle_action_new( "MeshEditFillAction",
+ _("Edit Fill"),
+ _("Edit fill mesh"),
+ INKSCAPE_ICON("object-fill"),
+ secondarySize );
+ gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
+ PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/mesh/edit_fill");
+ g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
+ }
+
+ /* Edit stroke mesh */
+ {
+ InkToggleAction* act = ink_toggle_action_new( "MeshEditStrokeAction",
+ _("Edit Stroke"),
+ _("Edit stroke mesh"),
+ INKSCAPE_ICON("object-stroke"),
+ secondarySize );
+ gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
+ PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/mesh/edit_stroke");
+ g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
+ }
+
+ /* Show/hide side and tensor handles */
+ {
+ InkToggleAction* act = ink_toggle_action_new( "MeshShowHandlesAction",
+ _("Show Handles"),
+ _("Show side and tensor handles"),
+ INKSCAPE_ICON("show-node-handles"),
+ secondarySize );
+ gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
+ PrefPusher *pusher = new PrefPusher(GTK_TOGGLE_ACTION(act), "/tools/mesh/show_handles");
+ g_signal_connect( holder, "destroy", G_CALLBACK(delete_prefspusher), pusher);
+ }
+
+
+ Inkscape::Selection *selection = sp_desktop_selection (desktop);
+ SPDocument *document = sp_desktop_document (desktop);
+
+ g_object_set_data(holder, "desktop", desktop);
+
+ // connect to selection modified and changed signals
+ sigc::connection *conn1 = new sigc::connection(
+ selection->connectChanged(sigc::bind(sigc::ptr_fun(&ms_tb_selection_changed), (gpointer) holder)));
+ sigc::connection *conn2 = new sigc::connection(
+ selection->connectModified(sigc::bind(sigc::ptr_fun(&ms_tb_selection_modified), (gpointer) holder)));
+ sigc::connection *conn3 = new sigc::connection(
+ desktop->connectToolSubselectionChanged( sigc::bind(sigc::ptr_fun(&ms_drag_selection_changed), (gpointer) holder)));
+
+ // when holder is destroyed, disconnect
+ g_signal_connect(G_OBJECT(holder), "destroy", G_CALLBACK(ms_disconnect_sigc), conn1);
+ g_signal_connect(G_OBJECT(holder), "destroy", G_CALLBACK(ms_disconnect_sigc), conn2);
+ g_signal_connect(G_OBJECT(holder), "destroy", G_CALLBACK(ms_disconnect_sigc), conn3);
+
+ // connect to release and modified signals of the defs (i.e. when someone changes mesh)
+ sigc::connection *release_connection = new sigc::connection();
+ *release_connection = document->getDefs()->connectRelease(sigc::bind<1>(sigc::ptr_fun(&ms_defs_release), GTK_WIDGET(holder)));
+ sigc::connection *modified_connection = new sigc::connection();
+ *modified_connection = document->getDefs()->connectModified(sigc::bind<2>(sigc::ptr_fun(&ms_defs_modified), GTK_WIDGET(holder)));
+
+ // when holder is destroyed, disconnect
+ g_signal_connect(G_OBJECT(holder), "destroy", G_CALLBACK(ms_disconnect_sigc), release_connection);
+ g_signal_connect(G_OBJECT(holder), "destroy", G_CALLBACK(ms_disconnect_sigc), modified_connection);
+
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/widgets/mesh-toolbar.h b/src/widgets/mesh-toolbar.h
new file mode 100644
index 000000000..277525804
--- /dev/null
+++ b/src/widgets/mesh-toolbar.h
@@ -0,0 +1,22 @@
+#ifndef SEEN_MESH_TOOLBAR_H
+#define SEEN_MESH_TOOLBAR_H
+
+/*
+ * Mesh aux toolbar
+ *
+ * Authors:
+ * bulia byak <bulia@dr.com>
+ * Tavmjong Bah <tavmjong@free.fr>
+ *
+ * Copyright (C) 2012 authors
+ * Copyright (C) 2005 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <gtk/gtk.h>
+struct SPDesktop;
+
+void sp_mesh_toolbox_prep( SPDesktop * /*desktop*/, GtkActionGroup* mainActions, GObject* holder);
+
+#endif /* !SEEN_MESH_TOOLBAR_H */
diff --git a/src/widgets/toolbox.cpp b/src/widgets/toolbox.cpp
index 78d0683f3..f8aed71a8 100644
--- a/src/widgets/toolbox.cpp
+++ b/src/widgets/toolbox.cpp
@@ -76,6 +76,7 @@
#include "erasor-toolbar.h"
#include "gradient-toolbar.h"
#include "lpe-toolbar.h"
+#include "mesh-toolbar.h"
#include "measure-toolbar.h"
#include "node-toolbar.h"
#include "rect-toolbar.h"
@@ -157,6 +158,7 @@ static struct {
{ "SPTextContext", "text_tool", SP_VERB_CONTEXT_TEXT, SP_VERB_CONTEXT_TEXT_PREFS },
{ "SPConnectorContext","connector_tool", SP_VERB_CONTEXT_CONNECTOR, SP_VERB_CONTEXT_CONNECTOR_PREFS },
{ "SPGradientContext", "gradient_tool", SP_VERB_CONTEXT_GRADIENT, SP_VERB_CONTEXT_GRADIENT_PREFS },
+ { "SPMeshContext", "mesh_tool", SP_VERB_CONTEXT_MESH, SP_VERB_CONTEXT_MESH_PREFS },
{ "SPDropperContext", "dropper_tool", SP_VERB_CONTEXT_DROPPER, SP_VERB_CONTEXT_DROPPER_PREFS },
{ NULL, NULL, 0, 0 }
};
@@ -209,8 +211,10 @@ static struct {
SP_VERB_INVALID, 0, 0},
{ "SPConnectorContext", "connector_toolbox", 0, sp_connector_toolbox_prep, "ConnectorToolbar",
SP_VERB_INVALID, 0, 0},
- { "SPGradientContext", "gradient_toolbox", 0, sp_gradient_toolbox_prep, "GradientToolbar",
- SP_VERB_INVALID, 0, 0},
+ { "SPGradientContext", "gradient_toolbox", 0, sp_gradient_toolbox_prep, "GradientToolbar",
+ SP_VERB_INVALID, 0, 0},
+ { "SPMeshContext", "mesh_toolbox", 0, sp_mesh_toolbox_prep, "MeshToolbar",
+ SP_VERB_INVALID, 0, 0},
{ "SPFloodContext", "paintbucket_toolbox", 0, sp_paintbucket_toolbox_prep, "PaintbucketToolbar",
SP_VERB_CONTEXT_PAINTBUCKET_PREFS, "/tools/paintbucket", N_("Style of Paint Bucket fill objects")},
{ NULL, NULL, NULL, NULL, NULL, SP_VERB_INVALID, NULL, NULL }
@@ -506,6 +510,18 @@ static gchar const * ui_descr =
" <toolitem action='GradientEditDeleteAction' />"
" </toolbar>"
+ " <toolbar name='MeshToolbar'>"
+ " <toolitem action='MeshNewTypeAction' />"
+ " <toolitem action='MeshNewFillStrokeAction' />"
+ " <toolitem action='MeshRowAction' />"
+ " <toolitem action='MeshColumnAction' />"
+ " <separator />"
+// " <toolitem action='MeshEditFillAction' />"
+// " <toolitem action='MeshEditStrokeAction' />"
+// " <toolitem action='MeshShowHandlesAction' />"
+ " <separator />"
+ " </toolbar>"
+
" <toolbar name='DropperToolbar'>"
" <toolitem action='DropperOpacityAction' />"
" <toolitem action='DropperPickAlphaAction' />"
@@ -1330,6 +1346,9 @@ void setup_tool_toolbox(GtkWidget *toolbox, SPDesktop *desktop)
" <!-- Fill -->"
" <toolitem action='ToolPaintBucket' />"
" <toolitem action='ToolGradient' />"
+#ifdef WITH_MESH
+ " <toolitem action='ToolMesh' />"
+#endif
" <toolitem action='ToolDropper' />"
" <toolitem action='ToolConnector' />"