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 //parse the arguments yargh!
179 if(settings.get_value("width.delta",value))
180 set_delta(atof(value.c_str()));
184 if(settings.get_value("width.radius",value))
185 set_radius(atof(value.c_str()));
190 if(settings.get_value("width.relative",value) && value == "1")
197 StateWidth_Context::save_settings()
199 settings.set_value("width.delta",strprintf("%f",get_delta()));
200 settings.set_value("width.radius",strprintf("%f",get_radius()));
201 settings.set_value("width.relative",get_relative()?"1":"0");
205 StateWidth_Context::reset()
210 StateWidth_Context::StateWidth_Context(CanvasView* canvas_view):
211 canvas_view_(canvas_view),
212 is_working(*canvas_view),
213 prev_workarea_layer_clicking(get_work_area()->get_allow_layer_clicks()),
214 prev_workarea_duck_clicking(get_work_area()->get_allow_duck_clicks()),
215 old_duckmask(get_work_area()->get_type_mask()),
217 settings(synfigapp::Main::get_selected_input_device()->settings()),
219 adj_delta(6,0,20,0.01,0.1),
220 spin_delta(adj_delta,0.01,3),
222 adj_radius(200,0,1e50,1,10),
223 spin_radius(adj_radius,1,1),
225 check_relative(_("Relative Growth"))
229 // Set up the tool options dialog
230 options_table.attach(*manage(new Gtk::Label(_("Width Tool"))), 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
233 options_table.attach(*manage(new Gtk::Label(_("Growth:"))), 0, 1, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
234 options_table.attach(spin_delta, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
236 options_table.attach(*manage(new Gtk::Label(_("Radius:"))), 0, 1, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
237 options_table.attach(spin_radius, 1, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
239 options_table.attach(check_relative, 0, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
241 options_table.show_all();
243 refresh_tool_options();
244 App::dialog_tool_options->present();
246 // Turn off layer clicking
247 get_work_area()->set_allow_layer_clicks(false);
249 // clear out the ducks
250 //get_work_area()->clear_ducks();
252 // Refresh the work area
253 get_work_area()->queue_draw();
255 //Create the new ducks
261 center->set_name("p1");
262 center->set_type(Duck::TYPE_POSITION);
268 radius->set_origin(center);
269 radius->set_radius(true);
270 radius->set_type(Duck::TYPE_RADIUS);
271 radius->set_name("radius");
276 closestpoint = new Duck();
277 closestpoint->set_name("closest");
278 closestpoint->set_type(Duck::TYPE_POSITION);
281 //Disable duck clicking for the maximum coolness :)
282 get_work_area()->set_allow_duck_clicks(false);
283 get_work_area()->set_type_mask((Duck::Type)((int)Duck::TYPE_WIDTH + (int)Duck::TYPE_RADIUS));
285 // Turn the mouse pointer to crosshairs
286 get_work_area()->set_cursor(Gdk::CROSSHAIR);
288 // Hide the tables if they are showing
289 //prev_table_status=get_canvas_view()->tables_are_visible();
290 //if(prev_table_status)get_canvas_view()->hide_tables();
292 // Disable the time bar
293 //get_canvas_view()->set_sensitive_timebar(false);
296 //get_work_area()->signal_user_click().connect(sigc::mem_fun(*this,&studio::StateWidth_Context::on_user_click));
298 App::toolbox->refresh();
302 StateWidth_Context::refresh_tool_options()
304 App::dialog_tool_options->clear();
305 App::dialog_tool_options->set_widget(options_table);
306 App::dialog_tool_options->set_local_name(_("Width Tool"));
307 App::dialog_tool_options->set_name("width");
311 StateWidth_Context::event_refresh_tool_options(const Smach::event& /*x*/)
313 refresh_tool_options();
314 return Smach::RESULT_ACCEPT;
317 StateWidth_Context::~StateWidth_Context()
321 //remove ducks if need be
324 get_work_area()->erase_duck(center);
325 get_work_area()->erase_duck(radius);
326 get_work_area()->erase_duck(closestpoint);
330 // Restore Duck clicking
331 get_work_area()->set_allow_duck_clicks(prev_workarea_duck_clicking);
333 // Restore layer clicking
334 get_work_area()->set_allow_layer_clicks(prev_workarea_layer_clicking);
336 // Restore the mouse pointer
337 get_work_area()->reset_cursor();
339 // Restore duck masking
340 get_work_area()->set_type_mask(old_duckmask);
342 // Tool options be rid of ye!!
343 App::dialog_tool_options->clear();
345 // Enable the time bar
346 //get_canvas_view()->set_sensitive_timebar(true);
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();
362 return Smach::RESULT_OK;
366 StateWidth_Context::event_refresh_handler(const Smach::event& /*x*/)
369 return Smach::RESULT_ACCEPT;
373 StateWidth_Context::AdjustWidth(handle<Duckmatic::Bezier> c, float t, Real mult, bool invert)
375 //Leave the function if there is no curve
378 Real amount1=0,amount2=0;
380 //decide how much to change each width
384 both pressure and multiply amount are in mult
385 (may want to change this to allow different types of falloff)
387 rsq is the squared distance from the point on the curve (also part of the falloff)
391 //may want to provide a different falloff function...
399 amount1 = (1-t)*mult;
409 handle<Duck> p1 = c->p1;
410 handle<Duck> p2 = c->p2;
416 const DuckList dl = get_work_area()->get_duck_list();
418 DuckList::const_iterator i = dl.begin();
420 for(;i != dl.end(); ++i)
422 if((*i)->get_type() == Duck::TYPE_WIDTH)
424 if((*i)->get_origin_duck() == p1)
429 if((*i)->get_origin_duck() == p2)
437 if(amount1 != 0 && w1)
439 Real width = w1->get_point().mag();
442 w1->set_point(Vector(width,0));
444 //log in the list of changes...
445 //to truly be changed after everything is said and done
446 changetable[w1] = width;
449 if(amount2 != 0 && w2)
451 Real width = w2->get_point().mag();
454 w2->set_point(Vector(width,0));
456 //log in the list of changes...
457 //to truly be changed after everything is said and done
458 changetable[w2] = width;
463 StateWidth_Context::event_mouse_handler(const Smach::event& x)
465 const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
468 if( (event.key == EVENT_WORKAREA_MOUSE_BUTTON_DOWN || event.key == EVENT_WORKAREA_MOUSE_BUTTON_DRAG)
469 && event.button == BUTTON_LEFT )
471 const Real pw = get_work_area()->get_pw();
472 const Real ph = get_work_area()->get_ph();
473 const Real scale = sqrt(pw*pw+ph*ph);
474 const Real rad = get_relative() ? scale * get_radius() : get_radius();
476 bool invert = (event.modifier&Gdk::CONTROL_MASK);
478 const Real threshold = 0.08;
485 //if we're dragging get the difference in time between now and then
486 if(event.key == EVENT_WORKAREA_MOUSE_BUTTON_DRAG)
488 dtime = min(1/15.0,clocktime());
492 //make way for new ducks
493 //get_work_area()->clear_ducks();
496 //mouse_pos = event.pos;
498 center->set_point(event.pos);
499 if(!added)get_work_area()->add_duck(center);
501 radius->set_scalar(rad);
502 if(!added)get_work_area()->add_duck(radius);
504 //the other duck is at the current duck
505 closestpoint->set_point(event.pos);
506 if(!added)get_work_area()->add_duck(closestpoint);
508 //get the closest curve...
509 handle<Duckmatic::Bezier> c;
510 if(event.pressure >= threshold)
511 c = get_work_area()->find_bezier(event.pos,scale*8,rad,&t);
513 //run algorithm on event.pos to get 2nd placement
519 curve[0] = c->p1->get_trans_point();
520 curve[1] = c->c1->get_trans_point();
521 curve[2] = c->c2->get_trans_point();
522 curve[3] = c->p2->get_trans_point();
525 rsq = (p-event.pos).mag_squared();
527 const Real r = rad*rad;
531 closestpoint->set_point(curve(t));
533 //adjust the width...
534 //squared falloff for radius... [0,1]
536 Real ri = (r - rsq)/r;
537 AdjustWidth(c,t,ri*event.pressure*get_delta()*dtime,invert);
541 //the points have been added
544 //draw where it is yo!
545 get_work_area()->queue_draw();
547 return Smach::RESULT_ACCEPT;
550 if(event.key == EVENT_WORKAREA_MOUSE_BUTTON_UP && event.button == BUTTON_LEFT)
554 get_work_area()->erase_duck(center);
555 get_work_area()->erase_duck(radius);
556 get_work_area()->erase_duck(closestpoint);
560 //Affect the width changes here...
561 map<handle<Duck>,Real>::iterator i = changetable.begin();
563 synfigapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Sketch Width"));
564 for(; i != changetable.end(); ++i)
566 //for each duck modify IT!!!
567 ValueDesc desc = i->first->get_value_desc();
569 if( desc.get_value_type() == ValueBase::TYPE_REAL )
571 Action::Handle action(Action::create("ValueDescSet"));
574 action->set_param("canvas",get_canvas());
575 action->set_param("canvas_interface",get_canvas_interface());
577 action->set_param("value_desc",desc);
578 action->set_param("new_value",ValueBase(i->second));
579 action->set_param("time",get_canvas_view()->get_time());
581 if(!action->is_ready() || !get_canvas_view()->get_instance()->perform_action(action))
584 synfig::warning("Changing the width action has failed");
585 return Smach::RESULT_ERROR;
592 get_work_area()->queue_draw();
594 return Smach::RESULT_ACCEPT;
597 return Smach::RESULT_OK;
602 StateWidth_Context::refresh_ducks()
604 get_work_area()->clear_ducks();
605 get_work_area()->queue_draw();