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>
62 /* === U S I N G =========================================================== */
66 using namespace synfig;
67 using namespace synfigapp;
68 using namespace studio;
70 /* === M A C R O S ========================================================= */
72 /* === G L O B A L S ======================================================= */
74 StateWidth studio::state_width;
76 /* === C L A S S E S & S T R U C T S ======================================= */
78 class studio::StateWidth_Context : public sigc::trackable
80 etl::handle<CanvasView> canvas_view_;
81 CanvasView::IsWorking is_working;
87 handle<Duck> closestpoint;
89 map<handle<Duck>,Real> changetable;
98 bool prev_workarea_layer_clicking;
99 bool prev_workarea_duck_clicking;
100 Duckmatic::Type old_duckmask;
103 synfigapp::Settings& settings;
106 Gtk::Table options_table;
108 //Gtk::Entry entry_id; //what to name the layer
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 StateGradient_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 == "0")
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,1,0.001,0.01),
220 spin_delta(adj_delta,0.01,3),
222 adj_radius(0,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);
231 //options_table.attach(entry_id, 0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
234 options_table.attach(*manage(new Gtk::Label(_("Growth:"))), 0, 1, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
235 options_table.attach(spin_delta, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
237 options_table.attach(*manage(new Gtk::Label(_("Radius:"))), 0, 1, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
238 options_table.attach(spin_radius, 1, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
240 options_table.attach(check_relative, 0, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
242 options_table.show_all();
244 refresh_tool_options();
245 App::dialog_tool_options->present();
247 // Turn off layer clicking
248 get_work_area()->set_allow_layer_clicks(false);
250 // clear out the ducks
251 //get_work_area()->clear_ducks();
253 // Refresh the work area
254 get_work_area()->queue_draw();
256 //Create the new ducks
262 center->set_name("p1");
263 center->set_type(Duck::TYPE_POSITION);
269 radius->set_origin(center);
270 radius->set_radius(true);
271 radius->set_type(Duck::TYPE_RADIUS);
272 radius->set_name("radius");
277 closestpoint = new Duck();
278 closestpoint->set_name("closest");
279 closestpoint->set_type(Duck::TYPE_POSITION);
282 //Disable duck clicking for the maximum coolness :)
283 get_work_area()->set_allow_duck_clicks(false);
284 get_work_area()->set_type_mask((Duck::Type)((int)Duck::TYPE_WIDTH + (int)Duck::TYPE_RADIUS));
286 // Turn the mouse pointer to crosshairs
287 get_work_area()->set_cursor(Gdk::CROSSHAIR);
289 // Hide the tables if they are showing
290 //prev_table_status=get_canvas_view()->tables_are_visible();
291 //if(prev_table_status)get_canvas_view()->hide_tables();
293 // Disable the time bar
294 //get_canvas_view()->set_sensitive_timebar(false);
297 //get_work_area()->signal_user_click().connect(sigc::mem_fun(*this,&studio::StateWidth_Context::on_user_click));
299 App::toolbox->refresh();
303 StateWidth_Context::refresh_tool_options()
305 App::dialog_tool_options->clear();
306 App::dialog_tool_options->set_widget(options_table);
307 App::dialog_tool_options->set_local_name(_("Width Tool"));
308 App::dialog_tool_options->set_name("width");
312 StateWidth_Context::event_refresh_tool_options(const Smach::event& /*x*/)
314 refresh_tool_options();
315 return Smach::RESULT_ACCEPT;
318 StateWidth_Context::~StateWidth_Context()
322 //remove ducks if need be
325 get_work_area()->erase_duck(center);
326 get_work_area()->erase_duck(radius);
327 get_work_area()->erase_duck(closestpoint);
331 // Restore Duck clicking
332 get_work_area()->set_allow_duck_clicks(prev_workarea_duck_clicking);
334 // Restore layer clicking
335 get_work_area()->set_allow_layer_clicks(prev_workarea_layer_clicking);
337 // Restore the mouse pointer
338 get_work_area()->reset_cursor();
340 // Restore duck masking
341 get_work_area()->set_type_mask(old_duckmask);
343 // Tool options be rid of ye!!
344 App::dialog_tool_options->clear();
346 // Enable the time bar
347 //get_canvas_view()->set_sensitive_timebar(true);
349 // Bring back the tables if they were out before
350 //if(prev_table_status)get_canvas_view()->show_tables();
352 // Refresh the work area
353 get_work_area()->queue_draw();
355 App::toolbox->refresh();
359 StateWidth_Context::event_stop_handler(const Smach::event& /*x*/)
361 throw Smach::egress_exception();
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("value_desc_set"));
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();