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
10 ** This package is free software; you can redistribute it and/or
11 ** modify it under the terms of the GNU General Public License as
12 ** published by the Free Software Foundation; either version 2 of
13 ** the License, or (at your option) any later version.
15 ** This package is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 ** General Public License for more details.
21 /* ========================================================================= */
23 /* === H E A D E R S ======================================================= */
32 #include <gtkmm/dialog.h>
33 #include <gtkmm/entry.h>
37 #include <synfig/valuenode_dynamiclist.h>
38 #include <synfigapp/action_system.h>
40 #include "state_width.h"
41 #include "canvasview.h"
45 #include <synfigapp/action.h>
46 #include "event_mouse.h"
47 #include "event_layerclick.h"
49 #include "dialog_tooloptions.h"
50 #include <gtkmm/optionmenu.h>
53 //#include <synfigapp/value_desc.h>
54 #include <synfigapp/main.h>
60 /* === U S I N G =========================================================== */
64 using namespace synfig;
65 using namespace synfigapp;
66 using namespace studio;
68 /* === M A C R O S ========================================================= */
70 /* === G L O B A L S ======================================================= */
72 StateWidth studio::state_width;
74 /* === C L A S S E S & S T R U C T S ======================================= */
76 class studio::StateWidth_Context : public sigc::trackable
78 etl::handle<CanvasView> canvas_view_;
79 CanvasView::IsWorking is_working;
85 handle<Duck> closestpoint;
87 map<handle<Duck>,Real> changetable;
96 bool prev_workarea_layer_clicking;
97 bool prev_workarea_duck_clicking;
98 Duckmatic::Type old_duckmask;
101 synfigapp::Settings& settings;
104 Gtk::Table options_table;
106 //Gtk::Entry entry_id; //what to name the layer
108 Gtk::Adjustment adj_delta;
109 Gtk::SpinButton spin_delta;
111 Gtk::Adjustment adj_radius;
112 Gtk::SpinButton spin_radius;
114 Gtk::CheckButton check_relative;
116 void AdjustWidth(handle<Duckmatic::Bezier> c, float t, Real mult, bool invert);
120 Real get_delta()const { return adj_delta.get_value(); }
121 void set_delta(Real f) { adj_delta.set_value(f); }
123 Real get_radius()const { return adj_radius.get_value(); }
124 void set_radius(Real f) { adj_radius.set_value(f); }
126 bool get_relative() const { return check_relative.get_active(); }
127 void set_relative(bool r) { check_relative.set_active(r); }
129 void refresh_tool_options(); //to refresh the toolbox
132 Smach::event_result event_stop_handler(const Smach::event& x);
133 Smach::event_result event_refresh_handler(const Smach::event& x);
134 Smach::event_result event_mouse_handler(const Smach::event& x);
135 Smach::event_result event_refresh_tool_options(const Smach::event& x);
137 //constructor destructor
138 StateWidth_Context(CanvasView* canvas_view);
139 ~StateWidth_Context();
142 const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
143 etl::handle<synfigapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
144 synfig::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
145 WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
147 //Modifying settings etc.
148 void load_settings();
149 void save_settings();
152 }; // END of class StateGradient_Context
154 /* === M E T H O D S ======================================================= */
156 StateWidth::StateWidth():
157 Smach::state<StateWidth_Context>("width")
159 insert(event_def(EVENT_STOP,&StateWidth_Context::event_stop_handler));
160 insert(event_def(EVENT_REFRESH,&StateWidth_Context::event_refresh_handler));
161 insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,&StateWidth_Context::event_mouse_handler));
162 insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DRAG,&StateWidth_Context::event_mouse_handler));
163 insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_UP,&StateWidth_Context::event_mouse_handler));
164 insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateWidth_Context::event_refresh_tool_options));
167 StateWidth::~StateWidth()
172 StateWidth_Context::load_settings()
176 //parse the arguments yargh!
177 if(settings.get_value("width.delta",value))
178 set_delta(atof(value.c_str()));
182 if(settings.get_value("width.radius",value))
183 set_radius(atof(value.c_str()));
188 if(settings.get_value("width.relative",value) && value == "0")
195 StateWidth_Context::save_settings()
197 settings.set_value("width.delta",strprintf("%f",get_delta()));
198 settings.set_value("width.radius",strprintf("%f",get_radius()));
199 settings.set_value("width.relative",get_relative()?"1":"0");
203 StateWidth_Context::reset()
208 StateWidth_Context::StateWidth_Context(CanvasView* canvas_view):
209 canvas_view_(canvas_view),
210 is_working(*canvas_view),
211 prev_workarea_layer_clicking(get_work_area()->allow_layer_clicks),
212 prev_workarea_duck_clicking(get_work_area()->allow_duck_clicks),
213 old_duckmask(get_work_area()->get_type_mask()),
215 settings(synfigapp::Main::get_selected_input_device()->settings()),
217 adj_delta(6,0,1,0.001,0.01),
218 spin_delta(adj_delta,0.01,3),
220 adj_radius(0,0,1e50,1,10),
221 spin_radius(adj_radius,1,1),
223 check_relative(_("Relative Growth"))
227 // Set up the tool options dialog
228 //options_table.attach(*manage(new Gtk::Label(_("Width Tool"))), 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
229 //options_table.attach(entry_id, 0, 2, 1, 2, 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()->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()->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();
292 //get_canvas_view()->hide_timebar();
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()->allow_duck_clicks = prev_workarea_duck_clicking;
332 // Restore layer clicking
333 get_work_area()->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();
345 if(get_canvas_view()->get_canvas()->rend_desc().get_time_start()!=get_canvas_view()->get_canvas()->rend_desc().get_time_end())
346 get_canvas_view()->show_timebar();
348 // Bring back the tables if they were out before
349 //if(prev_table_status)get_canvas_view()->show_tables();
351 // Refresh the work area
352 get_work_area()->queue_draw();
354 App::toolbox->refresh();
358 StateWidth_Context::event_stop_handler(const Smach::event& x)
360 throw Smach::egress_exception();
364 StateWidth_Context::event_refresh_handler(const Smach::event& x)
367 return Smach::RESULT_ACCEPT;
371 StateWidth_Context::AdjustWidth(handle<Duckmatic::Bezier> c, float t, Real mult, bool invert)
373 //Leave the function if there is no curve
376 Real amount1=0,amount2=0;
378 //decide how much to change each width
382 both pressure and multiply amount are in mult
383 (may want to change this to allow different types of falloff)
385 rsq is the squared distance from the point on the curve (also part of the falloff)
389 //may want to provide a different falloff function...
397 amount1 = (1-t)*mult;
407 handle<Duck> p1 = c->p1;
408 handle<Duck> p2 = c->p2;
414 const DuckList dl = get_work_area()->get_duck_list();
416 DuckList::const_iterator i = dl.begin();
418 for(;i != dl.end(); ++i)
420 if((*i)->get_type() == Duck::TYPE_WIDTH)
422 if((*i)->get_origin_duck() == p1)
427 if((*i)->get_origin_duck() == p2)
435 if(amount1 != 0 && w1)
437 Real width = w1->get_point().mag();
440 w1->set_point(Vector(width,0));
442 //log in the list of changes...
443 //to truly be changed after everything is said and done
444 changetable[w1] = width;
447 if(amount2 != 0 && w2)
449 Real width = w2->get_point().mag();
452 w2->set_point(Vector(width,0));
454 //log in the list of changes...
455 //to truly be changed after everything is said and done
456 changetable[w2] = width;
461 StateWidth_Context::event_mouse_handler(const Smach::event& x)
463 const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
466 if( (event.key == EVENT_WORKAREA_MOUSE_BUTTON_DOWN || event.key == EVENT_WORKAREA_MOUSE_BUTTON_DRAG)
467 && event.button == BUTTON_LEFT )
469 const Real pw = get_work_area()->get_pw();
470 const Real ph = get_work_area()->get_ph();
471 const Real scale = sqrt(pw*pw+ph*ph);
472 const Real rad = get_relative() ? scale * get_radius() : get_radius();
474 bool invert = (event.modifier&Gdk::CONTROL_MASK);
476 const Real threshold = 0.08;
483 //if we're dragging get the difference in time between now and then
484 if(event.key == EVENT_WORKAREA_MOUSE_BUTTON_DRAG)
486 dtime = min(1/15.0,clocktime());
490 //make way for new ducks
491 //get_work_area()->clear_ducks();
494 //mouse_pos = event.pos;
496 center->set_point(event.pos);
497 if(!added)get_work_area()->add_duck(center);
499 radius->set_scalar(rad);
500 if(!added)get_work_area()->add_duck(radius);
502 //the other duck is at the current duck
503 closestpoint->set_point(event.pos);
504 if(!added)get_work_area()->add_duck(closestpoint);
506 //get the closest curve...
507 handle<Duckmatic::Bezier> c;
508 if(event.pressure >= threshold)
509 c = get_work_area()->find_bezier(event.pos,scale*8,rad,&t);
511 //run algorithm on event.pos to get 2nd placement
517 curve[0] = c->p1->get_trans_point();
518 curve[1] = c->c1->get_trans_point();
519 curve[2] = c->c2->get_trans_point();
520 curve[3] = c->p2->get_trans_point();
523 rsq = (p-event.pos).mag_squared();
525 const Real r = rad*rad;
529 closestpoint->set_point(curve(t));
531 //adjust the width...
532 //squared falloff for radius... [0,1]
534 Real ri = (r - rsq)/r;
535 AdjustWidth(c,t,ri*event.pressure*get_delta()*dtime,invert);
539 //the points have been added
542 //draw where it is yo!
543 get_work_area()->queue_draw();
545 return Smach::RESULT_ACCEPT;
548 if(event.key == EVENT_WORKAREA_MOUSE_BUTTON_UP && event.button == BUTTON_LEFT)
552 get_work_area()->erase_duck(center);
553 get_work_area()->erase_duck(radius);
554 get_work_area()->erase_duck(closestpoint);
558 //Affect the width changes here...
559 map<handle<Duck>,Real>::iterator i = changetable.begin();
561 synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Sketch Width"));
562 for(; i != changetable.end(); ++i)
564 //for each duck modify IT!!!
565 ValueDesc desc = i->first->get_value_desc();
567 if( desc.get_value_type() == ValueBase::TYPE_REAL )
569 Action::Handle action(Action::create("value_desc_set"));
572 action->set_param("canvas",get_canvas());
573 action->set_param("canvas_interface",get_canvas_interface());
575 action->set_param("value_desc",desc);
576 action->set_param("new_value",ValueBase(i->second));
577 action->set_param("time",get_canvas_view()->get_time());
579 if(!action->is_ready() || !get_canvas_view()->get_instance()->perform_action(action))
582 synfig::warning("Changing the width action has failed");
583 return Smach::RESULT_ERROR;
590 get_work_area()->queue_draw();
592 return Smach::RESULT_ACCEPT;
595 return Smach::RESULT_OK;
600 StateWidth_Context::refresh_ducks()
602 get_work_area()->clear_ducks();
603 get_work_area()->queue_draw();