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 "canvasview.h"
46 #include <synfigapp/action.h>
47 #include "event_mouse.h"
48 #include "event_layerclick.h"
50 #include "dialog_tooloptions.h"
51 #include <gtkmm/optionmenu.h>
54 //#include <synfigapp/value_desc.h>
55 #include <synfigapp/main.h>
63 /* === U S I N G =========================================================== */
67 using namespace synfig;
68 using namespace synfigapp;
69 using namespace studio;
71 /* === M A C R O S ========================================================= */
73 /* === G L O B A L S ======================================================= */
75 StateWidth studio::state_width;
77 /* === C L A S S E S & S T R U C T S ======================================= */
79 class studio::StateWidth_Context : public sigc::trackable
81 etl::handle<CanvasView> canvas_view_;
82 CanvasView::IsWorking is_working;
88 handle<Duck> closestpoint;
90 map<handle<Duck>,Real> changetable;
99 bool prev_workarea_layer_clicking;
100 bool prev_workarea_duck_clicking;
101 Duckmatic::Type old_duckmask;
104 synfigapp::Settings& settings;
107 Gtk::Table options_table;
109 Gtk::Adjustment adj_delta;
110 Gtk::SpinButton spin_delta;
112 Gtk::Adjustment adj_radius;
113 Gtk::SpinButton spin_radius;
115 Gtk::CheckButton check_relative;
117 void AdjustWidth(handle<Duckmatic::Bezier> c, float t, Real mult, bool invert);
121 Real get_delta()const { return adj_delta.get_value(); }
122 void set_delta(Real f) { adj_delta.set_value(f); }
124 Real get_radius()const { return adj_radius.get_value(); }
125 void set_radius(Real f) { adj_radius.set_value(f); }
127 bool get_relative() const { return check_relative.get_active(); }
128 void set_relative(bool r) { check_relative.set_active(r); }
130 void refresh_tool_options(); //to refresh the toolbox
133 Smach::event_result event_stop_handler(const Smach::event& x);
134 Smach::event_result event_refresh_handler(const Smach::event& x);
135 Smach::event_result event_mouse_handler(const Smach::event& x);
136 Smach::event_result event_refresh_tool_options(const Smach::event& x);
138 //constructor destructor
139 StateWidth_Context(CanvasView* canvas_view);
140 ~StateWidth_Context();
143 const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
144 etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
145 synfig::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
146 WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
148 //Modifying settings etc.
149 void load_settings();
150 void save_settings();
153 }; // END of class StateWidth_Context
155 /* === M E T H O D S ======================================================= */
157 StateWidth::StateWidth():
158 Smach::state<StateWidth_Context>("width")
160 insert(event_def(EVENT_STOP,&StateWidth_Context::event_stop_handler));
161 insert(event_def(EVENT_REFRESH,&StateWidth_Context::event_refresh_handler));
162 insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,&StateWidth_Context::event_mouse_handler));
163 insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DRAG,&StateWidth_Context::event_mouse_handler));
164 insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_UP,&StateWidth_Context::event_mouse_handler));
165 insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateWidth_Context::event_refresh_tool_options));
168 StateWidth::~StateWidth()
173 StateWidth_Context::load_settings()
177 //parse the arguments yargh!
178 if(settings.get_value("width.delta",value))
179 set_delta(atof(value.c_str()));
183 if(settings.get_value("width.radius",value))
184 set_radius(atof(value.c_str()));
189 if(settings.get_value("width.relative",value) && value == "1")
196 StateWidth_Context::save_settings()
198 settings.set_value("width.delta",strprintf("%f",get_delta()));
199 settings.set_value("width.radius",strprintf("%f",get_radius()));
200 settings.set_value("width.relative",get_relative()?"1":"0");
204 StateWidth_Context::reset()
209 StateWidth_Context::StateWidth_Context(CanvasView* canvas_view):
210 canvas_view_(canvas_view),
211 is_working(*canvas_view),
212 prev_workarea_layer_clicking(get_work_area()->get_allow_layer_clicks()),
213 prev_workarea_duck_clicking(get_work_area()->get_allow_duck_clicks()),
214 old_duckmask(get_work_area()->get_type_mask()),
216 settings(synfigapp::Main::get_selected_input_device()->settings()),
218 adj_delta(6,0,20,0.01,0.1),
219 spin_delta(adj_delta,0.01,3),
221 adj_radius(200,0,1e50,1,10),
222 spin_radius(adj_radius,1,1),
224 check_relative(_("Relative Growth"))
228 // Set up the tool options dialog
229 options_table.attach(*manage(new Gtk::Label(_("Width Tool"))), 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
232 options_table.attach(*manage(new Gtk::Label(_("Growth:"))), 0, 1, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
233 options_table.attach(spin_delta, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
235 options_table.attach(*manage(new Gtk::Label(_("Radius:"))), 0, 1, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
236 options_table.attach(spin_radius, 1, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
238 options_table.attach(check_relative, 0, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
240 options_table.show_all();
242 refresh_tool_options();
243 App::dialog_tool_options->present();
245 // Turn off layer clicking
246 get_work_area()->set_allow_layer_clicks(false);
248 // clear out the ducks
249 //get_work_area()->clear_ducks();
251 // Refresh the work area
252 get_work_area()->queue_draw();
254 //Create the new ducks
260 center->set_name("p1");
261 center->set_type(Duck::TYPE_POSITION);
267 radius->set_origin(center);
268 radius->set_radius(true);
269 radius->set_type(Duck::TYPE_RADIUS);
270 radius->set_name("radius");
275 closestpoint = new Duck();
276 closestpoint->set_name("closest");
277 closestpoint->set_type(Duck::TYPE_POSITION);
280 //Disable duck clicking for the maximum coolness :)
281 get_work_area()->set_allow_duck_clicks(false);
282 get_work_area()->set_type_mask((Duck::Type)((int)Duck::TYPE_WIDTH + (int)Duck::TYPE_RADIUS));
284 // Turn the mouse pointer to crosshairs
285 get_work_area()->set_cursor(Gdk::CROSSHAIR);
287 // Hide the tables if they are showing
288 //prev_table_status=get_canvas_view()->tables_are_visible();
289 //if(prev_table_status)get_canvas_view()->hide_tables();
291 // Disable the time bar
292 //get_canvas_view()->set_sensitive_timebar(false);
295 //get_work_area()->signal_user_click().connect(sigc::mem_fun(*this,&studio::StateWidth_Context::on_user_click));
297 App::toolbox->refresh();
301 StateWidth_Context::refresh_tool_options()
303 App::dialog_tool_options->clear();
304 App::dialog_tool_options->set_widget(options_table);
305 App::dialog_tool_options->set_local_name(_("Width Tool"));
306 App::dialog_tool_options->set_name("width");
310 StateWidth_Context::event_refresh_tool_options(const Smach::event& /*x*/)
312 refresh_tool_options();
313 return Smach::RESULT_ACCEPT;
316 StateWidth_Context::~StateWidth_Context()
320 //remove ducks if need be
323 get_work_area()->erase_duck(center);
324 get_work_area()->erase_duck(radius);
325 get_work_area()->erase_duck(closestpoint);
329 // Restore Duck clicking
330 get_work_area()->set_allow_duck_clicks(prev_workarea_duck_clicking);
332 // Restore layer clicking
333 get_work_area()->set_allow_layer_clicks(prev_workarea_layer_clicking);
335 // Restore the mouse pointer
336 get_work_area()->reset_cursor();
338 // Restore duck masking
339 get_work_area()->set_type_mask(old_duckmask);
341 // Tool options be rid of ye!!
342 App::dialog_tool_options->clear();
344 // Enable the time bar
345 //get_canvas_view()->set_sensitive_timebar(true);
347 // Bring back the tables if they were out before
348 //if(prev_table_status)get_canvas_view()->show_tables();
350 // Refresh the work area
351 get_work_area()->queue_draw();
353 App::toolbox->refresh();
357 StateWidth_Context::event_stop_handler(const Smach::event& /*x*/)
359 //throw Smach::egress_exception();
361 return Smach::RESULT_OK;
365 StateWidth_Context::event_refresh_handler(const Smach::event& /*x*/)
368 return Smach::RESULT_ACCEPT;
372 StateWidth_Context::AdjustWidth(handle<Duckmatic::Bezier> c, float t, Real mult, bool invert)
374 //Leave the function if there is no curve
377 Real amount1=0,amount2=0;
379 //decide how much to change each width
383 both pressure and multiply amount are in mult
384 (may want to change this to allow different types of falloff)
386 rsq is the squared distance from the point on the curve (also part of the falloff)
390 //may want to provide a different falloff function...
398 amount1 = (1-t)*mult;
408 handle<Duck> p1 = c->p1;
409 handle<Duck> p2 = c->p2;
415 const DuckList dl = get_work_area()->get_duck_list();
417 DuckList::const_iterator i = dl.begin();
419 for(;i != dl.end(); ++i)
421 if((*i)->get_type() == Duck::TYPE_WIDTH)
423 if((*i)->get_origin_duck() == p1)
428 if((*i)->get_origin_duck() == p2)
436 if(amount1 != 0 && w1)
438 Real width = w1->get_point().mag();
441 w1->set_point(Vector(width,0));
443 //log in the list of changes...
444 //to truly be changed after everything is said and done
445 changetable[w1] = width;
448 if(amount2 != 0 && w2)
450 Real width = w2->get_point().mag();
453 w2->set_point(Vector(width,0));
455 //log in the list of changes...
456 //to truly be changed after everything is said and done
457 changetable[w2] = width;
462 StateWidth_Context::event_mouse_handler(const Smach::event& x)
464 const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
467 if( (event.key == EVENT_WORKAREA_MOUSE_BUTTON_DOWN || event.key == EVENT_WORKAREA_MOUSE_BUTTON_DRAG)
468 && event.button == BUTTON_LEFT )
470 const Real pw = get_work_area()->get_pw();
471 const Real ph = get_work_area()->get_ph();
472 const Real scale = sqrt(pw*pw+ph*ph);
473 const Real rad = get_relative() ? scale * get_radius() : get_radius();
475 bool invert = (event.modifier&Gdk::CONTROL_MASK);
477 const Real threshold = 0.08;
484 //if we're dragging get the difference in time between now and then
485 if(event.key == EVENT_WORKAREA_MOUSE_BUTTON_DRAG)
487 dtime = min(1/15.0,clocktime());
491 //make way for new ducks
492 //get_work_area()->clear_ducks();
495 //mouse_pos = event.pos;
497 center->set_point(event.pos);
498 if(!added)get_work_area()->add_duck(center);
500 radius->set_scalar(rad);
501 if(!added)get_work_area()->add_duck(radius);
503 //the other duck is at the current duck
504 closestpoint->set_point(event.pos);
505 if(!added)get_work_area()->add_duck(closestpoint);
507 //get the closest curve...
508 handle<Duckmatic::Bezier> c;
509 if(event.pressure >= threshold)
510 c = get_work_area()->find_bezier(event.pos,scale*8,rad,&t);
512 //run algorithm on event.pos to get 2nd placement
518 curve[0] = c->p1->get_trans_point();
519 curve[1] = c->c1->get_trans_point();
520 curve[2] = c->c2->get_trans_point();
521 curve[3] = c->p2->get_trans_point();
524 rsq = (p-event.pos).mag_squared();
526 const Real r = rad*rad;
530 closestpoint->set_point(curve(t));
532 //adjust the width...
533 //squared falloff for radius... [0,1]
535 Real ri = (r - rsq)/r;
536 AdjustWidth(c,t,ri*event.pressure*get_delta()*dtime,invert);
540 //the points have been added
543 //draw where it is yo!
544 get_work_area()->queue_draw();
546 return Smach::RESULT_ACCEPT;
549 if(event.key == EVENT_WORKAREA_MOUSE_BUTTON_UP && event.button == BUTTON_LEFT)
553 get_work_area()->erase_duck(center);
554 get_work_area()->erase_duck(radius);
555 get_work_area()->erase_duck(closestpoint);
559 //Affect the width changes here...
560 map<handle<Duck>,Real>::iterator i = changetable.begin();
562 synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Sketch Width"));
563 for(; i != changetable.end(); ++i)
565 //for each duck modify IT!!!
566 ValueDesc desc = i->first->get_value_desc();
568 if( desc.get_value_type() == ValueBase::TYPE_REAL )
570 Action::Handle action(Action::create("ValueDescSet"));
573 action->set_param("canvas",get_canvas());
574 action->set_param("canvas_interface",get_canvas_interface());
576 action->set_param("value_desc",desc);
577 action->set_param("new_value",ValueBase(i->second));
578 action->set_param("time",get_canvas_view()->get_time());
580 if(!action->is_ready() || !get_canvas_view()->get_instance()->perform_action(action))
583 synfig::warning("Changing the width action has failed");
584 return Smach::RESULT_ERROR;
591 get_work_area()->queue_draw();
593 return Smach::RESULT_ACCEPT;
596 return Smach::RESULT_OK;
601 StateWidth_Context::refresh_ducks()
603 get_work_area()->clear_ducks();
604 get_work_area()->queue_draw();