1 /* === S I N F G =========================================================== */
2 /*! \file state_gradient.cpp
3 ** \brief Template File
5 ** $Id: state_width.cpp,v 1.1.1.1 2005/01/07 03:34:37 darco Exp $
8 ** Copyright (c) 2002 Robert B. Quattlebaum Jr.
10 ** This software and associated documentation
11 ** are CONFIDENTIAL and PROPRIETARY property of
12 ** the above-mentioned copyright holder.
14 ** You may not copy, print, publish, or in any
15 ** other way distribute this software without
16 ** a prior written agreement with
17 ** the copyright holder.
20 /* ========================================================================= */
22 /* === H E A D E R S ======================================================= */
31 #include <gtkmm/dialog.h>
32 #include <gtkmm/entry.h>
36 #include <sinfg/valuenode_dynamiclist.h>
37 #include <sinfgapp/action_system.h>
39 #include "state_width.h"
40 #include "canvasview.h"
44 #include <sinfgapp/action.h>
45 #include "event_mouse.h"
46 #include "event_layerclick.h"
48 #include "dialog_tooloptions.h"
49 #include <gtkmm/optionmenu.h>
52 //#include <sinfgapp/value_desc.h>
53 #include <sinfgapp/main.h>
59 /* === U S I N G =========================================================== */
63 using namespace sinfg;
64 using namespace sinfgapp;
65 using namespace studio;
67 /* === M A C R O S ========================================================= */
69 /* === G L O B A L S ======================================================= */
71 StateWidth studio::state_width;
73 /* === C L A S S E S & S T R U C T S ======================================= */
75 class studio::StateWidth_Context : public sigc::trackable
77 etl::handle<CanvasView> canvas_view_;
78 CanvasView::IsWorking is_working;
84 handle<Duck> closestpoint;
86 map<handle<Duck>,Real> changetable;
95 bool prev_workarea_layer_clicking;
96 bool prev_workarea_duck_clicking;
97 Duckmatic::Type old_duckmask;
100 sinfgapp::Settings& settings;
103 Gtk::Table options_table;
105 //Gtk::Entry entry_id; //what to name the layer
107 Gtk::Adjustment adj_delta;
108 Gtk::SpinButton spin_delta;
110 Gtk::Adjustment adj_radius;
111 Gtk::SpinButton spin_radius;
113 Gtk::CheckButton check_relative;
115 void AdjustWidth(handle<Duckmatic::Bezier> c, float t, Real mult, bool invert);
119 Real get_delta()const { return adj_delta.get_value(); }
120 void set_delta(Real f) { adj_delta.set_value(f); }
122 Real get_radius()const { return adj_radius.get_value(); }
123 void set_radius(Real f) { adj_radius.set_value(f); }
125 bool get_relative() const { return check_relative.get_active(); }
126 void set_relative(bool r) { check_relative.set_active(r); }
128 void refresh_tool_options(); //to refresh the toolbox
131 Smach::event_result event_stop_handler(const Smach::event& x);
132 Smach::event_result event_refresh_handler(const Smach::event& x);
133 Smach::event_result event_mouse_handler(const Smach::event& x);
134 Smach::event_result event_refresh_tool_options(const Smach::event& x);
136 //constructor destructor
137 StateWidth_Context(CanvasView* canvas_view);
138 ~StateWidth_Context();
141 const etl::handle<CanvasView>& get_canvas_view()const{return canvas_view_;}
142 etl::handle<sinfgapp::CanvasInterface> get_canvas_interface()const{return canvas_view_->canvas_interface();}
143 sinfg::Canvas::Handle get_canvas()const{return canvas_view_->get_canvas();}
144 WorkArea * get_work_area()const{return canvas_view_->get_work_area();}
146 //Modifying settings etc.
147 void load_settings();
148 void save_settings();
151 }; // END of class StateGradient_Context
153 /* === M E T H O D S ======================================================= */
155 StateWidth::StateWidth():
156 Smach::state<StateWidth_Context>("width")
158 insert(event_def(EVENT_STOP,&StateWidth_Context::event_stop_handler));
159 insert(event_def(EVENT_REFRESH,&StateWidth_Context::event_refresh_handler));
160 insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DOWN,&StateWidth_Context::event_mouse_handler));
161 insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_DRAG,&StateWidth_Context::event_mouse_handler));
162 insert(event_def(EVENT_WORKAREA_MOUSE_BUTTON_UP,&StateWidth_Context::event_mouse_handler));
163 insert(event_def(EVENT_REFRESH_TOOL_OPTIONS,&StateWidth_Context::event_refresh_tool_options));
166 StateWidth::~StateWidth()
171 StateWidth_Context::load_settings()
175 //parse the arguments yargh!
176 if(settings.get_value("width.delta",value))
177 set_delta(atof(value.c_str()));
181 if(settings.get_value("width.radius",value))
182 set_radius(atof(value.c_str()));
187 if(settings.get_value("width.relative",value) && value == "0")
194 StateWidth_Context::save_settings()
196 settings.set_value("width.delta",strprintf("%f",get_delta()));
197 settings.set_value("width.radius",strprintf("%f",get_radius()));
198 settings.set_value("width.relative",get_relative()?"1":"0");
202 StateWidth_Context::reset()
207 StateWidth_Context::StateWidth_Context(CanvasView* canvas_view):
208 canvas_view_(canvas_view),
209 is_working(*canvas_view),
210 prev_workarea_layer_clicking(get_work_area()->allow_layer_clicks),
211 prev_workarea_duck_clicking(get_work_area()->allow_duck_clicks),
212 old_duckmask(get_work_area()->get_type_mask()),
214 settings(sinfgapp::Main::get_selected_input_device()->settings()),
216 adj_delta(6,0,1,0.001,0.01),
217 spin_delta(adj_delta,0.01,3),
219 adj_radius(0,0,1e50,1,10),
220 spin_radius(adj_radius,1,1),
222 check_relative(_("Relative Growth"))
226 // Set up the tool options dialog
227 //options_table.attach(*manage(new Gtk::Label(_("Width Tool"))), 0, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
228 //options_table.attach(entry_id, 0, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
231 options_table.attach(*manage(new Gtk::Label(_("Growth:"))), 0, 1, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
232 options_table.attach(spin_delta, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
234 options_table.attach(*manage(new Gtk::Label(_("Radius:"))), 0, 1, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
235 options_table.attach(spin_radius, 1, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
237 options_table.attach(check_relative, 0, 2, 3, 4, Gtk::EXPAND|Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 0);
239 options_table.show_all();
241 refresh_tool_options();
242 App::dialog_tool_options->present();
244 // Turn off layer clicking
245 get_work_area()->allow_layer_clicks=false;
247 // clear out the ducks
248 //get_work_area()->clear_ducks();
250 // Refresh the work area
251 get_work_area()->queue_draw();
253 //Create the new ducks
259 center->set_name("p1");
260 center->set_type(Duck::TYPE_POSITION);
266 radius->set_origin(center);
267 radius->set_radius(true);
268 radius->set_type(Duck::TYPE_RADIUS);
269 radius->set_name("radius");
274 closestpoint = new Duck();
275 closestpoint->set_name("closest");
276 closestpoint->set_type(Duck::TYPE_POSITION);
279 //Disable duck clicking for the maximum coolness :)
280 get_work_area()->allow_duck_clicks = false;
281 get_work_area()->set_type_mask((Duck::Type)((int)Duck::TYPE_WIDTH + (int)Duck::TYPE_RADIUS));
283 // Turn the mouse pointer to crosshairs
284 get_work_area()->set_cursor(Gdk::CROSSHAIR);
286 // Hide the tables if they are showing
287 //prev_table_status=get_canvas_view()->tables_are_visible();
288 //if(prev_table_status)get_canvas_view()->hide_tables();
291 //get_canvas_view()->hide_timebar();
294 //get_work_area()->signal_user_click().connect(sigc::mem_fun(*this,&studio::StateWidth_Context::on_user_click));
296 App::toolbox->refresh();
300 StateWidth_Context::refresh_tool_options()
302 App::dialog_tool_options->clear();
303 App::dialog_tool_options->set_widget(options_table);
304 App::dialog_tool_options->set_local_name(_("Width Tool"));
305 App::dialog_tool_options->set_name("width");
309 StateWidth_Context::event_refresh_tool_options(const Smach::event& x)
311 refresh_tool_options();
312 return Smach::RESULT_ACCEPT;
315 StateWidth_Context::~StateWidth_Context()
319 //remove ducks if need be
322 get_work_area()->erase_duck(center);
323 get_work_area()->erase_duck(radius);
324 get_work_area()->erase_duck(closestpoint);
328 // Restore Duck clicking
329 get_work_area()->allow_duck_clicks = prev_workarea_duck_clicking;
331 // Restore layer clicking
332 get_work_area()->allow_layer_clicks = prev_workarea_layer_clicking;
334 // Restore the mouse pointer
335 get_work_area()->reset_cursor();
337 // Restore duck masking
338 get_work_area()->set_type_mask(old_duckmask);
340 // Tool options be rid of ye!!
341 App::dialog_tool_options->clear();
344 if(get_canvas_view()->get_canvas()->rend_desc().get_time_start()!=get_canvas_view()->get_canvas()->rend_desc().get_time_end())
345 get_canvas_view()->show_timebar();
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();
363 StateWidth_Context::event_refresh_handler(const Smach::event& x)
366 return Smach::RESULT_ACCEPT;
370 StateWidth_Context::AdjustWidth(handle<Duckmatic::Bezier> c, float t, Real mult, bool invert)
372 //Leave the function if there is no curve
375 Real amount1=0,amount2=0;
377 //decide how much to change each width
381 both pressure and multiply amount are in mult
382 (may want to change this to allow different types of falloff)
384 rsq is the squared distance from the point on the curve (also part of the falloff)
388 //may want to provide a different falloff function...
396 amount1 = (1-t)*mult;
406 handle<Duck> p1 = c->p1;
407 handle<Duck> p2 = c->p2;
413 const DuckList dl = get_work_area()->get_duck_list();
415 DuckList::const_iterator i = dl.begin();
417 for(;i != dl.end(); ++i)
419 if((*i)->get_type() == Duck::TYPE_WIDTH)
421 if((*i)->get_origin_duck() == p1)
426 if((*i)->get_origin_duck() == p2)
434 if(amount1 != 0 && w1)
436 Real width = w1->get_point().mag();
439 w1->set_point(Vector(width,0));
441 //log in the list of changes...
442 //to truly be changed after everything is said and done
443 changetable[w1] = width;
446 if(amount2 != 0 && w2)
448 Real width = w2->get_point().mag();
451 w2->set_point(Vector(width,0));
453 //log in the list of changes...
454 //to truly be changed after everything is said and done
455 changetable[w2] = width;
460 StateWidth_Context::event_mouse_handler(const Smach::event& x)
462 const EventMouse& event(*reinterpret_cast<const EventMouse*>(&x));
465 if( (event.key == EVENT_WORKAREA_MOUSE_BUTTON_DOWN || event.key == EVENT_WORKAREA_MOUSE_BUTTON_DRAG)
466 && event.button == BUTTON_LEFT )
468 const Real pw = get_work_area()->get_pw();
469 const Real ph = get_work_area()->get_ph();
470 const Real scale = sqrt(pw*pw+ph*ph);
471 const Real rad = get_relative() ? scale * get_radius() : get_radius();
473 bool invert = (event.modifier&Gdk::CONTROL_MASK);
475 const Real threshold = 0.08;
482 //if we're dragging get the difference in time between now and then
483 if(event.key == EVENT_WORKAREA_MOUSE_BUTTON_DRAG)
485 dtime = min(1/15.0,clocktime());
489 //make way for new ducks
490 //get_work_area()->clear_ducks();
493 //mouse_pos = event.pos;
495 center->set_point(event.pos);
496 if(!added)get_work_area()->add_duck(center);
498 radius->set_scalar(rad);
499 if(!added)get_work_area()->add_duck(radius);
501 //the other duck is at the current duck
502 closestpoint->set_point(event.pos);
503 if(!added)get_work_area()->add_duck(closestpoint);
505 //get the closest curve...
506 handle<Duckmatic::Bezier> c;
507 if(event.pressure >= threshold)
508 c = get_work_area()->find_bezier(event.pos,scale*8,rad,&t);
510 //run algorithm on event.pos to get 2nd placement
516 curve[0] = c->p1->get_trans_point();
517 curve[1] = c->c1->get_trans_point();
518 curve[2] = c->c2->get_trans_point();
519 curve[3] = c->p2->get_trans_point();
522 rsq = (p-event.pos).mag_squared();
524 const Real r = rad*rad;
528 closestpoint->set_point(curve(t));
530 //adjust the width...
531 //squared falloff for radius... [0,1]
533 Real ri = (r - rsq)/r;
534 AdjustWidth(c,t,ri*event.pressure*get_delta()*dtime,invert);
538 //the points have been added
541 //draw where it is yo!
542 get_work_area()->queue_draw();
544 return Smach::RESULT_ACCEPT;
547 if(event.key == EVENT_WORKAREA_MOUSE_BUTTON_UP && event.button == BUTTON_LEFT)
551 get_work_area()->erase_duck(center);
552 get_work_area()->erase_duck(radius);
553 get_work_area()->erase_duck(closestpoint);
557 //Affect the width changes here...
558 map<handle<Duck>,Real>::iterator i = changetable.begin();
560 sinfgapp::Action::PassiveGrouper group(get_canvas_interface()->get_instance().get(),_("Sketch Width"));
561 for(; i != changetable.end(); ++i)
563 //for each duck modify IT!!!
564 ValueDesc desc = i->first->get_value_desc();
566 if( desc.get_value_type() == ValueBase::TYPE_REAL )
568 Action::Handle action(Action::create("value_desc_set"));
571 action->set_param("canvas",get_canvas());
572 action->set_param("canvas_interface",get_canvas_interface());
574 action->set_param("value_desc",desc);
575 action->set_param("new_value",ValueBase(i->second));
576 action->set_param("time",get_canvas_view()->get_time());
578 if(!action->is_ready() || !get_canvas_view()->get_instance()->perform_action(action))
581 sinfg::warning("Changing the width action has failed");
582 return Smach::RESULT_ERROR;
589 get_work_area()->queue_draw();
591 return Smach::RESULT_ACCEPT;
594 return Smach::RESULT_OK;
599 StateWidth_Context::refresh_ducks()
601 get_work_area()->clear_ducks();
602 get_work_area()->queue_draw();