1 /* === S Y N F I G ========================================================= */
2 /*! \file state_width.cpp
3 ** \brief Template File
8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 ** Copyright (c) 2008 Chris Moore
11 ** This package is free software; you can redistribute it and/or
12 ** modify it under the terms of the GNU General Public License as
13 ** published by the Free Software Foundation; either version 2 of
14 ** the License, or (at your option) any later version.
16 ** This package is distributed in the hope that it will be useful,
17 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ** General Public License for more details.
22 /* ========================================================================= */
24 /* === H E A D E R S ======================================================= */
33 #include <gtkmm/dialog.h>
34 #include <gtkmm/entry.h>
38 #include <synfig/valuenode_dynamiclist.h>
39 #include <synfigapp/action_system.h>
41 #include "state_width.h"
42 #include "state_normal.h"
43 #include "canvasview.h"
47 #include <synfigapp/action.h>
48 #include "event_mouse.h"
49 #include "event_layerclick.h"
51 #include "docks/dialog_tooloptions.h"
52 #include <gtkmm/optionmenu.h>
55 //#include <synfigapp/value_desc.h>
56 #include <synfigapp/main.h>
64 /* === U S I N G =========================================================== */
68 using namespace synfig;
69 using namespace synfigapp;
70 using namespace studio;
72 /* === M A C R O S ========================================================= */
74 /* === G L O B A L S ======================================================= */
76 StateWidth studio::state_width;
78 /* === C L A S S E S & S T R U C T S ======================================= */
80 class studio::StateWidth_Context : public sigc::trackable
82 etl::handle<CanvasView> canvas_view_;
83 CanvasView::IsWorking is_working;
89 handle<Duck> closestpoint;
91 map<handle<Duck>,Real> changetable;
100 bool prev_workarea_layer_clicking;
101 bool prev_workarea_duck_clicking;
102 Duckmatic::Type old_duckmask;
105 synfigapp::Settings& settings;
108 Gtk::Table options_table;
110 Gtk::Adjustment adj_delta;
111 Gtk::SpinButton spin_delta;
113 Gtk::Adjustment adj_radius;
114 Gtk::SpinButton spin_radius;
116 Gtk::CheckButton check_relative;
118 void AdjustWidth(handle<Duckmatic::Bezier> c, float t, Real mult, bool invert);
122 Real get_delta()const { return adj_delta.get_value(); }
123 void set_delta(Real f) { adj_delta.set_value(f); }
125 Real get_radius()const { return adj_radius.get_value(); }
126 void set_radius(Real f) { adj_radius.set_value(f); }
128 bool get_relative() const { return check_relative.get_active(); }
129 void set_relative(bool r) { check_relative.set_active(r); }
131 void refresh_tool_options(); //to refresh the toolbox
134 Smach::event_result event_stop_handler(const Smach::event& x);
135 Smach::event_result event_refresh_handler(const Smach::event& x);
136 Smach::event_result event_mouse_handler(const Smach::event& x);
137 Smach::event_result event_refresh_tool_options(const Smach::event& x);
139 //constructor destructor
140 StateWidth_Context(CanvasView* canvas_view);
141 ~StateWidth_Context();
144 const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
145 etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
146 synfig::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
147 WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
149 //Modifying settings etc.
150 void load_settings();
151 void save_settings();
154 }; // END of class StateWidth_Context
156 /* === M E T H O D S ======================================================= */
158 StateWidth::StateWidth():
159 Smach::state<StateWidth_Context>("width")
161 insert(event_def(EVENT_STOP,&StateWidth_Context::event_stop_handler));
162 insert(event_def(EVENT_REFRESH,&StateWidth_Context::event_refresh_handler));
163 insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,&StateWidth_Context::event_mouse_handler));
164 insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DRAG,&StateWidth_Context::event_mouse_handler));
165 insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_UP,&StateWidth_Context::event_mouse_handler));
166 insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateWidth_Context::event_refresh_tool_options));
169 StateWidth::~StateWidth()
174 StateWidth_Context::load_settings()
178 synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
181 //parse the arguments yargh!
182 if(settings.get_value("width.delta",value))
183 set_delta(atof(value.c_str()));
187 if(settings.get_value("width.radius",value))
188 set_radius(atof(value.c_str()));
193 if(settings.get_value("width.relative",value) && value == "1")
200 synfig::warning("State Width: Caught exception when attempting to load settings.");
205 StateWidth_Context::save_settings()
209 synfig::ChangeLocale change_locale(LC_NUMERIC, "C");
210 settings.set_value("width.delta",strprintf("%f",get_delta()));
211 settings.set_value("width.radius",strprintf("%f",get_radius()));
212 settings.set_value("width.relative",get_relative()?"1":"0");
216 synfig::warning("State Width: Caught exception when attempting to save settings.");
221 StateWidth_Context::reset()
226 StateWidth_Context::StateWidth_Context(CanvasView* canvas_view):
227 canvas_view_(canvas_view),
228 is_working(*canvas_view),
229 prev_workarea_layer_clicking(get_work_area()->get_allow_layer_clicks()),
230 prev_workarea_duck_clicking(get_work_area()->get_allow_duck_clicks()),
231 old_duckmask(get_work_area()->get_type_mask()),
233 settings(synfigapp::Main::get_selected_input_device()->settings()),
235 adj_delta(6,0,20,0.01,0.1),
236 spin_delta(adj_delta,0.01,3),
238 adj_radius(200,0,1e50,1,10),
239 spin_radius(adj_radius,1,1),
241 check_relative(_("Relative Growth"))
245 // Set up the tool options dialog
246 options_table.attach(*manage(new Gtk::Label(_("Width Tool"))), 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
249 options_table.attach(*manage(new Gtk::Label(_("Growth:"))), 0, 1, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
250 options_table.attach(spin_delta, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
252 options_table.attach(*manage(new Gtk::Label(_("Radius:"))), 0, 1, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
253 options_table.attach(spin_radius, 1, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
255 options_table.attach(check_relative, 0, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
257 options_table.show_all();
259 refresh_tool_options();
260 App::dialog_tool_options->present();
262 // Turn off layer clicking
263 get_work_area()->set_allow_layer_clicks(false);
265 // clear out the ducks
266 //get_work_area()->clear_ducks();
268 // Refresh the work area
269 get_work_area()->queue_draw();
271 //Create the new ducks
277 center->set_name("p1");
278 center->set_type(Duck::TYPE_POSITION);
284 radius->set_origin(center);
285 radius->set_radius(true);
286 radius->set_type(Duck::TYPE_RADIUS);
287 radius->set_name("radius");
292 closestpoint = new Duck();
293 closestpoint->set_name("closest");
294 closestpoint->set_type(Duck::TYPE_POSITION);
297 //Disable duck clicking for the maximum coolness :)
298 get_work_area()->set_allow_duck_clicks(false);
299 get_work_area()->set_type_mask((Duck::Type)((int)Duck::TYPE_WIDTH + (int)Duck::TYPE_RADIUS));
301 // Turn the mouse pointer to crosshairs
302 get_work_area()->set_cursor(Gdk::CROSSHAIR);
304 // Hide the tables if they are showing
305 //prev_table_status=get_canvas_view()->tables_are_visible();
306 //if(prev_table_status)get_canvas_view()->hide_tables();
308 // Disable the time bar
309 //get_canvas_view()->set_sensitive_timebar(false);
312 //get_work_area()->signal_user_click().connect(sigc::mem_fun(*this,&studio::StateWidth_Context::on_user_click));
314 App::toolbox->refresh();
318 StateWidth_Context::refresh_tool_options()
320 App::dialog_tool_options->clear();
321 App::dialog_tool_options->set_widget(options_table);
322 App::dialog_tool_options->set_local_name(_("Width Tool"));
323 App::dialog_tool_options->set_name("width");
327 StateWidth_Context::event_refresh_tool_options(const Smach::event& /*x*/)
329 refresh_tool_options();
330 return Smach::RESULT_ACCEPT;
333 StateWidth_Context::~StateWidth_Context()
337 //remove ducks if need be
340 get_work_area()->erase_duck(center);
341 get_work_area()->erase_duck(radius);
342 get_work_area()->erase_duck(closestpoint);
346 // Restore Duck clicking
347 get_work_area()->set_allow_duck_clicks(prev_workarea_duck_clicking);
349 // Restore layer clicking
350 get_work_area()->set_allow_layer_clicks(prev_workarea_layer_clicking);
352 // Restore the mouse pointer
353 get_work_area()->reset_cursor();
355 // Restore duck masking
356 get_work_area()->set_type_mask(old_duckmask);
358 // Tool options be rid of ye!!
359 App::dialog_tool_options->clear();
361 // Enable the time bar
362 //get_canvas_view()->set_sensitive_timebar(true);
364 // Bring back the tables if they were out before
365 //if(prev_table_status)get_canvas_view()->show_tables();
367 // Refresh the work area
368 get_work_area()->queue_draw();
370 App::toolbox->refresh();
374 StateWidth_Context::event_stop_handler(const Smach::event& /*x*/)
376 //throw Smach::egress_exception();
378 return Smach::RESULT_OK;
382 StateWidth_Context::event_refresh_handler(const Smach::event& /*x*/)
385 return Smach::RESULT_ACCEPT;
389 StateWidth_Context::AdjustWidth(handle<Duckmatic::Bezier> c, float t, Real mult, bool invert)
391 //Leave the function if there is no curve
394 Real amount1=0,amount2=0;
396 //decide how much to change each width
400 both pressure and multiply amount are in mult
401 (may want to change this to allow different types of falloff)
403 rsq is the squared distance from the point on the curve (also part of the falloff)
407 //may want to provide a different falloff function...
415 amount1 = (1-t)*mult;
425 handle<Duck> p1 = c->p1;
426 handle<Duck> p2 = c->p2;
432 const DuckList dl = get_work_area()->get_duck_list();
434 DuckList::const_iterator i = dl.begin();
436 for(;i != dl.end(); ++i)
438 if((*i)->get_type() == Duck::TYPE_WIDTH)
440 if((*i)->get_origin_duck() == p1)
445 if((*i)->get_origin_duck() == p2)
453 if(amount1 != 0 && w1)
455 Real width = w1->get_point().mag();
458 w1->set_point(Vector(width,0));
460 //log in the list of changes...
461 //to truly be changed after everything is said and done
462 changetable[w1] = width;
465 if(amount2 != 0 && w2)
467 Real width = w2->get_point().mag();
470 w2->set_point(Vector(width,0));
472 //log in the list of changes...
473 //to truly be changed after everything is said and done
474 changetable[w2] = width;
479 StateWidth_Context::event_mouse_handler(const Smach::event& x)
481 const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
484 if( (event.key == EVENT_WORKAREA_MOUSE_BUTTON_DOWN || event.key == EVENT_WORKAREA_MOUSE_BUTTON_DRAG)
485 && event.button == BUTTON_LEFT )
487 const Real pw = get_work_area()->get_pw();
488 const Real ph = get_work_area()->get_ph();
489 const Real scale = sqrt(pw*pw+ph*ph);
490 const Real rad = get_relative() ? scale * get_radius() : get_radius();
492 bool invert = (event.modifier&Gdk::CONTROL_MASK);
494 const Real threshold = 0.08;
501 //if we're dragging get the difference in time between now and then
502 if(event.key == EVENT_WORKAREA_MOUSE_BUTTON_DRAG)
504 dtime = min(1/15.0,clocktime());
508 //make way for new ducks
509 //get_work_area()->clear_ducks();
512 //mouse_pos = event.pos;
514 center->set_point(event.pos);
515 if(!added)get_work_area()->add_duck(center);
517 radius->set_scalar(rad);
518 if(!added)get_work_area()->add_duck(radius);
520 //the other duck is at the current duck
521 closestpoint->set_point(event.pos);
522 if(!added)get_work_area()->add_duck(closestpoint);
524 //get the closest curve...
525 handle<Duckmatic::Bezier> c;
526 if(event.pressure >= threshold)
527 c = get_work_area()->find_bezier(event.pos,scale*8,rad,&t);
529 //run algorithm on event.pos to get 2nd placement
535 curve[0] = c->p1->get_trans_point();
536 curve[1] = c->c1->get_trans_point();
537 curve[2] = c->c2->get_trans_point();
538 curve[3] = c->p2->get_trans_point();
541 rsq = (p-event.pos).mag_squared();
543 const Real r = rad*rad;
547 closestpoint->set_point(curve(t));
549 //adjust the width...
550 //squared falloff for radius... [0,1]
552 Real ri = (r - rsq)/r;
553 AdjustWidth(c,t,ri*event.pressure*get_delta()*dtime,invert);
557 //the points have been added
560 //draw where it is yo!
561 get_work_area()->queue_draw();
563 return Smach::RESULT_ACCEPT;
566 if(event.key == EVENT_WORKAREA_MOUSE_BUTTON_UP && event.button == BUTTON_LEFT)
570 get_work_area()->erase_duck(center);
571 get_work_area()->erase_duck(radius);
572 get_work_area()->erase_duck(closestpoint);
576 //Affect the width changes here...
577 map<handle<Duck>,Real>::iterator i = changetable.begin();
579 synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Sketch Width"));
580 for(; i != changetable.end(); ++i)
582 //for each duck modify IT!!!
583 ValueDesc desc = i->first->get_value_desc();
585 if( desc.get_value_type() == ValueBase::TYPE_REAL )
587 Action::Handle action(Action::create("ValueDescSet"));
590 action->set_param("canvas",get_canvas());
591 action->set_param("canvas_interface",get_canvas_interface());
593 action->set_param("value_desc",desc);
594 action->set_param("new_value",ValueBase(i->second));
595 action->set_param("time",get_canvas_view()->get_time());
597 if(!action->is_ready() || !get_canvas_view()->get_instance()->perform_action(action))
600 synfig::warning("Changing the width action has failed");
601 return Smach::RESULT_ERROR;
608 get_work_area()->queue_draw();
610 return Smach::RESULT_ACCEPT;
613 return Smach::RESULT_OK;
618 StateWidth_Context::refresh_ducks()
620 get_work_area()->clear_ducks();
621 get_work_area()->queue_draw();