spray-context.cpp revision d5ad05ac17abfd7d2eae333a8c1233971e1c1a8f
#define __SP_SPRAY_CONTEXT_C__
/*
* Spray Tool
*
* Authors:
* Pierre-Antoine MARC
* Pierre CACLIN
* Aurel-Aimé MARMION
* Julien LERAY
* Benoît LAVORATA
* Vincent MONTAGNE
* Pierre BARBRY-BLOT
* Steren GIANNINI (steren.giannini@gmail.com)
*
* Copyright (C) 2009 authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include "config.h"
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <glibmm/i18n.h>
#include <numeric>
#include "svg/svg.h"
#include "display/canvas-bpath.h"
#include <glib/gmem.h>
#include "macros.h"
#include "document.h"
#include "selection.h"
#include "desktop.h"
#include "desktop-events.h"
#include "desktop-handles.h"
#include "unistd.h"
#include "desktop-style.h"
#include "message-context.h"
#include "pixmaps/cursor-spray.xpm"
#include <boost/optional.hpp>
#include "libnr/nr-matrix-ops.h"
#include "libnr/nr-scale-translate-ops.h"
#include "xml/repr.h"
#include "context-fns.h"
#include "sp-item.h"
#include "inkscape.h"
#include "color.h"
#include "svg/svg-color.h"
#include "splivarot.h"
#include "sp-item-group.h"
#include "sp-shape.h"
#include "sp-path.h"
#include "path-chemistry.h"
#include "sp-gradient.h"
#include "sp-stop.h"
#include "sp-gradient-reference.h"
#include "sp-linear-gradient.h"
#include "sp-radial-gradient.h"
#include "gradient-chemistry.h"
#include "sp-text.h"
#include "sp-flowtext.h"
#include "display/canvas-bpath.h"
#include "display/canvas-arena.h"
#include "display/curve.h"
#include "livarot/Shape.h"
#include <2geom/isnan.h>
#include <2geom/transforms.h>
#include "preferences.h"
#include "style.h"
#include "box3d.h"
#include "sp-item-transform.h"
#include "filter-chemistry.h"
#include "sp-gaussian-blur-fns.h"
#include "sp-gaussian-blur.h"
#include "spray-context.h"
#include "ui/dialog/dialog-manager.h"
#include "helper/action.h"
#include <iostream>
using namespace std;
#define DDC_RED_RGBA 0xff0000ff
#define DYNA_MIN_WIDTH 1.0e-6
static void sp_spray_context_class_init(SPSprayContextClass *klass);
static void sp_spray_context_init(SPSprayContext *ddc);
static void sp_spray_context_dispose(GObject *object);
static void sp_spray_context_setup(SPEventContext *ec);
static void sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val);
static gint sp_spray_context_root_handler(SPEventContext *ec, GdkEvent *event);
static SPEventContextClass *parent_class = 0;
/**
* This function returns pseudo-random numbers from a normal distribution
* @param mu : mean
* @param sigma : standard deviation ( > 0 )
*/
inline double NormalDistribution(double mu,double sigma)
{
// use Box Muller's algorithm
return mu + sigma * sqrt( -2.0 * log(g_random_double_range(0, 1)) ) * cos( 2.0*M_PI*g_random_double_range(0, 1) );
}
GtkType sp_spray_context_get_type(void)
{
static GType type = 0;
if (!type) {
GTypeInfo info = {
sizeof(SPSprayContextClass),
NULL, NULL,
(GClassInitFunc) sp_spray_context_class_init,
NULL, NULL,
sizeof(SPSprayContext),
4,
(GInstanceInitFunc) sp_spray_context_init,
NULL, /* value_table */
};
type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPSprayContext", &info, (GTypeFlags)0);
}
return type;
}
static void sp_spray_context_class_init(SPSprayContextClass *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_spray_context_dispose;
event_context_class->setup = sp_spray_context_setup;
event_context_class->set = sp_spray_context_set;
event_context_class->root_handler = sp_spray_context_root_handler;
}
/* Method to rotate items */
void sp_spray_rotate_rel(Geom::Point c,SPDesktop */*desktop*/,SPItem *item, Geom::Rotate const &rotation)
{
Geom::Point center = c;
Geom::Translate const s(c);
Geom::Matrix affine = Geom::Matrix(s).inverse() * Geom::Matrix(rotation) * Geom::Matrix(s);
// Rotate item.
item->set_i2d_affine(item->i2d_affine() * (Geom::Matrix)affine);
// Use each item's own transform writer, consistent with sp_selection_apply_affine()
item->doWriteTransform(SP_OBJECT_REPR(item), item->transform);
// Restore the center position (it's changed because the bbox center changed)
if (item->isCenterSet()) {
item->setCenter(c);
item->updateRepr();
}
}
/* Method to scale items */
void sp_spray_scale_rel(Geom::Point c, SPDesktop */*desktop*/, SPItem *item, Geom::Scale const &scale)
{
Geom::Translate const s(c);
item->set_i2d_affine(item->i2d_affine() * s.inverse() * scale * s );
item->doWriteTransform(SP_OBJECT_REPR(item), item->transform);
}
static void sp_spray_context_init(SPSprayContext *tc)
{
SPEventContext *event_context = SP_EVENT_CONTEXT(tc);
event_context->cursor_shape = cursor_spray_xpm;
event_context->hot_x = 4;
event_context->hot_y = 4;
/* attributes */
tc->dragging = FALSE;
tc->distrib = 1;
tc->width = 0.2;
tc->force = 0.2;
tc->ratio = 0;
tc->tilt=0;
tc->mean = 0.2;
tc->rotation_variation=0;
tc->standard_deviation=0.2;
tc->scale=1;
tc->scale_variation = 1;
tc->pressure = TC_DEFAULT_PRESSURE;
tc->is_dilating = false;
tc->has_dilated = false;
tc->do_h = true;
tc->do_s = true;
tc->do_l = true;
tc->do_o = false;
new (&tc->style_set_connection) sigc::connection();
}
static void sp_spray_context_dispose(GObject *object)
{
SPSprayContext *tc = SP_SPRAY_CONTEXT(object);
tc->style_set_connection.disconnect();
tc->style_set_connection.~connection();
if (tc->dilate_area) {
gtk_object_destroy(GTK_OBJECT(tc->dilate_area));
tc->dilate_area = NULL;
}
if (tc->_message_context) {
delete tc->_message_context;
}
G_OBJECT_CLASS(parent_class)->dispose(object);
}
bool is_transform_modes(gint mode)
{
return (mode == SPRAY_MODE_COPY ||
mode == SPRAY_MODE_CLONE ||
mode == SPRAY_MODE_SINGLE_PATH ||
mode == SPRAY_OPTION);
}
void sp_spray_update_cursor(SPSprayContext *tc, bool /*with_shift*/)
{
SPEventContext *event_context = SP_EVENT_CONTEXT(tc);
SPDesktop *desktop = event_context->desktop;
guint num = 0;
gchar *sel_message = NULL;
if (!desktop->selection->isEmpty()) {
num = g_slist_length((GSList *) desktop->selection->itemList());
sel_message = g_strdup_printf(ngettext("<b>%i</b> object selected","<b>%i</b> objects selected",num), num);
} else {
sel_message = g_strdup_printf(_("<b>Nothing</b> selected"));
}
switch (tc->mode) {
case SPRAY_MODE_COPY:
tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray <b>copies</b> of the initial selection"), sel_message);
break;
case SPRAY_MODE_CLONE:
tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray <b>clones</b> of the initial selection"), sel_message);
break;
case SPRAY_MODE_SINGLE_PATH:
tc->_message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or scroll to spray in a <b>single path</b> of the initial selection"), sel_message);
break;
default:
break;
}
sp_event_context_update_cursor(event_context);
g_free(sel_message);
}
static void sp_spray_context_setup(SPEventContext *ec)
{
SPSprayContext *tc = SP_SPRAY_CONTEXT(ec);
if (((SPEventContextClass *) parent_class)->setup)
((SPEventContextClass *) parent_class)->setup(ec);
{
/* TODO: have a look at sp_dyna_draw_context_setup where the same is done.. generalize? at least make it an arcto! */
SPCurve *c = new SPCurve();
const double C1 = 0.552;
c->moveto(-1,0);
c->curveto(-1, C1, -C1, 1, 0, 1 );
c->curveto(C1, 1, 1, C1, 1, 0 );
c->curveto(1, -C1, C1, -1, 0, -1 );
c->curveto(-C1, -1, -1, -C1, -1, 0 );
c->closepath();
tc->dilate_area = sp_canvas_bpath_new(sp_desktop_controls(ec->desktop), c);
c->unref();
sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(tc->dilate_area), 0x00000000,(SPWindRule)0);
sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(tc->dilate_area), 0xff9900ff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
sp_canvas_item_hide(tc->dilate_area);
}
tc->is_drawing = false;
tc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
sp_event_context_read(ec, "distrib");
sp_event_context_read(ec, "width");
sp_event_context_read(ec, "ratio");
sp_event_context_read(ec, "tilt");
sp_event_context_read(ec, "rotation_variation");
sp_event_context_read(ec, "scale_variation");
sp_event_context_read(ec, "mode");
sp_event_context_read(ec, "population");
sp_event_context_read(ec, "force");
sp_event_context_read(ec, "mean");
sp_event_context_read(ec, "standard_deviation");
sp_event_context_read(ec, "usepressure");
sp_event_context_read(ec, "Scale");
sp_event_context_read(ec, "doh");
sp_event_context_read(ec, "dol");
sp_event_context_read(ec, "dos");
sp_event_context_read(ec, "doo");
;
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
if (prefs->getBool("/tools/spray/selcue")) {
ec->enableSelectionCue();
}
if (prefs->getBool("/tools/spray/gradientdrag")) {
ec->enableGrDrag();
}
}
static void sp_spray_context_set(SPEventContext *ec, Inkscape::Preferences::Entry *val)
{
SPSprayContext *tc = SP_SPRAY_CONTEXT(ec);
Glib::ustring path = val->getEntryName();
if (path == "width") {
tc->width = 0.01 * CLAMP(val->getInt(10), 1, 100);
} else if (path == "mode") {
tc->mode = val->getInt();
sp_spray_update_cursor(tc, false);
} else if (path == "distribution") {
tc->distrib = val->getInt(1);
} else if (path == "population") {
tc->population = 0.01 * CLAMP(val->getInt(10), 1, 100);
} else if (path == "tilt") {
tc->tilt = CLAMP(val->getDouble(0.1), 0, 1000.0);
} else if (path == "ratio") {
tc->ratio = CLAMP(val->getDouble(), 0.0, 0.9);
} else if (path == "force") {
tc->force = CLAMP(val->getDouble(1.0), 0, 1.0);
} else if (path == "rotation_variation") {
tc->rotation_variation = CLAMP(val->getDouble(0.0), 0, 100.0);
} else if (path == "scale_variation") {
tc->scale_variation = CLAMP(val->getDouble(1.0), 0, 100.0);
} else if (path == "mean") {
tc->mean = 0.01 * CLAMP(val->getInt(10), 1, 100);
} else if (path == "standard_deviation") {
tc->standard_deviation = 0.01 * CLAMP(val->getInt(10), 1, 100);
} else if (path == "usepressure") {
tc->usepressure = val->getBool();
} else if (path == "doh") {
tc->do_h = val->getBool();
} else if (path == "dos") {
tc->do_s = val->getBool();
} else if (path == "dol") {
tc->do_l = val->getBool();
} else if (path == "doo") {
tc->do_o = val->getBool();
}
}
static void sp_spray_extinput(SPSprayContext *tc, GdkEvent *event)
{
if (gdk_event_get_axis (event, GDK_AXIS_PRESSURE, &tc->pressure))
tc->pressure = CLAMP (tc->pressure, TC_MIN_PRESSURE, TC_MAX_PRESSURE);
else
tc->pressure = TC_DEFAULT_PRESSURE;
}
double get_dilate_radius(SPSprayContext *tc)
{
return 250 * tc->width/SP_EVENT_CONTEXT(tc)->desktop->current_zoom();
}
double get_path_force(SPSprayContext *tc)
{
double force = 8 * (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE)
/sqrt(SP_EVENT_CONTEXT(tc)->desktop->current_zoom());
if (force > 3) {
force += 4 * (force - 3);
}
return force * tc->force;
}
double get_path_mean(SPSprayContext *tc)
{
return tc->mean;
}
double get_path_standard_deviation(SPSprayContext *tc)
{
return tc->standard_deviation;
}
double get_move_force(SPSprayContext *tc)
{
double force = (tc->usepressure? tc->pressure : TC_DEFAULT_PRESSURE);
return force * tc->force;
}
double get_move_mean(SPSprayContext *tc)
{
return tc->mean;
}
double get_move_standard_deviation(SPSprayContext *tc)
{
return tc->standard_deviation;
}
/**
* Method to handle the distribution of the items
* @param[out] radius : radius of the position of the sprayed object
* @param[out] angle : angle of the position of the sprayed object
* @param[in] a : mean
* @param[in] s : standard deviation
* @param[in] choice :
*/
void random_position( double &radius, double &angle, double &a, double &s, int /*choice*/)
{
// angle is taken from an uniform distribution
angle = g_random_double_range(0, M_PI*2.0);
// radius is taken from a Normal Distribution
double radius_temp =-1;
while(!((radius_temp>=0)&&(radius_temp<=1)))
{
radius_temp = NormalDistribution( a, s );
}
// Because we are in polar coordinates, a special treatment has to be done to the radius.
// Otherwise, positions taken from an uniform repartition on radius and angle will not seam to
// be uniformily distributed on the disk (more at the center and less at the boundary).
// We counter this effect with a 0.5 exponent. This is empiric.
radius = pow( radius_temp, 0.5);
}
bool sp_spray_recursive(SPDesktop *desktop,
Inkscape::Selection *selection,
SPItem *item,
Geom::Point p,
Geom::Point /*vector*/,
gint mode,
double radius,
double /*force*/,
double population,
double &scale,
double scale_variation,
bool /*reverse*/,
double mean,
double standard_deviation,
double ratio,
double tilt,
double rotation_variation,
gint _distrib )
{
bool did = false;
if (SP_IS_BOX3D(item) ) {
// convert 3D boxes to ordinary groups before spraying their shapes
item = SP_ITEM(box3d_convert_to_group(SP_BOX3D(item)));
selection->add(item);
}
double _fid = g_random_double_range(0,1);
double angle = g_random_double_range( - rotation_variation / 100.0 * M_PI , rotation_variation / 100.0 * M_PI );
double _scale = g_random_double_range( 1.0 - scale_variation / 100.0, 1.0 + scale_variation / 100.0 );
double dr; double dp;
random_position( dr, dp, mean, standard_deviation, _distrib );
dr=dr*radius;
if (mode == SPRAY_MODE_COPY) {
Geom::OptRect a = item->getBounds(item->i2doc_affine());
if (a) {
SPItem *item_copied;
if(_fid<=population)
{
// duplicate
SPDocument *doc = SP_OBJECT_DOCUMENT(item);
Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item);
Inkscape::XML::Node *parent = old_repr->parent();
Inkscape::XML::Node *copy = old_repr->duplicate(xml_doc);
parent->appendChild(copy);
SPObject *new_obj = doc->getObjectByRepr(copy);
item_copied = (SPItem *) new_obj; //convertion object->item
Geom::Point center=item->getCenter();
sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
//Move the cursor p
Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio),-sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint());
sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
did = true;
}
}
} else if (mode == SPRAY_MODE_SINGLE_PATH) {
SPItem *father; //initial Object
SPItem *item_copied; //Projected Object
SPItem *unionResult; //previous union
SPItem *son; //father copy
int i=1;
for (GSList *items = g_slist_copy((GSList *) selection->itemList());
items != NULL;
items = items->next) {
SPItem *item1 = (SPItem *) items->data;
if (i==1) {
father=item1;
}
if (i==2) {
unionResult=item1;
}
i++;
}
SPDocument *doc = SP_OBJECT_DOCUMENT(father);
Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(father);
Inkscape::XML::Node *parent = old_repr->parent();
Geom::OptRect a = father->getBounds(father->i2doc_affine());
if (a) {
if (i==2) {
Inkscape::XML::Node *copy1 = old_repr->duplicate(xml_doc);
parent->appendChild(copy1);
SPObject *new_obj1 = doc->getObjectByRepr(copy1);
son = (SPItem *) new_obj1; // conversion object->item
unionResult=son;
Inkscape::GC::release(copy1);
}
if (_fid<=population) { // Rules the population of objects sprayed
// duplicates the father
Inkscape::XML::Node *copy2 = old_repr->duplicate(xml_doc);
parent->appendChild(copy2);
SPObject *new_obj2 = doc->getObjectByRepr(copy2);
item_copied = (SPItem *) new_obj2;
// Move around the cursor
Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio),-sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint());
Geom::Point center=father->getCenter();
sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
// union and duplication
selection->clear();
selection->add(item_copied);
selection->add(unionResult);
sp_selected_path_union_skip_undo(selection->desktop());
selection->add(father);
Inkscape::GC::release(copy2);
did = true;
}
}
} else if (mode == SPRAY_MODE_CLONE) {
Geom::OptRect a = item->getBounds(item->i2doc_affine());
if (a) {
if(_fid<=population) {
SPItem *item_copied;
SPDocument *doc = SP_OBJECT_DOCUMENT(item);
Inkscape::XML::Document* xml_doc = sp_document_repr_doc(doc);
Inkscape::XML::Node *old_repr = SP_OBJECT_REPR(item);
Inkscape::XML::Node *parent = old_repr->parent();
// Creation of the clone
Inkscape::XML::Node *clone = xml_doc->createElement("svg:use");
// Ad the clone to the list of the father's sons
parent->appendChild(clone);
// Generates the link between father and son attributes
clone->setAttribute("xlink:href", g_strdup_printf("#%s", old_repr->attribute("id")), false);
SPObject *clone_object = doc->getObjectByRepr(clone);
// conversion object->item
item_copied = (SPItem *) clone_object;
Geom::Point center=item->getCenter();
sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(_scale,_scale));
sp_spray_scale_rel(center,desktop,item_copied, Geom::Scale(scale,scale));
sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle));
Geom::Point move = (Geom::Point(cos(tilt)*cos(dp)*dr/(1-ratio)+sin(tilt)*sin(dp)*dr/(1+ratio),-sin(tilt)*cos(dp)*dr/(1-ratio)+cos(tilt)*sin(dp)*dr/(1+ratio)))+(p-a->midpoint());
sp_item_move_rel(item_copied, Geom::Translate(move[Geom::X], -move[Geom::Y]));
Inkscape::GC::release(clone);
did = true;
}
}
}
return did;
}
bool sp_spray_dilate(SPSprayContext *tc, Geom::Point /*event_p*/, Geom::Point p, Geom::Point vector, bool reverse)
{
Inkscape::Selection *selection = sp_desktop_selection(SP_EVENT_CONTEXT(tc)->desktop);
SPDesktop *desktop = SP_EVENT_CONTEXT(tc)->desktop;
if (selection->isEmpty()) {
return false;
}
bool did = false;
double radius = get_dilate_radius(tc);
bool do_fill = false, do_stroke = false, do_opacity = false;
guint32 fill_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", true, &do_fill);
guint32 stroke_goal = sp_desktop_get_color_tool(desktop, "/tools/spray", false, &do_stroke);
double opacity_goal = sp_desktop_get_master_opacity_tool(desktop, "/tools/spray", &do_opacity);
if (reverse) {
// RGB inversion
fill_goal = SP_RGBA32_U_COMPOSE(
(255 - SP_RGBA32_R_U(fill_goal)),
(255 - SP_RGBA32_G_U(fill_goal)),
(255 - SP_RGBA32_B_U(fill_goal)),
(255 - SP_RGBA32_A_U(fill_goal)));
stroke_goal = SP_RGBA32_U_COMPOSE(
(255 - SP_RGBA32_R_U(stroke_goal)),
(255 - SP_RGBA32_G_U(stroke_goal)),
(255 - SP_RGBA32_B_U(stroke_goal)),
(255 - SP_RGBA32_A_U(stroke_goal)));
opacity_goal = 1 - opacity_goal;
}
double path_force = get_path_force(tc);
if (radius == 0 || path_force == 0) {
return false;
}
double path_mean = get_path_mean(tc);
if (radius == 0 || path_mean == 0) {
return false;
}
double path_standard_deviation = get_path_standard_deviation(tc);
if (radius == 0 || path_standard_deviation == 0) {
return false;
}
double move_force = get_move_force(tc);
double move_mean = get_move_mean(tc);
double move_standard_deviation = get_move_standard_deviation(tc);
for (GSList *items = g_slist_copy((GSList *) selection->itemList());
items != NULL;
items = items->next) {
SPItem *item = (SPItem *) items->data;
if (is_transform_modes(tc->mode)) {
if (sp_spray_recursive (desktop,selection, item, p, vector, tc->mode, radius, move_force, tc->population,tc->scale, tc->scale_variation, reverse, move_mean, move_standard_deviation,tc->ratio,tc->tilt, tc->rotation_variation, tc->distrib))
did = true;
} else {
if (sp_spray_recursive (desktop,selection, item, p, vector, tc->mode, radius, path_force, tc->population,tc->scale, tc->scale_variation, reverse, path_mean, path_standard_deviation,tc->ratio,tc->tilt, tc->rotation_variation, tc->distrib))
did = true;
}
}
return did;
}
void sp_spray_update_area(SPSprayContext *tc)
{
double radius = get_dilate_radius(tc);
Geom::Matrix const sm ( Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) );
sp_canvas_item_affine_absolute(tc->dilate_area, (sm* Geom::Rotate(tc->tilt))* Geom::Translate(SP_EVENT_CONTEXT(tc)->desktop->point()));
sp_canvas_item_show(tc->dilate_area);
}
void sp_spray_switch_mode(SPSprayContext *tc, gint mode, bool with_shift)
{
// select the button mode
SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode);
// need to set explicitly, because the prefs may not have changed by the previous
tc->mode = mode;
sp_spray_update_cursor (tc, with_shift);
}
void sp_spray_switch_mode_temporarily(SPSprayContext *tc, gint mode, bool with_shift)
{
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
// Juggling about so that prefs have the old value but tc->mode and the button show new mode:
gint now_mode = prefs->getInt("/tools/spray/mode", 0);
SP_EVENT_CONTEXT(tc)->desktop->setToolboxSelectOneValue ("spray_tool_mode", mode);
// button has changed prefs, restore
prefs->setInt("/tools/spray/mode", now_mode);
// changing prefs changed tc->mode, restore back :)
tc->mode = mode;
sp_spray_update_cursor (tc, with_shift);
}
gint sp_spray_context_root_handler(SPEventContext *event_context,
GdkEvent *event)
{
SPSprayContext *tc = SP_SPRAY_CONTEXT(event_context);
SPDesktop *desktop = event_context->desktop;
gint ret = FALSE;
switch (event->type) {
case GDK_ENTER_NOTIFY:
sp_canvas_item_show(tc->dilate_area);
break;
case GDK_LEAVE_NOTIFY:
sp_canvas_item_hide(tc->dilate_area);
break;
case GDK_BUTTON_PRESS:
if (event->button.button == 1 && !event_context->space_panning) {
if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false) {
return TRUE;
}
Geom::Point const motion_w(event->button.x,
event->button.y);
Geom::Point const motion_dt(desktop->w2d(motion_w));
tc->last_push = desktop->dt2doc(motion_dt);
sp_spray_extinput(tc, event);
sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
tc->is_drawing = true;
tc->is_dilating = true;
tc->has_dilated = false;
if(tc->is_dilating && event->button.button == 1 && !event_context->space_panning)
sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT);
tc->has_dilated=true;
ret = TRUE;
}
break;
case GDK_MOTION_NOTIFY:
{
Geom::Point const motion_w(event->motion.x,
event->motion.y);
Geom::Point motion_dt(desktop->w2d(motion_w));
Geom::Point motion_doc(desktop->dt2doc(motion_dt));
sp_spray_extinput(tc, event);
// draw the dilating cursor
double radius = get_dilate_radius(tc);
Geom::Matrix const sm (Geom::Scale(radius/(1-tc->ratio), radius/(1+tc->ratio)) );
sp_canvas_item_affine_absolute(tc->dilate_area, (sm*Geom::Rotate(tc->tilt))*Geom::Translate(desktop->w2d(motion_w)));
sp_canvas_item_show(tc->dilate_area);
guint num = 0;
if (!desktop->selection->isEmpty()) {
num = g_slist_length((GSList *) desktop->selection->itemList());
}
if (num == 0) {
tc->_message_context->flash(Inkscape::ERROR_MESSAGE, _("<b>Nothing selected!</b> Select objects to spray."));
}
// dilating:
if (tc->is_drawing && ( event->motion.state & GDK_BUTTON1_MASK )) {
sp_spray_dilate (tc, motion_w, motion_doc, motion_doc - tc->last_push, event->button.state & GDK_SHIFT_MASK? true : false);
//tc->last_push = motion_doc;
tc->has_dilated = true;
// it's slow, so prevent clogging up with events
gobble_motion_events(GDK_BUTTON1_MASK);
return TRUE;
}
}
break;
/*Spray with the scroll*/
case GDK_SCROLL:
{
if (event->scroll.state & GDK_BUTTON1_MASK)
{
double temp ;
temp=tc->population;
tc->population=1.0;
desktop->setToolboxAdjustmentValue ("population", tc->population * 100);
Geom::Point const scroll_w(event->button.x,event->button.y);
Geom::Point const scroll_dt = desktop->point();;
Geom::Point motion_doc(desktop->dt2doc(scroll_dt));
switch (event->scroll.direction)
{
case GDK_SCROLL_UP:
{
if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false)
{
return TRUE;
}
tc->last_push = desktop->dt2doc(scroll_dt);
sp_spray_extinput(tc, event);
sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
tc->is_drawing = true;
tc->is_dilating = true;
tc->has_dilated = false;
if(tc->is_dilating && !event_context->space_panning)
sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0),false);
tc->has_dilated=true;
tc->population=temp;
desktop->setToolboxAdjustmentValue ("population", tc->population * 100);
ret = TRUE;
}
break;
case GDK_SCROLL_DOWN:
{
if (Inkscape::have_viable_layer(desktop, tc->_message_context) == false)
{
return TRUE;
}
tc->last_push = desktop->dt2doc(scroll_dt);
sp_spray_extinput(tc, event);
sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
tc->is_drawing = true;
tc->is_dilating = true;
tc->has_dilated = false;
if(tc->is_dilating && !event_context->space_panning)
sp_spray_dilate (tc, scroll_w, desktop->dt2doc(scroll_dt), Geom::Point(0,0), false);
tc->has_dilated=true;
ret = TRUE;
}
break;
case GDK_SCROLL_RIGHT:
{} break;
case GDK_SCROLL_LEFT:
{} break;
}
}
break;
}
case GDK_BUTTON_RELEASE:
{
Geom::Point const motion_w(event->button.x, event->button.y);
Geom::Point const motion_dt(desktop->w2d(motion_w));
sp_canvas_end_forced_full_redraws(desktop->canvas);
tc->is_drawing = false;
if (tc->is_dilating && event->button.button == 1 && !event_context->space_panning) {
if (!tc->has_dilated) {
// if we did not rub, do a light tap
tc->pressure = 0.03;
sp_spray_dilate (tc, motion_w, desktop->dt2doc(motion_dt), Geom::Point(0,0), MOD__SHIFT);
}
tc->is_dilating = false;
tc->has_dilated = false;
switch (tc->mode) {
case SPRAY_MODE_COPY:
sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
SP_VERB_CONTEXT_SPRAY, _("Spray with copies"));
break;
case SPRAY_MODE_CLONE:
sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
SP_VERB_CONTEXT_SPRAY, _("Spray with clones"));
break;
case SPRAY_MODE_SINGLE_PATH:
sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(tc)->desktop),
SP_VERB_CONTEXT_SPRAY, _("Spray in single path"));
break;
}
}
break;
}
case GDK_KEY_PRESS:
switch (get_group0_keyval (&event->key)) {
case GDK_j: if (MOD__SHIFT_ONLY) {
sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT);
ret = TRUE;
}
case GDK_J: if (MOD__SHIFT_ONLY) {
sp_spray_switch_mode(tc, SPRAY_MODE_COPY, MOD__SHIFT);
ret = TRUE;
}
break;
case GDK_m:
case GDK_M:
case GDK_0:
break;
case GDK_i:
case GDK_I:
case GDK_k: if (MOD__SHIFT_ONLY) {
sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT);
ret = TRUE;
}
case GDK_K:if (MOD__SHIFT_ONLY) {
sp_spray_switch_mode(tc, SPRAY_MODE_SINGLE_PATH, MOD__SHIFT);
ret = TRUE;
}
break;
case GDK_l: if (MOD__SHIFT_ONLY) {
sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT);
ret = TRUE;
}
case GDK_L:
if (MOD__SHIFT_ONLY) {
sp_spray_switch_mode(tc, SPRAY_MODE_CLONE, MOD__SHIFT);
ret = TRUE;
}
break;
case GDK_Up:
case GDK_KP_Up:
if (!MOD__CTRL_ONLY) {
tc->scale += 0.05;
//desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100);
ret = TRUE;
}
break;
case GDK_Down:
case GDK_KP_Down:
if (!MOD__CTRL_ONLY) {
tc->scale -= 0.05;
if (tc->scale < 0.0)
tc->scale = 0.0;
//desktop->setToolboxAdjustmentValue ("spray-force", tc->force * 100);
ret = TRUE;
}
break;
case GDK_Right:
case GDK_KP_Right:
if (!MOD__CTRL_ONLY) {
tc->width += 0.01;
if (tc->width > 1.0)
tc->width = 1.0;
desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100); // the same spinbutton is for alt+x
sp_spray_update_area(tc);
ret = TRUE;
}
break;
case GDK_Left:
case GDK_KP_Left:
if (!MOD__CTRL_ONLY) {
tc->width -= 0.01;
if (tc->width < 0.01)
tc->width = 0.01;
desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
sp_spray_update_area(tc);
ret = TRUE;
}
break;
case GDK_Home:
case GDK_KP_Home:
tc->width = 0.01;
desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
sp_spray_update_area(tc);
ret = TRUE;
break;
case GDK_End:
case GDK_KP_End:
tc->width = 1.0;
desktop->setToolboxAdjustmentValue ("altx-spray", tc->width * 100);
sp_spray_update_area(tc);
ret = TRUE;
break;
case GDK_x:
case GDK_X:
if (MOD__ALT_ONLY) {
desktop->setToolboxFocusTo ("altx-spray");
ret = TRUE;
}
break;
case GDK_Shift_L:
case GDK_Shift_R:
sp_spray_update_cursor(tc, true);
break;
/*Set the scale to 1*/
case GDK_Control_L:
tc->scale=1;
default:
break;
}
break;
case GDK_KEY_RELEASE: {
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
switch (get_group0_keyval(&event->key)) {
case GDK_Shift_L:
case GDK_Shift_R:
sp_spray_update_cursor(tc, false);
break;
case GDK_Control_L:
case GDK_Control_R:
sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT);
tc->_message_context->clear();
break;
default:
sp_spray_switch_mode (tc, prefs->getInt("/tools/spray/mode"), MOD__SHIFT);
break;
}
}
default:
break;
}
if (!ret) {
if (((SPEventContextClass *) parent_class)->root_handler) {
ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event);
}
}
return ret;
}
/*
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:encoding=utf-8:textwidth=99 :