summaryrefslogtreecommitdiffstats
path: root/src/ui/tools
diff options
context:
space:
mode:
authorJabiertxof <jtx@jtx>2017-01-24 14:16:06 +0000
committerJabiertxof <jtx@jtx>2017-01-24 14:16:06 +0000
commitb25ebda10578c5d697db9716e3c2b70950d33e45 (patch)
tree4635b8b3f65600cfd54b5465b906ae42165b0674 /src/ui/tools
parentFix some bugs (diff)
parentfix nodes reverting back during editing (diff)
downloadinkscape-b25ebda10578c5d697db9716e3c2b70950d33e45.tar.gz
inkscape-b25ebda10578c5d697db9716e3c2b70950d33e45.zip
fixing to new trunk
(bzr r15142.1.38)
Diffstat (limited to 'src/ui/tools')
-rw-r--r--src/ui/tools/calligraphic-tool.cpp4
-rw-r--r--src/ui/tools/eraser-tool.cpp12
-rw-r--r--src/ui/tools/flood-tool.cpp10
-rw-r--r--src/ui/tools/gradient-tool.cpp18
-rw-r--r--src/ui/tools/mesh-tool.cpp412
-rw-r--r--src/ui/tools/mesh-tool.h27
-rw-r--r--src/ui/tools/node-tool.cpp34
-rw-r--r--src/ui/tools/node-tool.h9
-rw-r--r--src/ui/tools/spray-tool.cpp4
-rw-r--r--src/ui/tools/tweak-tool.cpp185
10 files changed, 450 insertions, 265 deletions
diff --git a/src/ui/tools/calligraphic-tool.cpp b/src/ui/tools/calligraphic-tool.cpp
index 9b4dbb1a2..c2b86b538 100644
--- a/src/ui/tools/calligraphic-tool.cpp
+++ b/src/ui/tools/calligraphic-tool.cpp
@@ -921,10 +921,10 @@ void CalligraphicTool::set_to_accumulated(bool unionize, bool subtract) {
if (unionize) {
desktop->getSelection()->add(this->repr);
- sp_selected_path_union_skip_undo(desktop->getSelection());
+ desktop->getSelection()->pathUnion(true);
} else if (subtract) {
desktop->getSelection()->add(this->repr);
- sp_selected_path_diff_skip_undo(desktop->getSelection());
+ desktop->getSelection()->pathDiff(true);
} else {
if (this->keep_selected) {
desktop->getSelection()->set(this->repr);
diff --git a/src/ui/tools/eraser-tool.cpp b/src/ui/tools/eraser-tool.cpp
index 4f941e534..ae312e054 100644
--- a/src/ui/tools/eraser-tool.cpp
+++ b/src/ui/tools/eraser-tool.cpp
@@ -714,7 +714,7 @@ void EraserTool::set_to_accumulated() {
Inkscape::GC::release(dup); // parent takes over
selection->set(dup);
if (!this->nowidth) {
- sp_selected_path_union_skip_undo(selection);
+ selection->pathUnion(true);
}
selection->add(item);
if(item->style->fill_rule.value == SP_WIND_RULE_EVENODD){
@@ -725,9 +725,9 @@ void EraserTool::set_to_accumulated() {
css = 0;
}
if (this->nowidth) {
- sp_selected_path_cut_skip_undo(selection);
+ selection->pathCut(true);
} else {
- sp_selected_path_diff_skip_undo(selection);
+ selection->pathDiff(true);
}
workDone = true; // TODO set this only if something was cut.
bool break_apart = prefs->getBool("/tools/eraser/break_apart", false);
@@ -762,7 +762,7 @@ void EraserTool::set_to_accumulated() {
this->repr->parent()->appendChild(dup);
Inkscape::GC::release(dup); // parent takes over
selection->set(dup);
- sp_selected_path_union_skip_undo(selection);
+ selection->pathUnion(true);
if (bbox && bbox->intersects(*eraserBbox)) {
SPClipPath *clip_path = item->clip_ref->getObject();
if (clip_path) {
@@ -786,7 +786,7 @@ void EraserTool::set_to_accumulated() {
sp_object_unref(clip_path);
selection->raiseToTop(true);
selection->add(dup_clip);
- sp_selected_path_diff_skip_undo(selection);
+ selection->pathDiff(true);
SPItem * clip = SP_ITEM(*(selection->items().begin()));
}
}
@@ -802,7 +802,7 @@ void EraserTool::set_to_accumulated() {
rect->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
selection->raiseToTop(true);
selection->add(rect);
- sp_selected_path_diff_skip_undo(selection);
+ selection->pathDiff(true);
}
selection->raiseToTop(true);
selection->add(item);
diff --git a/src/ui/tools/flood-tool.cpp b/src/ui/tools/flood-tool.cpp
index 0b893a7ba..1801a0ea0 100644
--- a/src/ui/tools/flood-tool.cpp
+++ b/src/ui/tools/flood-tool.cpp
@@ -89,10 +89,10 @@ Glib::ustring ch_init[8] = {
const std::vector<Glib::ustring> FloodTool::channel_list( ch_init, ch_init+8 );
Glib::ustring gap_init[4] = {
- C_("Flood autogap", "None"),
- C_("Flood autogap", "Small"),
- C_("Flood autogap", "Medium"),
- C_("Flood autogap", "Large")
+ NC_("Flood autogap", "None"),
+ NC_("Flood autogap", "Small"),
+ NC_("Flood autogap", "Medium"),
+ NC_("Flood autogap", "Large")
};
const std::vector<Glib::ustring> FloodTool::gap_list( gap_init, gap_init+4 );
@@ -446,7 +446,7 @@ static void do_trace(bitmap_coords_info bci, guchar *trace_px, SPDesktop *deskto
ngettext("Area filled, path with <b>%d</b> node created and unioned with selection.","Area filled, path with <b>%d</b> nodes created and unioned with selection.",
SP_PATH(reprobj)->nodesInPath()), SP_PATH(reprobj)->nodesInPath() );
selection->add(reprobj);
- sp_selected_path_union_skip_undo(desktop->getSelection());
+ selection->pathUnion(true);
} else {
desktop->messageStack()->flashF( Inkscape::WARNING_MESSAGE,
ngettext("Area filled, path with <b>%d</b> node created.","Area filled, path with <b>%d</b> nodes created.",
diff --git a/src/ui/tools/gradient-tool.cpp b/src/ui/tools/gradient-tool.cpp
index 16df225e0..750596808 100644
--- a/src/ui/tools/gradient-tool.cpp
+++ b/src/ui/tools/gradient-tool.cpp
@@ -79,6 +79,9 @@ GradientTool::~GradientTool() {
delete this->subselcon;
}
+// This must match GrPointType enum sp-gradient.h
+// We should move this to a shared header (can't simply move to gradient.h since that would require
+// including <glibmm/i18n.h> which messes up "N_" in extensions... argh!).
const gchar *gr_handle_descr [] = {
N_("Linear gradient <b>start</b>"), //POINT_LG_BEGIN
N_("Linear gradient <b>end</b>"),
@@ -88,7 +91,10 @@ const gchar *gr_handle_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>")
};
void GradientTool::selection_changed(Inkscape::Selection*) {
@@ -831,6 +837,16 @@ bool GradientTool::root_handler(GdkEvent* event) {
ret = TRUE;
break;
+ case GDK_KEY_i:
+ case GDK_KEY_I:
+ if (MOD__SHIFT_ONLY(event)) {
+ // Shift+I - insert stops (alternate keybinding for keyboards
+ // that don't have the Insert key)
+ sp_gradient_context_add_stops_between_selected_stops (this);
+ ret = TRUE;
+ }
+ break;
+
case GDK_KEY_Delete:
case GDK_KEY_KP_Delete:
case GDK_KEY_BackSpace:
diff --git a/src/ui/tools/mesh-tool.cpp b/src/ui/tools/mesh-tool.cpp
index c6983b94a..e628094d9 100644
--- a/src/ui/tools/mesh-tool.cpp
+++ b/src/ui/tools/mesh-tool.cpp
@@ -41,6 +41,7 @@
#include "sp-text.h"
#include "sp-defs.h"
#include "style.h"
+#include "ui/control-manager.h"
// Gradient specific
#include "gradient-drag.h"
@@ -74,6 +75,9 @@ MeshTool::MeshTool()
: ToolBase(cursor_gradient_xpm, 4, 4)
, cursor_addnode(false)
, node_added(false)
+ , show_handles(true)
+ , edit_fill(true)
+ , edit_stroke(true)
// TODO: Why are these connections stored as pointers?
, selcon(NULL)
, subselcon(NULL)
@@ -92,7 +96,19 @@ MeshTool::~MeshTool() {
delete this->subselcon;
}
+// This must match GrPointType enum sp-gradient.h
+// We should move this to a shared header (can't simply move to gradient.h since that would require
+// including <glibmm/i18n.h> which messes up "N_" in extensions... argh!).
const gchar *ms_handle_descr [] = {
+ N_("Linear gradient <b>start</b>"), //POINT_LG_BEGIN
+ N_("Linear gradient <b>end</b>"),
+ N_("Linear gradient <b>mid stop</b>"),
+ N_("Radial gradient <b>center</b>"),
+ N_("Radial gradient <b>radius</b>"),
+ 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_("Mesh gradient <b>corner</b>"),
N_("Mesh gradient <b>handle</b>"),
N_("Mesh gradient <b>tensor</b>")
@@ -240,7 +256,25 @@ void MeshTool::setup() {
)
));
+ sp_event_context_read(this, "show_handles");
+ sp_event_context_read(this, "edit_fill");
+ sp_event_context_read(this, "edit_stroke");
+
this->selection_changed(selection);
+
+}
+
+void MeshTool::set(const Inkscape::Preferences::Entry& value) {
+ Glib::ustring entry_name = value.getEntryName();
+ if (entry_name == "show_handles") {
+ this->show_handles = value.getBool(true);
+ } else if (entry_name == "edit_fill") {
+ this->edit_fill = value.getBool(true);
+ } else if (entry_name == "edit_stroke") {
+ this->edit_stroke = value.getBool(true);
+ } else {
+ ToolBase::set(value);
+ }
}
void
@@ -266,32 +300,39 @@ sp_mesh_context_select_prev (ToolBase *event_context)
}
/**
-Returns true if mouse cursor over mesh edge.
+Returns vector of control lines mouse is over. Returns only first if 'first' is true.
*/
-static bool
-sp_mesh_context_is_over_line (MeshTool *rc, SPCtrlLine *line, Geom::Point event_p)
+static std::vector<SPCtrlCurve *>
+sp_mesh_context_over_line (MeshTool *rc, Geom::Point event_p, bool first = true)
{
- if (!SP_IS_CTRLCURVE(line) ) {
- return false;
- }
-
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(line);
- Geom::BezierCurveN<3> b( curve->p0, curve->p1, curve->p2, curve->p3 );
- Geom::Coord coord = b.nearestTime( rc->mousepoint_doc ); // Coord == double
- Geom::Point nearest = b( coord );
+ double tolerance = (double) SP_EVENT_CONTEXT(rc)->tolerance;
+
+ GrDrag *drag = rc->_grdrag;
- double dist_screen = Geom::L2 (rc->mousepoint_doc - nearest) * desktop->current_zoom();
+ std::vector<SPCtrlCurve *> selected;
- double tolerance = (double) SP_EVENT_CONTEXT(rc)->tolerance;
+ for (std::vector<SPCtrlLine *>::const_iterator l = drag->lines.begin(); l != drag->lines.end(); ++l) {
+ if (!SP_IS_CTRLCURVE(*l)) continue;
- bool close = (dist_screen < tolerance);
+ SPCtrlCurve *curve = SP_CTRLCURVE(*l);
+ Geom::BezierCurveN<3> b( curve->p0, curve->p1, curve->p2, curve->p3 );
+ Geom::Coord coord = b.nearestTime( rc->mousepoint_doc ); // Coord == double
+ Geom::Point nearest = b( coord );
- return close;
+ double dist_screen = Geom::L2 (rc->mousepoint_doc - nearest) * desktop->current_zoom();
+ if (dist_screen < tolerance) {
+ selected.push_back(curve);
+ if (first) {
+ break;
+ }
+ }
+ }
+ return selected;
}
@@ -336,12 +377,13 @@ sp_mesh_context_corner_operation (MeshTool *rc, MeshCornerOperation operation )
std::map<SPMeshGradient*, std::vector<guint> > points;
std::map<SPMeshGradient*, SPItem*> items;
-
+ std::map<SPMeshGradient*, Inkscape::PaintTarget> fill_or_stroke;
+
// Get list of selected draggers for each mesh.
- // For all selected draggers
+ // For all selected draggers (a dragger may include draggerables from different meshes).
for (std::set<GrDragger *>::const_iterator i = drag->selected.begin(); i != drag->selected.end(); ++i) {
GrDragger *dragger = *i;
- // For all draggables of dragger
+ // For all draggables of dragger (a draggable corresponds to a unique mesh).
for (std::vector<GrDraggable *>::const_iterator j = dragger->draggables.begin(); j != dragger->draggables.end() ; ++j) {
GrDraggable *d = *j;
@@ -354,6 +396,7 @@ sp_mesh_context_corner_operation (MeshTool *rc, MeshCornerOperation operation )
// Collect points together for same gradient
points[gradient].push_back( d->point_i );
items[gradient] = d->item;
+ fill_or_stroke[gradient] = d->fill_or_stroke ? Inkscape::FOR_FILL: Inkscape::FOR_STROKE;
}
}
@@ -389,6 +432,11 @@ sp_mesh_context_corner_operation (MeshTool *rc, MeshCornerOperation operation )
noperation += mg->array.color_pick( iter->second, items[iter->first] );
break;
+ case MG_CORNER_INSERT:
+ // std::cout << "INSERT" << std::endl;
+ noperation += mg->array.insert( iter->second );
+ break;
+
default:
std::cout << "sp_mesh_corner_operation: unknown operation" << std::endl;
}
@@ -402,22 +450,31 @@ sp_mesh_context_corner_operation (MeshTool *rc, MeshCornerOperation operation )
case MG_CORNER_SIDE_TOGGLE:
DocumentUndo::done(doc, SP_VERB_CONTEXT_MESH, _("Toggled mesh path type."));
+ drag->local_change = true; // Don't create new draggers.
break;
case MG_CORNER_SIDE_ARC:
DocumentUndo::done(doc, SP_VERB_CONTEXT_MESH, _("Approximated arc for mesh side."));
+ drag->local_change = true; // Don't create new draggers.
break;
case MG_CORNER_TENSOR_TOGGLE:
DocumentUndo::done(doc, SP_VERB_CONTEXT_MESH, _("Toggled mesh tensors."));
+ drag->local_change = true; // Don't create new draggers.
break;
case MG_CORNER_COLOR_SMOOTH:
DocumentUndo::done(doc, SP_VERB_CONTEXT_MESH, _("Smoothed mesh corner color."));
+ drag->local_change = true; // Don't create new draggers.
break;
case MG_CORNER_COLOR_PICK:
DocumentUndo::done(doc, SP_VERB_CONTEXT_MESH, _("Picked mesh corner color."));
+ drag->local_change = true; // Don't create new draggers.
+ break;
+
+ case MG_CORNER_INSERT:
+ DocumentUndo::done(doc, SP_VERB_CONTEXT_MESH, _("Inserted new row or column."));
break;
default:
@@ -426,7 +483,9 @@ sp_mesh_context_corner_operation (MeshTool *rc, MeshCornerOperation operation )
}
}
}
- drag->updateDraggers();
+
+ // Not needed. Update is done via gr_drag_sel_modified().
+ // drag->updateDraggers();
}
@@ -456,38 +515,33 @@ sp_mesh_context_fit_mesh_in_bbox (MeshTool *rc)
SPItem *item = *i;
SPStyle *style = item->style;
- if (style && (style->fill.isPaintserver())) {
- SPPaintServer *server = item->style->getFillPaintServer();
- if ( SP_IS_MESHGRADIENT(server) ) {
-
- SPMeshGradient *gradient = SP_MESHGRADIENT(server);
- SPCurve * outline = gradient->array.outline_path();
- Geom::OptRect mesh_bbox = outline->get_pathvector().boundsExact();
- outline->unref();
- Geom::OptRect item_bbox = item->geometricBounds();
-
- if ((*mesh_bbox).width() == 0) {
- continue;
- }
- if ((*mesh_bbox).height() == 0) {
- continue;
+ if (style) {
+
+ if (style->fill.isPaintserver()) {
+ SPPaintServer *server = item->style->getFillPaintServer();
+ if ( SP_IS_MESHGRADIENT(server) ) {
+
+ Geom::OptRect item_bbox = item->geometricBounds();
+ SPMeshGradient *gradient = SP_MESHGRADIENT(server);
+ if (gradient->array.fill_box( item_bbox )) {
+ changed = true;
+ }
}
- double scale_x = (*item_bbox).width() /(*mesh_bbox).width() ;
- double scale_y = (*item_bbox).height()/(*mesh_bbox).height();
-
- Geom::Translate t1(-(*mesh_bbox).min());
- Geom::Scale scale(scale_x,scale_y);
- Geom::Translate t2((*item_bbox).min());
- Geom::Affine transform = t1 * scale * t2;
- if (!transform.isIdentity() ) {
- gradient->array.transform(transform);
- gradient->array.write( gradient );
- gradient->requestModified(SP_OBJECT_MODIFIED_FLAG);
- changed = true;
+ }
+
+ if (style->stroke.isPaintserver()) {
+ SPPaintServer *server = item->style->getStrokePaintServer();
+ if ( SP_IS_MESHGRADIENT(server) ) {
+
+ Geom::OptRect item_bbox = item->visualBounds();
+ SPMeshGradient *gradient = SP_MESHGRADIENT(server);
+ if (gradient->array.fill_box( item_bbox )) {
+ changed = true;
+ }
}
}
+
}
-
}
if (changed) {
DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_MESH,
@@ -523,42 +577,42 @@ bool MeshTool::root_handler(GdkEvent* event) {
// Double click:
// If over a mesh line, divide mesh row/column
- // If not over a line, create new gradients for selected objects.
+ // If not over a line and no mesh, create new mesh for top selected object.
if ( event->button.button == 1 ) {
- // Are we over a mesh line?
- bool over_line = false;
- if (! drag->lines.empty()) {
- for (std::vector<SPCtrlLine *>::const_iterator l = drag->lines.begin(); l != drag->lines.end() && (!over_line); ++l) {
- over_line |= sp_mesh_context_is_over_line (this, *l, Geom::Point(event->motion.x, event->motion.y));
- }
- }
+ // Are we over a mesh line?
+ std::vector<SPCtrlCurve *> over_line =
+ sp_mesh_context_over_line(this, Geom::Point(event->motion.x, event->motion.y));
- if (over_line) {
+ if (!over_line.empty()) {
// 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(this, selection->items().front(), this->mousepoint_doc, event->button.time);
} else {
- sp_mesh_new_default(*this);
// Create a new gradient with default coordinates.
-// auto items= selection->items();
-// for(auto i=items.begin();i!=items.end();++i){
-// SPItem *item = *i;
-// 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(desktop->getDocument(), desktop, item, fsmode);
-
-// SPGradient *priv = sp_item_set_gradient(item, vector, new_type, fsmode);
-// sp_gradient_reset_to_userspace(priv, item);
-// }
-
-// DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_MESH,
-// _("Create default mesh"));
+
+ // Check if object already has mesh... if it does,
+ // don't create new mesh with click-drag.
+ bool has_mesh = false;
+ if (!selection->isEmpty()) {
+ SPStyle *style = selection->items().front()->style;
+ if (style) {
+ Inkscape::PaintTarget fill_or_stroke =
+ (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ?
+ Inkscape::FOR_FILL : Inkscape::FOR_STROKE;
+ SPPaintServer *server =
+ (fill_or_stroke == Inkscape::FOR_FILL) ?
+ style->getFillPaintServer():
+ style->getStrokePaintServer();
+ if (server && SP_IS_MESHGRADIENT(server))
+ has_mesh = true;
+ }
+ }
+
+ if (!has_mesh) {
+ sp_mesh_new_default(*this);
+ }
}
ret = TRUE;
@@ -570,10 +624,36 @@ bool MeshTool::root_handler(GdkEvent* event) {
#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 mesh already exists, do rubber band selection.
+ // Else set origin for drag which will create a new gradient.
if ( event->button.button == 1 && !this->space_panning ) {
+
+ // Are we over a mesh line?
+ std::vector<SPCtrlCurve *> over_line =
+ sp_mesh_context_over_line(this, Geom::Point(event->motion.x, event->motion.y), false);
+
+ if (!over_line.empty()) {
+ for (std::vector<SPCtrlCurve *>::const_iterator it = over_line.begin();
+ it != over_line.end(); ++it ) {
+ SPItem *item = (*it)->item;
+ Inkscape::PaintTarget fill_or_stroke =
+ (*it)->is_fill ? Inkscape::FOR_FILL : Inkscape::FOR_STROKE;
+ GrDragger* dragger0 = drag->getDraggerFor(item, POINT_MG_CORNER, (*it)->corner0, fill_or_stroke);
+ GrDragger* dragger1 = drag->getDraggerFor(item, POINT_MG_CORNER, (*it)->corner1, fill_or_stroke);
+ bool add = (event->button.state & GDK_SHIFT_MASK);
+ bool toggle = (event->button.state & GDK_CONTROL_MASK);
+ if ( !add && !toggle ) {
+ drag->deselectAll();
+ }
+ drag->setSelected( dragger0, true, !toggle );
+ drag->setSelected( dragger1, true, !toggle );
+ }
+ ret = true;
+ break; // To avoid putting the following code in an else block.
+ }
+
Geom::Point button_w(event->button.x, event->button.y);
// save drag origin
@@ -584,25 +664,43 @@ bool MeshTool::root_handler(GdkEvent* event) {
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)) {
- this->item_to_select = sp_event_context_find_item (desktop, button_w, event->button.state & GDK_MOD1_MASK, TRUE);
+ // Check if object already has mesh... if it does,
+ // don't create new mesh with click-drag.
+ bool has_mesh = false;
+ if (!selection->isEmpty()) {
+ SPStyle *style = selection->items().front()->style;
+ if (style) {
+ Inkscape::PaintTarget fill_or_stroke =
+ (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ?
+ Inkscape::FOR_FILL : Inkscape::FOR_STROKE;
+ SPPaintServer *server =
+ (fill_or_stroke == Inkscape::FOR_FILL) ?
+ style->getFillPaintServer():
+ style->getStrokePaintServer();
+ if (server && SP_IS_MESHGRADIENT(server))
+ has_mesh = true;
}
+ }
- if (!selection->isEmpty()) {
- SnapManager &m = desktop->namedview->snap_manager;
- m.setup(desktop);
- m.freeSnapReturnByRef(button_dt, Inkscape::SNAPSOURCE_NODE_HANDLE);
- m.unSetup();
- }
+ if (has_mesh) {
+ Inkscape::Rubberband::get(desktop)->start(desktop, button_dt);
+ }
- this->origin = button_dt;
+ // 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)) {
+ this->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();
+ }
+
+ this->origin = button_dt;
+
ret = TRUE;
}
break;
@@ -663,19 +761,14 @@ bool MeshTool::root_handler(GdkEvent* event) {
}
// Change cursor shape if over line
- bool over_line = false;
+ std::vector<SPCtrlCurve *> over_line =
+ sp_mesh_context_over_line(this, Geom::Point(event->motion.x, event->motion.y));
- if (!drag->lines.empty()) {
- for (std::vector<SPCtrlLine *>::const_iterator l = drag->lines.begin(); l != drag->lines.end() ; ++l) {
- over_line |= sp_mesh_context_is_over_line (this, *l, Geom::Point(event->motion.x, event->motion.y));
- }
- }
-
- if (this->cursor_addnode && !over_line) {
+ if (this->cursor_addnode && over_line.empty()) {
this->cursor_shape = cursor_gradient_xpm;
this->sp_event_context_update_cursor();
this->cursor_addnode = false;
- } else if (!this->cursor_addnode && over_line) {
+ } else if (!this->cursor_addnode && !over_line.empty()) {
this->cursor_shape = cursor_gradient_add_xpm;
this->sp_event_context_update_cursor();
this->cursor_addnode = true;
@@ -692,23 +785,15 @@ bool MeshTool::root_handler(GdkEvent* event) {
this->xp = this->yp = 0;
if ( event->button.button == 1 && !this->space_panning ) {
- // Check if over line
- bool over_line = false;
- SPCtrlLine *line = NULL;
-
- if (!drag->lines.empty()) {
- for (std::vector<SPCtrlLine *>::const_iterator l = drag->lines.begin(); l != drag->lines.end() && (!over_line); ++l) {
- over_line = sp_mesh_context_is_over_line (this, *l, Geom::Point(event->motion.x, event->motion.y));
- if (over_line) {
- break;
- }
- }
- }
+ // Check if over line
+ std::vector<SPCtrlCurve *> over_line =
+ sp_mesh_context_over_line(this, Geom::Point(event->motion.x, event->motion.y));
if ( (event->button.state & GDK_CONTROL_MASK) && (event->button.state & GDK_MOD1_MASK ) ) {
- if (over_line && line) {
- sp_mesh_context_split_near_point(this, line->item, this->mousepoint_doc, 0);
+ if (!over_line.empty()) {
+ sp_mesh_context_split_near_point(this, over_line[0]->item,
+ this->mousepoint_doc, 0);
ret = TRUE;
}
} else {
@@ -721,22 +806,47 @@ bool MeshTool::root_handler(GdkEvent* event) {
}
if (!this->within_tolerance) {
- // we've been dragging, either create a new gradient
- // or rubberband-select if we have rubberband
- Inkscape::Rubberband *r = Inkscape::Rubberband::get(desktop);
-
- if (r->is_started() && !this->within_tolerance) {
- // this was a rubberband drag
- if (r->getMode() == RUBBERBAND_MODE_RECT) {
- Geom::OptRect const b = r->getRectangle();
- drag->selectRect(*b);
+
+ // Check if object already has mesh... if it does,
+ // don't create new mesh with click-drag.
+ bool has_mesh = false;
+ if (!selection->isEmpty()) {
+ SPStyle *style = selection->items().front()->style;
+ if (style) {
+ Inkscape::PaintTarget fill_or_stroke =
+ (prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ?
+ Inkscape::FOR_FILL : Inkscape::FOR_STROKE;
+ SPPaintServer *server =
+ (fill_or_stroke == Inkscape::FOR_FILL) ?
+ style->getFillPaintServer():
+ style->getStrokePaintServer();
+ if (server && SP_IS_MESHGRADIENT(server))
+ has_mesh = true;
}
- } else {
- // Create a new mesh gradient
+ }
+
+ if (!has_mesh) {
sp_mesh_new_default(*this);
+ } else {
+
+ // we've been dragging, either create a new gradient
+ // or rubberband-select if we have rubberband
+ Inkscape::Rubberband *r = Inkscape::Rubberband::get(desktop);
+
+ if (r->is_started() && !this->within_tolerance) {
+ // this was a rubberband drag
+ if (r->getMode() == RUBBERBAND_MODE_RECT) {
+ Geom::OptRect const b = r->getRectangle();
+ if (!(event->button.state & GDK_SHIFT_MASK)) {
+ drag->deselectAll();
+ }
+ drag->selectRect(*b);
+ }
+ }
}
+
} else if (this->item_to_select) {
- if (over_line && line) {
+ if (!over_line.empty()) {
// Clicked on an existing mesh line, don't change selection. This stops
// possible change in selection during a double click with overlapping objects
} else {
@@ -744,15 +854,21 @@ bool MeshTool::root_handler(GdkEvent* event) {
if (event->button.state & GDK_SHIFT_MASK) {
selection->toggle(this->item_to_select);
} else {
+ drag->deselectAll();
selection->set(this->item_to_select);
}
}
} else {
- // click in an empty space; do the same as Esc
- if (!drag->selected.empty()) {
- drag->deselectAll();
+ if (!over_line.empty()) {
+ // Clicked on an existing mesh line, don't change selection. This stops
+ // possible change in selection during a double click with overlapping objects
} else {
- selection->clear();
+ // click in an empty space; do the same as Esc
+ if (!drag->selected.empty()) {
+ drag->deselectAll();
+ } else {
+ selection->clear();
+ }
}
}
@@ -780,10 +896,11 @@ bool MeshTool::root_handler(GdkEvent* event) {
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 (this->defaultMessageContext(), event,
- _("FIXME<b>Ctrl</b>: snap mesh angle"),
- _("FIXME<b>Shift</b>: draw mesh around the starting point"),
- NULL);
+
+ // sp_event_show_modifier_tip (this->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:
@@ -901,25 +1018,33 @@ bool MeshTool::root_handler(GdkEvent* event) {
}
break;
+ // Mesh Operations --------------------------------------------
+
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;
+ sp_mesh_context_corner_operation ( this, MG_CORNER_INSERT );
ret = TRUE;
break;
+ case GDK_KEY_i:
+ case GDK_KEY_I:
+ if (MOD__SHIFT_ONLY(event)) {
+ // Shift+I - insert corners (alternate keybinding for keyboards
+ // that don't have the Insert key)
+ sp_mesh_context_corner_operation ( this, MG_CORNER_INSERT );
+ ret = TRUE;
+ }
+ break;
+
case GDK_KEY_Delete:
case GDK_KEY_KP_Delete:
case GDK_KEY_BackSpace:
if ( !drag->selected.empty() ) {
- std::cout << "Deleting mesh stops not implemented yet" << std::endl;
ret = TRUE;
}
break;
- // Mesh Operations --------------------------------------------
-
case GDK_KEY_b: // Toggle mesh side between lineto and curveto.
case GDK_KEY_B:
if (MOD__ALT(event) && drag->isNonEmpty() && drag->hasSelection()) {
@@ -1011,6 +1136,16 @@ static void sp_mesh_new_default(MeshTool &rc) {
(prefs->getInt("/tools/gradient/newfillorstroke", 1) != 0) ?
Inkscape::FOR_FILL : Inkscape::FOR_STROKE;
+ // Ensure mesh is immediately editable.
+ // Editting both fill and stroke at same time doesn't work well so avoid.
+ if (fill_or_stroke == Inkscape::FOR_FILL) {
+ prefs->setBool("/tools/mesh/edit_fill", true );
+ prefs->setBool("/tools/mesh/edit_stroke", false);
+ } else {
+ prefs->setBool("/tools/mesh/edit_fill", false);
+ prefs->setBool("/tools/mesh/edit_stroke", true );
+ }
+
// 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");
@@ -1065,7 +1200,6 @@ static void sp_mesh_new_default(MeshTool &rc) {
}
}
-
}
}
}
diff --git a/src/ui/tools/mesh-tool.h b/src/ui/tools/mesh-tool.h
index f142bfd9c..1f012dc53 100644
--- a/src/ui/tools/mesh-tool.h
+++ b/src/ui/tools/mesh-tool.h
@@ -31,29 +31,34 @@ namespace Tools {
class MeshTool : public ToolBase {
public:
- MeshTool();
- virtual ~MeshTool();
+ MeshTool();
+ virtual ~MeshTool();
Geom::Point origin;
- bool cursor_addnode;
-
- bool node_added;
-
Geom::Point mousepoint_doc; // stores mousepoint when over_line in doc coords
sigc::connection *selcon;
sigc::connection *subselcon;
- static const std::string prefsPath;
+ static const std::string prefsPath;
- virtual void setup();
- virtual bool root_handler(GdkEvent* event);
+ virtual void setup();
+ virtual void set(const Inkscape::Preferences::Entry& val);
+ virtual bool root_handler(GdkEvent* event);
- virtual const std::string& getPrefsPath();
+ virtual const std::string& getPrefsPath();
private:
- void selection_changed(Inkscape::Selection* sel);
+ void selection_changed(Inkscape::Selection* sel);
+
+ bool cursor_addnode;
+ bool node_added;
+ bool show_handles;
+ bool edit_fill;
+ bool edit_stroke;
+
+
};
void sp_mesh_context_select_next(ToolBase *event_context);
diff --git a/src/ui/tools/node-tool.cpp b/src/ui/tools/node-tool.cpp
index f3679b40f..0c948c91c 100644
--- a/src/ui/tools/node-tool.cpp
+++ b/src/ui/tools/node-tool.cpp
@@ -32,6 +32,8 @@
#include "sp-text.h"
#include "ui/control-manager.h"
#include "ui/tools/node-tool.h"
+#include "ui/tools-switch.h"
+#include "ui/tools/tool-base.h"
#include "ui/tool/control-point-selection.h"
#include "ui/tool/event-utils.h"
#include "ui/tool/multi-path-manipulator.h"
@@ -209,7 +211,7 @@ void NodeTool::setup() {
this->_sizeUpdatedConn = ControlManager::getManager().connectCtrlSizeChanged(
sigc::mem_fun(this, &NodeTool::handleControlUiStyleChange)
);
-
+ this->helperpath_tmpitem = NULL;
this->_selected_nodes = new Inkscape::UI::ControlPointSelection(this->desktop, this->_transform_handle_group);
data.node_data.selection = this->_selected_nodes;
@@ -237,7 +239,6 @@ void NodeTool::setup() {
)))
);
- this->helperpath_tmpitem = NULL;
this->cursor_drag = false;
this->show_transform_handles = true;
this->single_node_transform_handles = false;
@@ -270,29 +271,34 @@ void NodeTool::setup() {
}
this->desktop->emitToolSubselectionChanged(NULL); // sets the coord entry fields to inactive
- this->update_helperpath();
+ sp_update_helperpath();
}
// show helper paths of the applied LPE, if any
-void NodeTool::update_helperpath () {
- Inkscape::Selection *selection = this->desktop->getSelection();
-
- if (this->helperpath_tmpitem) {
- this->desktop->remove_temporary_canvasitem(this->helperpath_tmpitem);
- this->helperpath_tmpitem = NULL;
+void sp_update_helperpath() {
+ SPDesktop * desktop = SP_ACTIVE_DESKTOP;
+ if (!desktop || !tools_isactive(desktop, TOOLS_NODES)) {
+ return;
+ }
+ Inkscape::UI::Tools::NodeTool *nt = static_cast<Inkscape::UI::Tools::NodeTool*>(desktop->event_context);
+ Inkscape::Selection *selection = desktop->getSelection();
+ if (nt->helperpath_tmpitem) {
+ desktop->remove_temporary_canvasitem(nt->helperpath_tmpitem);
+ nt->helperpath_tmpitem = NULL;
}
if (SP_IS_LPE_ITEM(selection->singleItem())) {
Inkscape::LivePathEffect::Effect *lpe = SP_LPE_ITEM(selection->singleItem())->getCurrentLPE();
if (lpe && lpe->isVisible()/* && lpe->showOrigPath()*/) {
- Inkscape::UI::ControlPointSelection *selectionNodes = _selected_nodes;
+
+ Inkscape::UI::ControlPointSelection *selectionNodes = nt->_selected_nodes;
std::vector<Geom::Point> selectedNodesPositions;
for (Inkscape::UI::ControlPointSelection::iterator i = selectionNodes->begin(); i != selectionNodes->end(); ++i) {
Inkscape::UI::Node *n = dynamic_cast<Inkscape::UI::Node *>(*i);
selectedNodesPositions.push_back(n->position());
}
lpe->setSelectedNodePoints(selectedNodesPositions);
- lpe->setCurrentZoom(this->desktop->current_zoom());
+ lpe->setCurrentZoom(desktop->current_zoom());
SPCurve *c = new SPCurve();
SPCurve *cc = new SPCurve();
std::vector<Geom::PathVector> cs = lpe->getCanvasIndicators(SP_LPE_ITEM(selection->singleItem()));
@@ -302,11 +308,11 @@ void NodeTool::update_helperpath () {
cc->reset();
}
if (!c->is_empty()) {
- SPCanvasItem *helperpath = sp_canvas_bpath_new(this->desktop->getTempGroup(), c, true);
+ SPCanvasItem *helperpath = sp_canvas_bpath_new(desktop->getTempGroup(), c, true);
sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helperpath), 0x0000ff9A, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helperpath), 0, SP_WIND_RULE_NONZERO);
sp_canvas_item_affine_absolute(helperpath, selection->singleItem()->i2dt_affine());
- this->helperpath_tmpitem = this->desktop->add_temporary_canvasitem(helperpath, 0);
+ nt->helperpath_tmpitem = desktop->add_temporary_canvasitem(helperpath, 0);
}
c->unref();
cc->unref();
@@ -468,7 +474,7 @@ bool NodeTool::root_handler(GdkEvent* event) {
switch (event->type)
{
case GDK_MOTION_NOTIFY: {
- update_helperpath();
+ sp_update_helperpath();
combine_motion_events(desktop->canvas, event->motion, 0);
SPItem *over_item = sp_event_context_find_item (desktop, event_point(event->button),
FALSE, TRUE);
diff --git a/src/ui/tools/node-tool.h b/src/ui/tools/node-tool.h
index 8342d66a6..983ba6cee 100644
--- a/src/ui/tools/node-tool.h
+++ b/src/ui/tools/node-tool.h
@@ -49,14 +49,14 @@ public:
Inkscape::UI::ControlPointSelection* _selected_nodes;
Inkscape::UI::MultiPathManipulator* _multipath;
-
+ Inkscape::Display::TemporaryItem *helperpath_tmpitem;
+
bool edit_clipping_paths;
bool edit_masks;
static const std::string prefsPath;
virtual void setup();
- virtual void update_helperpath();
virtual void set(const Inkscape::Preferences::Entry& val);
virtual bool root_handler(GdkEvent* event);
@@ -68,7 +68,7 @@ private:
sigc::connection _sizeUpdatedConn;
SPItem *flashed_item;
- Inkscape::Display::TemporaryItem *helperpath_tmpitem;
+
Inkscape::Display::TemporaryItem *flash_tempitem;
Inkscape::UI::Selector* _selector;
Inkscape::UI::PathSharedData* _path_data;
@@ -96,8 +96,9 @@ private:
void update_tip(GdkEvent *event);
void handleControlUiStyleChange();
};
-
+ void sp_update_helperpath();
}
+
}
}
diff --git a/src/ui/tools/spray-tool.cpp b/src/ui/tools/spray-tool.cpp
index 3649008ff..ad006627c 100644
--- a/src/ui/tools/spray-tool.cpp
+++ b/src/ui/tools/spray-tool.cpp
@@ -1026,7 +1026,7 @@ static bool sp_spray_recursive(SPDesktop *desktop,
if (unionResult) { // No need to add the very first item (initialized with NULL).
set->add(unionResult);
}
- sp_selected_path_union_skip_undo(set);
+ set->pathUnion(true);
set->add(parent_item);
Inkscape::GC::release(copy);
did = true;
@@ -1364,7 +1364,7 @@ bool SprayTool::root_handler(GdkEvent* event) {
SP_VERB_CONTEXT_SPRAY, _("Spray with clones"));
break;
case SPRAY_MODE_SINGLE_PATH:
- sp_selected_path_union_skip_undo(objectSet());
+ objectSet()->pathUnion(true);
desktop->getSelection()->add(object_set.objects().begin(), object_set.objects().end());
DocumentUndo::done(this->desktop->getDocument(),
SP_VERB_CONTEXT_SPRAY, _("Spray in single path"));
diff --git a/src/ui/tools/tweak-tool.cpp b/src/ui/tools/tweak-tool.cpp
index a0394ecd4..ff5d623c2 100644
--- a/src/ui/tools/tweak-tool.cpp
+++ b/src/ui/tools/tweak-tool.cpp
@@ -56,6 +56,8 @@
#include "sp-gradient-reference.h"
#include "sp-linear-gradient.h"
#include "sp-radial-gradient.h"
+#include "sp-mesh-gradient.h"
+#include "sp-mesh-array.h"
#include "gradient-chemistry.h"
#include "sp-text.h"
#include "sp-flowtext.h"
@@ -761,112 +763,132 @@ static void tweak_colors_in_gradient(SPItem *item, Inkscape::PaintTarget fill_or
p *= (gradient->gradientTransform).inverse();
// now p is in gradient's original coordinates
- double pos = 0;
- double r = 0;
-
SPLinearGradient *lg = dynamic_cast<SPLinearGradient *>(gradient);
- if (lg) {
- Geom::Point p1(lg->x1.computed, lg->y1.computed);
- Geom::Point p2(lg->x2.computed, lg->y2.computed);
- Geom::Point pdiff(p2 - p1);
- double vl = Geom::L2(pdiff);
+ SPRadialGradient *rg = dynamic_cast<SPRadialGradient *>(gradient);
+ if (lg || rg) {
- // This is the matrix which moves and rotates the gradient line
- // so it's oriented along the X axis:
- Geom::Affine norm = Geom::Affine(Geom::Translate(-p1)) * Geom::Affine(Geom::Rotate(-atan2(pdiff[Geom::Y], pdiff[Geom::X])));
+ double pos = 0;
+ double r = 0;
- // Transform the mouse point by it to find out its projection onto the gradient line:
- Geom::Point pnorm = p * norm;
+ if (lg) {
+ Geom::Point p1(lg->x1.computed, lg->y1.computed);
+ Geom::Point p2(lg->x2.computed, lg->y2.computed);
+ Geom::Point pdiff(p2 - p1);
+ double vl = Geom::L2(pdiff);
- // Scale its X coordinate to match the length of the gradient line:
- pos = pnorm[Geom::X] / vl;
- // Calculate radius in lenfth-of-gradient-line units
- r = radius / vl;
+ // This is the matrix which moves and rotates the gradient line
+ // so it's oriented along the X axis:
+ Geom::Affine norm = Geom::Affine(Geom::Translate(-p1)) *
+ Geom::Affine(Geom::Rotate(-atan2(pdiff[Geom::Y], pdiff[Geom::X])));
- } else {
- SPRadialGradient *rg = dynamic_cast<SPRadialGradient *>(gradient);
+ // Transform the mouse point by it to find out its projection onto the gradient line:
+ Geom::Point pnorm = p * norm;
+
+ // Scale its X coordinate to match the length of the gradient line:
+ pos = pnorm[Geom::X] / vl;
+ // Calculate radius in lenfth-of-gradient-line units
+ r = radius / vl;
+
+ }
if (rg) {
Geom::Point c (rg->cx.computed, rg->cy.computed);
pos = Geom::L2(p - c) / rg->r.computed;
r = radius / rg->r.computed;
}
- }
- // Normalize pos to 0..1, taking into accound gradient spread:
- double pos_e = pos;
- if (gradient->getSpread() == SP_GRADIENT_SPREAD_PAD) {
- if (pos > 1) {
- pos_e = 1;
- }
- if (pos < 0) {
- pos_e = 0;
- }
- } else if (gradient->getSpread() == SP_GRADIENT_SPREAD_REPEAT) {
- if (pos > 1 || pos < 0) {
- pos_e = pos - floor(pos);
- }
- } else if (gradient->getSpread() == SP_GRADIENT_SPREAD_REFLECT) {
- if (pos > 1 || pos < 0) {
- bool odd = ((int)(floor(pos)) % 2 == 1);
- pos_e = pos - floor(pos);
- if (odd) {
- pos_e = 1 - pos_e;
+ // Normalize pos to 0..1, taking into accound gradient spread:
+ double pos_e = pos;
+ if (gradient->getSpread() == SP_GRADIENT_SPREAD_PAD) {
+ if (pos > 1) {
+ pos_e = 1;
+ }
+ if (pos < 0) {
+ pos_e = 0;
+ }
+ } else if (gradient->getSpread() == SP_GRADIENT_SPREAD_REPEAT) {
+ if (pos > 1 || pos < 0) {
+ pos_e = pos - floor(pos);
+ }
+ } else if (gradient->getSpread() == SP_GRADIENT_SPREAD_REFLECT) {
+ if (pos > 1 || pos < 0) {
+ bool odd = ((int)(floor(pos)) % 2 == 1);
+ pos_e = pos - floor(pos);
+ if (odd) {
+ pos_e = 1 - pos_e;
+ }
}
}
- }
- SPGradient *vector = sp_gradient_get_forked_vector_if_necessary(gradient, false);
+ SPGradient *vector = sp_gradient_get_forked_vector_if_necessary(gradient, false);
- double offset_l = 0;
- double offset_h = 0;
- SPObject *child_prev = NULL;
- for (auto& child: vector->children) {
- SPStop *stop = dynamic_cast<SPStop *>(&child);
- if (!stop) {
- continue;
- }
+ double offset_l = 0;
+ double offset_h = 0;
+ SPObject *child_prev = NULL;
+ for (auto& child: vector->children) {
+ SPStop *stop = dynamic_cast<SPStop *>(&child);
+ if (!stop) {
+ continue;
+ }
- offset_h = stop->offset;
-
- if (child_prev) {
- SPStop *prevStop = dynamic_cast<SPStop *>(child_prev);
- g_assert(prevStop != NULL);
-
- if (offset_h - offset_l > r && pos_e >= offset_l && pos_e <= offset_h) {
- // the summit falls in this interstop, and the radius is small,
- // so it only affects the ends of this interstop;
- // distribute the force between the two endstops so that they
- // get all the painting even if they are not touched by the brush
- tweak_color (mode, stop->specified_color.v.c, rgb_goal,
- force * (pos_e - offset_l) / (offset_h - offset_l),
- do_h, do_s, do_l);
- tweak_color(mode, prevStop->specified_color.v.c, rgb_goal,
- force * (offset_h - pos_e) / (offset_h - offset_l),
- do_h, do_s, do_l);
- stop->updateRepr();
- child_prev->updateRepr();
- break;
- } else {
- // wide brush, may affect more than 2 stops,
- // paint each stop by the force from the profile curve
- if (offset_l <= pos_e && offset_l > pos_e - r) {
+ offset_h = stop->offset;
+
+ if (child_prev) {
+ SPStop *prevStop = dynamic_cast<SPStop *>(child_prev);
+ g_assert(prevStop != NULL);
+
+ if (offset_h - offset_l > r && pos_e >= offset_l && pos_e <= offset_h) {
+ // the summit falls in this interstop, and the radius is small,
+ // so it only affects the ends of this interstop;
+ // distribute the force between the two endstops so that they
+ // get all the painting even if they are not touched by the brush
+ tweak_color (mode, stop->specified_color.v.c, rgb_goal,
+ force * (pos_e - offset_l) / (offset_h - offset_l),
+ do_h, do_s, do_l);
tweak_color(mode, prevStop->specified_color.v.c, rgb_goal,
- force * tweak_profile (fabs (pos_e - offset_l), r),
+ force * (offset_h - pos_e) / (offset_h - offset_l),
do_h, do_s, do_l);
+ stop->updateRepr();
child_prev->updateRepr();
+ break;
+ } else {
+ // wide brush, may affect more than 2 stops,
+ // paint each stop by the force from the profile curve
+ if (offset_l <= pos_e && offset_l > pos_e - r) {
+ tweak_color(mode, prevStop->specified_color.v.c, rgb_goal,
+ force * tweak_profile (fabs (pos_e - offset_l), r),
+ do_h, do_s, do_l);
+ child_prev->updateRepr();
+ }
+
+ if (offset_h >= pos_e && offset_h < pos_e + r) {
+ tweak_color (mode, stop->specified_color.v.c, rgb_goal,
+ force * tweak_profile (fabs (pos_e - offset_h), r),
+ do_h, do_s, do_l);
+ stop->updateRepr();
+ }
}
+ }
- if (offset_h >= pos_e && offset_h < pos_e + r) {
+ offset_l = offset_h;
+ child_prev = &child;
+ }
+ } else {
+ // Mesh
+ SPMeshGradient *mg = dynamic_cast<SPMeshGradient *>(gradient);
+ if (mg) {
+ SPMeshGradient *mg_array = dynamic_cast<SPMeshGradient *>(mg->getArray());
+ SPMeshNodeArray *array = &(mg_array->array);
+ // Every third node is a corner node
+ for( unsigned i=0; i < array->nodes.size(); i+=3 ) {
+ for( unsigned j=0; j < array->nodes[i].size(); j+=3 ) {
+ SPStop *stop = array->nodes[i][j]->stop;
+ double distance = Geom::L2(Geom::Point(p - array->nodes[i][j]->p));
tweak_color (mode, stop->specified_color.v.c, rgb_goal,
- force * tweak_profile (fabs (pos_e - offset_h), r),
- do_h, do_s, do_l);
+ force * tweak_profile (distance, radius), do_h, do_s, do_l);
stop->updateRepr();
}
}
}
-
- offset_l = offset_h;
- child_prev = &child;
}
}
@@ -1062,7 +1084,8 @@ sp_tweak_dilate (TweakTool *tc, Geom::Point event_p, Geom::Point p, Geom::Point
double move_force = get_move_force(tc);
double color_force = MIN(sqrt(path_force)/20.0, 1);
- auto items= selection->items();
+// auto items= selection->items();
+ std::vector<SPItem*> items(selection->items().begin(), selection->items().end());
for(auto i=items.begin();i!=items.end(); ++i){
SPItem *item = *i;