spray-tool.cpp revision c752a3b8a278d43c8a20d09d8d40b0753e6d1a08
/*
* 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)
* Jon A. Cruz <jon@joncruz.org>
* Abhishek Sharma
*
* Copyright (C) 2009 authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include "config.h"
#include <numeric>
#include "ui/dialog/dialog-manager.h"
#include <glib.h>
#include "macros.h"
#include "document.h"
#include "document-undo.h"
#include "selection.h"
#include "desktop.h"
#include "desktop-events.h"
#include "message-context.h"
#include "pixmaps/cursor-spray.xpm"
#include <boost/optional.hpp>
#include "context-fns.h"
#include "sp-item.h"
#include "inkscape.h"
#include "splivarot.h"
#include "sp-item-group.h"
#include "sp-shape.h"
#include "sp-path.h"
#include "path-chemistry.h"
#include "sp-text.h"
#include "sp-root.h"
#include "sp-flowtext.h"
#include "display/sp-canvas.h"
#include "display/canvas-bpath.h"
#include "display/canvas-arena.h"
#include "preferences.h"
#include "style.h"
#include "box3d.h"
#include "sp-item-transform.h"
#include "filter-chemistry.h"
#include "ui/tools/spray-tool.h"
#include "verbs.h"
#include <iostream>
#include <gdk/gdkkeysyms.h>
using Inkscape::DocumentUndo;
using namespace std;
#define DDC_RED_RGBA 0xff0000ff
#define DYNA_MIN_WIDTH 1.0e-6
// Disabled in 0.91 because of Bug #1274831 (crash, spraying an object
// with the mode: spray object in single path)
// Please enable again when working on 1.0
#define ENABLE_SPRAY_MODE_SINGLE_PATH
namespace Inkscape {
namespace UI {
namespace Tools {
}
/**
* This function returns pseudo-random numbers from a normal distribution
* @param mu : mean
* @param sigma : standard deviation ( > 0 )
*/
{
// 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) );
}
/* Method to rotate items */
static void sp_spray_rotate_rel(Geom::Point c, SPDesktop */*desktop*/, SPItem *item, Geom::Rotate const &rotation)
{
// Rotate item.
// Use each item's own transform writer, consistent with sp_selection_apply_affine()
// Restore the center position (it's changed because the bbox center changed)
if (item->isCenterSet()) {
item->updateRepr();
}
}
/* Method to scale items */
static void sp_spray_scale_rel(Geom::Point c, SPDesktop */*desktop*/, SPItem *item, Geom::Scale const &scale)
{
}
///* Method to scale items */
//static Geom::Affine sp_spray_scale_rel(Geom::Point c, SPDesktop *desktop, SPItem *item, Geom::Scale const &scale, bool write)
//{
// //Maybe isbetter create a metod inverse to set_i2d_affine to calculate transforms
// // whith objects not in tree
// Geom::Translate const s(c);
// Geom::Affine scale_computed = item->i2dt_affine() * s.inverse() * scale * s;
// Geom::Affine dt2p; /* desktop to item parent transform */
// if (item->parent) {
// dt2p = static_cast<SPItem *>(item->parent)->i2dt_affine().inverse();
// } else {
// dt2p = desktop->dt2doc();
// }
// Geom::Affine i2p( scale_computed * dt2p );
// if(!write){
// i2p = scale_computed * dt2p * desktop->dt2doc().inverse();
// }
// if(write) {
// item->set_item_transform(i2p);
// item->doWriteTransform(item->getRepr(), item->transform);
// }
// return i2p;
//}
, dragging(false)
, usepressure(false)
, usetilt(false)
, usetext(false)
, width(0.2)
, ratio(0)
, tilt(0)
, rotation_variation(0)
, population(0)
, scale_variation(1)
, scale(1)
, mean(0.2)
, standard_deviation(0.2)
, distrib(1)
, mode(0)
, is_drawing(false)
, is_dilating(false)
, has_dilated(false)
, dilate_area(NULL)
, overlap(false)
, offset(0)
{
}
this->enableGrDrag(false);
this->style_set_connection.disconnect();
if (this->dilate_area) {
sp_canvas_item_destroy(this->dilate_area);
this->dilate_area = NULL;
}
}
sel_message = g_strdup_printf(ngettext("<b>%i</b> object selected","<b>%i</b> objects selected",num), num);
} else {
}
switch (this->mode) {
case SPRAY_MODE_COPY:
this->message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or click and scroll to spray <b>copies</b> of the initial selection."), sel_message);
break;
case SPRAY_MODE_CLONE:
this->message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or click and scroll to spray <b>clones</b> of the initial selection."), sel_message);
break;
case SPRAY_MODE_SINGLE_PATH:
this->message_context->setF(Inkscape::NORMAL_MESSAGE, _("%s. Drag, click or click and scroll to spray in a <b>single path</b> of the initial selection."), sel_message);
break;
default:
break;
}
this->sp_event_context_update_cursor();
}
{
/* TODO: have a look at sp_dyna_draw_context_setup where the same is done.. generalize? at least make it an arcto! */
c->unref();
sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(this->dilate_area), 0xff9900ff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
sp_canvas_item_hide(this->dilate_area);
}
this->is_drawing = false;
sp_event_context_read(this, "distrib");
sp_event_context_read(this, "width");
sp_event_context_read(this, "ratio");
sp_event_context_read(this, "tilt");
sp_event_context_read(this, "rotation_variation");
sp_event_context_read(this, "scale_variation");
sp_event_context_read(this, "mode");
sp_event_context_read(this, "population");
sp_event_context_read(this, "mean");
sp_event_context_read(this, "standard_deviation");
sp_event_context_read(this, "usepressure");
sp_event_context_read(this, "Scale");
sp_event_context_read(this, "offset");
sp_event_context_read(this, "overlap");
this->enableSelectionCue();
}
this->enableGrDrag();
}
}
if (path == "mode") {
this->update_cursor(false);
} else if (path == "width") {
} else if (path == "usepressure") {
} else if (path == "population") {
} else if (path == "rotation_variation") {
} else if (path == "scale_variation") {
} else if (path == "standard_deviation") {
} else if (path == "mean") {
// Not implemented in the toolbar and preferences yet
} else if (path == "distribution") {
} else if (path == "tilt") {
} else if (path == "ratio") {
} else if (path == "offset") {
} else if (path == "overlap") {
}
}
{
} else {
}
}
{
}
{
}
{
return tc->standard_deviation;
}
{
//g_warning("Pressure, population: %f, %f", pressure, pressure * tc->population);
}
{
}
{
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 :
*/
{
// angle is taken from an uniform distribution
// radius is taken from a Normal Distribution
double 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.
}
gchar const * spray_origin)
{
std::vector<SPItem*> items_down = desktop->getDocument()->getItemsPartiallyInBox(desktop->dkey, *bbox);
//std::cout << bbox->top() << "top" << bbox->left() << "left" << bbox->bottom() << "bottom" << bbox->right() << "rightINSIDE\n";
)
){
// if(!SP_IS_GROUP(item) && 1>2){
// SPShape *down_item_shape = dynamic_cast<SPShape *>(item_down);
// if (down_item_shape) {
// Geom::PathVector pathv;
// SPPath *down_item_path = dynamic_cast<SPPath *>(down_item_shape);
// if (down_item_path) {
// pathv = down_item_path->get_curve()->get_pathvector();
// } else {
// pathv = down_item_shape->getCurve()->get_pathvector();
// }
// if (!pathv.empty()) {
// SPShape *copied_item_shape = dynamic_cast<SPShape *>(item_copied);
// if (copied_item_shape) {
// Geom::PathVector pathv_other;
// SPPath *copied_item_path = dynamic_cast<SPPath *>(copied_item_shape);
// if (copied_item_path) {
// pathv_other = copied_item_path->get_curve()->get_pathvector();
// } else {
// pathv_other = copied_item_shape->getCurve()->get_pathvector();
// }
// if (!pathv_other.empty()) {
// Geom::CrossingSet cs = Geom::crossings(pathv, pathv_other);
// if(cs[0].size() == 0){
// continue;
// }
// }
// }
// }
// }
// }
// sp_item_move_rel(item_copied, Geom::Translate(-move[Geom::X], move[Geom::Y]));
// sp_spray_rotate_rel(center,desktop,item_copied, Geom::Rotate(angle).inverse());
// sp_spray_scale_rel(center,desktop, item_copied, Geom::Scale(scale,scale).inverse());
// sp_spray_scale_rel(center,desktop, item_copied, Geom::Scale(_scale, _scale).inverse());
//
// double min_scale = (1.0 - scale_variation / 100.0);
// _scale = min_scale + ((_scale - min_scale)/2);
// 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]));
// Geom::Point center = item_copied->getCenter();
// sp_spray_scale_rel(center,desktop, item_copied, Geom::Scale(scale).inverse());
// double min_scale = (1.0 - scale_variation / 100.0);
// scale = g_random_double_range(min_scale, 0.8);
// sp_spray_scale_rel(center, desktop, item_copied, Geom::Scale(scale));
// bbox = item_copied->desktopVisualBounds();
// if(bbox->intersects(item_down->desktopVisualBounds()) ||
// bbox->contains(item_down->desktopVisualBounds())
// )
// {
// //this hack is for speed draw, moved and on release delete all at this point
// //item_copied->deleteObject();
// item_copied->setHidden(true);
// hidding_items.push_back(item_copied);
// return true;
// } else {
// std::cout << "applied\n";
// }
return true;
}
}
return false;
}
double radius,
double population,
double &scale,
double scale_variation,
bool /*reverse*/,
double mean,
double standard_deviation,
double ratio,
double tilt,
double rotation_variation,
{
bool did = false;
{
if (box) {
// convert 3D boxes to ordinary groups before spraying their shapes
}
}
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 );
if (mode == SPRAY_MODE_COPY) {
if (a) {
if(_fid <= population)
{
gchar const * spray_origin;
} else {
}
path *= item->transform.inverse() * doc->getRoot()->c2p.inverse() * item->i2dt_affine() * s.inverse() * Geom::Scale(_scale) * s * item->i2dt_affine().inverse() * doc->getRoot()->c2p * item->transform;
path *= item->transform.inverse() * doc->getRoot()->c2p.inverse() * item->i2dt_affine() * s.inverse() * Geom::Scale(scale) * s * item->i2dt_affine().inverse() * doc->getRoot()->c2p * item->transform;
path *= item->transform.inverse() * doc->getRoot()->c2p.inverse() * item->i2dt_affine() * s.inverse() * Geom::Rotate(angle) * s * item->i2dt_affine().inverse() * doc->getRoot()->c2p * item->transform;
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());
//std::cout << rect.top() << "top" << rect.left() << "left" << rect.bottom() << "bottom" << rect.right() << "transformed\n";
std::cout << bbox->top() << "top" << bbox->left() << "left" << bbox->bottom() << "bottom" << bbox->right() << "PREV\n";
//std::cout << "ppp\n";
//std::cout << "aaa\n";
//return true;
// double min_scale = (1.0 - scale_variation / 100.0);
// _scale = g_random_double_range(min_scale, 0.8);
// center=item->getCenter();
// Geom::Translate const moved_center(center);
// rect *= item->i2dt_affine() * moved_center.inverse() * Geom::Scale(_scale) * moved_center;
// Geom::OptRect bbox_transformed2(Geom::Point(rect.left(), rect.top()),Geom::Point(rect.right(), rect.bottom()));
// if(!fit_item(desktop, bbox_transformed2, spray_origin)){
// return true;
// }
//std::cout << "ccc";
// Duplicate
}
// Move the cursor p
std::cout << bbox->top() << "top" << bbox->left() << "left" << bbox->bottom() << "bottom" << bbox->right() << "POST\n";
did = true;
} else {
did = false;
}
}
}
} else if (mode == SPRAY_MODE_SINGLE_PATH) {
int i=1;
if (i == 1) {
parent_item = item1;
}
if (i == 2) {
unionResult = item1;
}
i++;
}
if (parent_item) {
if (a) {
// Duplicates the parent item
gchar const * spray_origin;
} else {
}
// 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());
// Union and duplication
if (unionResult) { // No need to add the very first item (initialized with NULL).
}
did = true;
}
}
}
#endif
} else if (mode == SPRAY_MODE_CLONE) {
if (a) {
if(_fid <= population) {
// Creation of the clone
// Ad the clone to the list of the parent's children
// Generates the link between parent and child attributes
gchar const * spray_origin;
} else {
}
// Conversion object->item
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());
did = true;
}
}
}
return did;
}
static bool sp_spray_dilate(SprayTool *tc, Geom::Point /*event_p*/, Geom::Point p, Geom::Point vector, bool reverse)
{
return false;
}
bool did = false;
if (radius == 0 || population == 0) {
return false;
}
return false;
}
if (radius == 0 || path_standard_deviation == 0) {
return false;
}
{
}
if (sp_spray_recursive(desktop, selection, item, p, vector, tc->mode, radius, population, tc->scale, tc->scale_variation, reverse, move_mean, move_standard_deviation, tc->ratio, tc->tilt, tc->rotation_variation, tc->distrib)) {
did = true;
}
}
}
}
return did;
}
{
sp_canvas_item_affine_absolute(tc->dilate_area, (sm* Geom::Rotate(tc->tilt))* Geom::Translate(SP_EVENT_CONTEXT(tc)->desktop->point()));
}
{
// Select the button mode
// Need to set explicitly, because the prefs may not have changed by the previous
}
case GDK_ENTER_NOTIFY:
sp_canvas_item_show(this->dilate_area);
break;
case GDK_LEAVE_NOTIFY:
sp_canvas_item_hide(this->dilate_area);
break;
case GDK_BUTTON_PRESS:
return TRUE;
}
sp_spray_extinput(this, event);
this->is_drawing = true;
this->is_dilating = true;
this->has_dilated = false;
}
this->has_dilated = true;
}
break;
case GDK_MOTION_NOTIFY: {
sp_spray_extinput(this, event);
// Draw the dilating cursor
double radius = get_dilate_radius(this);
sp_canvas_item_affine_absolute(this->dilate_area, (sm*Geom::Rotate(this->tilt))*Geom::Translate(desktop->w2d(motion_w)));
sp_canvas_item_show(this->dilate_area);
}
if (num == 0) {
this->message_context->flash(Inkscape::ERROR_MESSAGE, _("<b>Nothing selected!</b> Select objects to spray."));
}
// Dilating:
sp_spray_dilate(this, motion_w, motion_doc, motion_doc - this->last_push, event->button.state & GDK_SHIFT_MASK? true : false);
//this->last_push = motion_doc;
this->has_dilated = true;
// It's slow, so prevent clogging up with events
return TRUE;
}
}
break;
/* Spray with the scroll */
case GDK_SCROLL: {
double temp ;
temp = this->population;
this->population = 1.0;
case GDK_SCROLL_DOWN:
case GDK_SCROLL_UP: {
return TRUE;
}
sp_spray_extinput(this, event);
this->is_drawing = true;
this->is_dilating = true;
this->has_dilated = false;
if(this->is_dilating && !this->space_panning) {
}
this->has_dilated = true;
this->population = temp;
}
break;
case GDK_SCROLL_RIGHT:
{} break;
case GDK_SCROLL_LEFT:
{} break;
}
}
break;
}
case GDK_BUTTON_RELEASE: {
this->is_drawing = false;
if (!this->has_dilated) {
// If we did not rub, do a light tap
this->pressure = 0.03;
}
this->is_dilating = false;
this->has_dilated = false;
switch (this->mode) {
case SPRAY_MODE_COPY:
SP_VERB_CONTEXT_SPRAY, _("Spray with copies"));
break;
case SPRAY_MODE_CLONE:
SP_VERB_CONTEXT_SPRAY, _("Spray with clones"));
break;
case SPRAY_MODE_SINGLE_PATH:
SP_VERB_CONTEXT_SPRAY, _("Spray in single path"));
break;
}
}
break;
}
case GDK_KEY_PRESS:
case GDK_KEY_j:
case GDK_KEY_J:
if (MOD__SHIFT_ONLY(event)) {
}
break;
case GDK_KEY_k:
case GDK_KEY_K:
if (MOD__SHIFT_ONLY(event)) {
}
break;
case GDK_KEY_l:
case GDK_KEY_L:
if (MOD__SHIFT_ONLY(event)) {
}
break;
#endif
case GDK_KEY_Up:
case GDK_KEY_KP_Up:
if (!MOD__CTRL_ONLY(event)) {
this->population += 0.01;
if (this->population > 1.0) {
this->population = 1.0;
}
}
break;
case GDK_KEY_Down:
case GDK_KEY_KP_Down:
if (!MOD__CTRL_ONLY(event)) {
this->population -= 0.01;
if (this->population < 0.0) {
this->population = 0.0;
}
}
break;
case GDK_KEY_Right:
case GDK_KEY_KP_Right:
if (!MOD__CTRL_ONLY(event)) {
this->width += 0.01;
if (this->width > 1.0) {
this->width = 1.0;
}
// The same spinbutton is for alt+x
sp_spray_update_area(this);
}
break;
case GDK_KEY_Left:
case GDK_KEY_KP_Left:
if (!MOD__CTRL_ONLY(event)) {
this->width -= 0.01;
if (this->width < 0.01) {
this->width = 0.01;
}
sp_spray_update_area(this);
}
break;
case GDK_KEY_Home:
case GDK_KEY_KP_Home:
this->width = 0.01;
sp_spray_update_area(this);
break;
case GDK_KEY_End:
case GDK_KEY_KP_End:
this->width = 1.0;
sp_spray_update_area(this);
break;
case GDK_KEY_x:
case GDK_KEY_X:
if (MOD__ALT_ONLY(event)) {
}
break;
case GDK_KEY_Shift_L:
case GDK_KEY_Shift_R:
this->update_cursor(true);
break;
case GDK_KEY_Control_L:
case GDK_KEY_Control_R:
break;
case GDK_KEY_Delete:
case GDK_KEY_KP_Delete:
case GDK_KEY_BackSpace:
break;
default:
break;
}
break;
case GDK_KEY_RELEASE: {
case GDK_KEY_Shift_L:
case GDK_KEY_Shift_R:
this->update_cursor(false);
break;
case GDK_KEY_Control_L:
case GDK_KEY_Control_R:
this->message_context->clear();
break;
default:
break;
}
}
default:
break;
}
if (!ret) {
// if ((SP_EVENT_CONTEXT_CLASS(sp_spray_context_parent_class))->root_handler) {
// ret = (SP_EVENT_CONTEXT_CLASS(sp_spray_context_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:fileencoding=utf-8:textwidth=99 :