1 /* === S Y N F I G ========================================================= */ 2 /*! \file duckmatic.h 3 ** \brief Template Header 4 ** 5 ** $Id$ 6 ** 7 ** \legal 8 ** Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley 9 ** Copyright (c) 2007, 2008 Chris Moore 10 ** Copyright (c) 2011 Nikita Kitaev 11 ** 12 ** This package is free software; you can redistribute it and/or 13 ** modify it under the terms of the GNU General Public License as 14 ** published by the Free Software Foundation; either version 2 of 15 ** the License, or (at your option) any later version. 16 ** 17 ** This package is distributed in the hope that it will be useful, 18 ** but WITHOUT ANY WARRANTY; without even the implied warranty of 19 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 ** General Public License for more details. 21 ** \endlegal 22 */ 23 /* ========================================================================= */ 24 25 /* === S T A R T =========================================================== */ 26 27 #ifndef __SYNFIG_STUDIO_DUCKMATIC_H 28 #define __SYNFIG_STUDIO_DUCKMATIC_H 29 30 /* === H E A D E R S ======================================================= */ 31 32 #include <list> 33 #include <map> 34 #include <set> 35 36 #include <ETL/smart_ptr> 37 #include <ETL/handle> 38 39 #include <synfig/vector.h> 40 #include <synfig/string.h> 41 #include <synfig/real.h> 42 #include <sigc++/sigc++.h> 43 #include <synfig/time.h> 44 #include <synfig/color.h> 45 #include <ETL/smart_ptr> 46 47 #include "duck.h" 48 #include <synfig/color.h> 49 #include <synfig/guidset.h> 50 51 /* === M A C R O S ========================================================= */ 52 53 #ifdef HASH_MAP_H 54 #include HASH_MAP_H 55 #include FUNCTIONAL_H 56 57 #ifndef __STRING_HASH__ 58 #define __STRING_HASH__ 59 class StringHash 60 { 61 # ifdef FUNCTIONAL_HASH_ON_STRING 62 HASH_MAP_NAMESPACE::hash<synfig::String> hasher_; 63 # else // FUNCTIONAL_HASH_ON_STRING 64 HASH_MAP_NAMESPACE::hash<const char*> hasher_; 65 # endif // FUNCTIONAL_HASH_ON_STRING 66 public: operator()67 size_t operator()(const synfig::String& x)const 68 { 69 # ifdef FUNCTIONAL_HASH_ON_STRING 70 return hasher_(x); 71 # else // FUNCTIONAL_HASH_ON_STRING 72 return hasher_(x.c_str()); 73 # endif // FUNCTIONAL_HASH_ON_STRING 74 } 75 }; 76 #endif 77 #else 78 #include <map> 79 #endif 80 81 /* === T Y P E D E F S ===================================================== */ 82 83 /* === C L A S S E S & S T R U C T S ======================================= */ 84 85 namespace synfigapp { class ValueDesc; class CanvasInterface; } 86 namespace synfig { class ParamDesc; } 87 88 namespace studio 89 { 90 91 class CanvasView; 92 class Duckmatic; 93 94 class DuckDrag_Base : public etl::shared_object 95 { 96 public: 97 virtual void begin_duck_drag(Duckmatic* duckmatic, const synfig::Vector& begin)=0; 98 virtual bool end_duck_drag(Duckmatic* duckmatic)=0; 99 virtual void duck_drag(Duckmatic* duckmatic, const synfig::Vector& vector)=0; 100 }; 101 102 class DuckDrag_Translate : public DuckDrag_Base 103 { 104 synfig::Vector last_translate_; 105 synfig::Vector drag_offset_; 106 synfig::Vector snap; 107 std::vector<synfig::Vector> positions; 108 bool is_moving; 109 110 public: 111 void begin_duck_drag(Duckmatic* duckmatic, const synfig::Vector& begin); 112 bool end_duck_drag(Duckmatic* duckmatic); 113 void duck_drag(Duckmatic* duckmatic, const synfig::Vector& vector); 114 }; 115 116 class BezierDrag_Base : public etl::shared_object 117 { 118 public: 119 virtual void begin_bezier_drag(Duckmatic* duckmatic, const synfig::Vector& begin, float bezier_click_pos)=0; 120 virtual bool end_bezier_drag(Duckmatic* duckmatic)=0; 121 virtual void bezier_drag(Duckmatic* duckmatic, const synfig::Vector& vector)=0; 122 }; 123 124 class BezierDrag_Default : public BezierDrag_Base 125 { 126 synfig::Vector last_translate_; 127 synfig::Vector drag_offset_; 128 float click_pos_; 129 synfig::Vector c1_initial; 130 synfig::Vector c2_initial; 131 float c1_ratio; 132 float c2_ratio; 133 //bool c1_selected; 134 //bool c2_selected; 135 //Warning: unused variables c1_selected c2_selected 136 bool is_moving; 137 138 public: 139 void begin_bezier_drag(Duckmatic* duckmatic, const synfig::Vector& begin, float bezier_click_pos); 140 bool end_bezier_drag(Duckmatic* duckmatic); 141 void bezier_drag(Duckmatic* duckmatic, const synfig::Vector& vector); 142 }; 143 144 /*! \class Duckmatic 145 ** 146 ** This class helps organize any of the devices displayed in 147 ** the work area that the user may want to interact with. 148 ** This includes ducks, beziers, and strokes 149 ** 150 */ 151 class Duckmatic 152 { 153 friend class DuckDrag_Base; 154 friend class DuckDrag_Translate; 155 156 /* 157 -- ** -- P U B L I C T Y P E S --------------------------------------------- 158 */ 159 160 public: 161 162 #ifdef HASH_MAP_H 163 typedef HASH_MAP_CLASS<synfig::GUID,etl::smart_ptr<synfig::Point>,synfig::GUIDHash> DuckDataMap; 164 #else 165 typedef std::map<synfig::GUID,etl::smart_ptr<synfig::Point> > DuckDataMap; 166 #endif 167 168 typedef studio::DuckMap DuckMap; 169 170 typedef studio::Duck Duck; 171 172 struct Stroke; 173 174 struct Bezier; 175 176 class Push; 177 178 friend class Push; 179 180 typedef Duck::Type Type; 181 182 typedef std::list<float> GuideList; 183 184 /* 185 -- ** -- P R I V A T E D A T A --------------------------------------------- 186 */ 187 188 private: 189 190 etl::loose_handle<synfigapp::CanvasInterface> canvas_interface; 191 192 Type type_mask, type_mask_state; 193 194 DuckMap duck_map; 195 196 DuckDataMap duck_data_share_map; 197 198 std::list<etl::handle<Stroke> > stroke_list_; 199 200 std::list<etl::handle<Stroke> > persistent_stroke_list_; 201 202 synfig::GUIDSet selected_ducks; 203 204 synfig::GUID last_duck_guid; 205 206 std::list<etl::handle<Bezier> > bezier_list_; 207 208 //! I cannot recall what this is for 209 //synfig::Vector snap; 210 211 etl::handle<DuckDrag_Base> duck_dragger_; 212 213 etl::handle<BezierDrag_Base> bezier_dragger_; 214 215 sigc::signal<void> signal_duck_selection_changed_; 216 sigc::signal<void, const etl::handle<Duck>& > signal_duck_selection_single_; 217 218 sigc::signal<void> signal_strokes_changed_; 219 220 sigc::signal<void> signal_grid_changed_; 221 222 mutable sigc::signal<void> signal_sketch_saved_; 223 224 GuideList guide_list_x_; 225 GuideList guide_list_y_; 226 227 mutable synfig::String sketch_filename_; 228 229 synfig::TransformStack curr_transform_stack; 230 bool curr_transform_stack_set; 231 std::list<sigc::connection> duck_changed_connections; 232 233 bool alternative_mode_; 234 bool lock_animation_mode_; 235 236 /* 237 -- ** -- P R O T E C T E D D A T A ----------------------------------------- 238 */ 239 240 protected: 241 242 etl::handle<Bezier> selected_bezier; 243 244 synfig::Time cur_time; 245 246 //! This flag is set if operations should snap to the grid 247 /*! \todo perhaps there should be two of these flags, one for each axis? 248 ** \see show_grid, grid_size */ 249 bool grid_snap; 250 251 bool guide_snap; 252 253 //! This vector describes the grid size. 254 /*! \see grid_snap, show_grid */ 255 synfig::Vector grid_size; 256 //! Hold the grid color. 257 synfig::Color grid_color; 258 //! Hold the guides color. 259 synfig::Color guides_color; 260 261 float zoom; //!< Zoom factor 262 float prev_zoom; //!< Previous Zoom factor 263 264 bool show_persistent_strokes; 265 266 bool axis_lock; 267 268 /* 269 -- ** -- P R I V A T E M E T H O D S --------------------------------------- 270 */ 271 272 private: 273 274 synfig::Vector last_translate_; 275 synfig::Vector drag_offset_; 276 277 //etl::handle<Duck> selected_duck; 278 279 void connect_signals(const Duck::Handle &duck, const synfigapp::ValueDesc& value_desc, CanvasView &canvas_view); 280 281 /* 282 -- ** -- P U B L I C M E T H O D S ----------------------------------------- 283 */ 284 285 public: 286 287 Duckmatic(etl::loose_handle<synfigapp::CanvasInterface> canvas_interface); 288 virtual ~Duckmatic(); 289 set_alternative_mode(bool x)290 void set_alternative_mode(bool x) { alternative_mode_=x; } get_alternative_mode()291 bool get_alternative_mode()const { return alternative_mode_; } 292 set_lock_animation_mode(bool x)293 void set_lock_animation_mode(bool x) { lock_animation_mode_=x; } get_lock_animation_mode()294 bool get_lock_animation_mode()const { return lock_animation_mode_; } 295 signal_duck_selection_changed()296 sigc::signal<void>& signal_duck_selection_changed() { return signal_duck_selection_changed_; } signal_duck_selection_single()297 sigc::signal<void, const etl::handle<Duck>& >& signal_duck_selection_single() { return signal_duck_selection_single_; } signal_strokes_changed()298 sigc::signal<void>& signal_strokes_changed() { return signal_strokes_changed_; } signal_grid_changed()299 sigc::signal<void>& signal_grid_changed() { return signal_grid_changed_; } signal_sketch_saved()300 sigc::signal<void>& signal_sketch_saved() { return signal_sketch_saved_; } 301 get_guide_list_x()302 GuideList& get_guide_list_x() { return guide_list_x_; } get_guide_list_y()303 GuideList& get_guide_list_y() { return guide_list_y_; } get_guide_list_x()304 const GuideList& get_guide_list_x()const { return guide_list_x_; } get_guide_list_y()305 const GuideList& get_guide_list_y()const { return guide_list_y_; } 306 307 void set_guide_snap(bool x=true); get_guide_snap()308 bool get_guide_snap()const { return guide_snap; } toggle_guide_snap()309 void toggle_guide_snap() { set_guide_snap(!get_guide_snap()); } 310 //! Sets the color of the guides 311 void set_guides_color(const synfig::Color &c); 312 //! Returns the color of the guides get_guides_color()313 const synfig::Color &get_guides_color()const { return guides_color;} 314 315 //! Sets the state of the grid snap flag 316 void set_grid_snap(bool x=true); 317 318 //! Gets the state of the grid snap flag get_grid_snap()319 bool get_grid_snap()const { return grid_snap; } 320 enable_grid_snap()321 void enable_grid_snap() { set_grid_snap(true); } 322 disable_grid_snap()323 void disable_grid_snap() { set_grid_snap(false); } 324 toggle_grid_snap()325 void toggle_grid_snap() { set_grid_snap(!grid_snap); } 326 327 synfig::Point snap_point_to_grid(const synfig::Point& x)const; 328 get_show_persistent_strokes()329 bool get_show_persistent_strokes()const { return show_persistent_strokes; } 330 void set_show_persistent_strokes(bool x); 331 332 //! Sets the size of the grid 333 void set_grid_size(const synfig::Vector &s); 334 //! Sets the color of the grid 335 void set_grid_color(const synfig::Color &c); 336 337 //! Returns the size of the grid get_grid_size()338 const synfig::Vector &get_grid_size()const { return grid_size; } 339 //! Returns the color of the grid get_grid_color()340 const synfig::Color &get_grid_color()const { return grid_color;} 341 get_time()342 const synfig::Time &get_time()const { return cur_time; } 343 get_axis_lock()344 bool get_axis_lock()const { return axis_lock; } set_axis_lock(bool x)345 void set_axis_lock(bool x) { axis_lock=x; } 346 set_time(synfig::Time x)347 void set_time(synfig::Time x) { cur_time=x; } 348 349 bool is_duck_group_selectable(const etl::handle<Duck>& x)const; 350 351 //const DuckMap& duck_map()const { return duck_map; } 352 DuckList get_duck_list()const; 353 bezier_list()354 const std::list<etl::handle<Bezier> >& bezier_list()const { return bezier_list_; } 355 stroke_list()356 const std::list<etl::handle<Stroke> >& stroke_list()const { return stroke_list_; } 357 persistent_stroke_list()358 const std::list<etl::handle<Stroke> >& persistent_stroke_list()const { return persistent_stroke_list_; } 359 persistent_stroke_list()360 std::list<etl::handle<Stroke> >& persistent_stroke_list() { return persistent_stroke_list_; } 361 362 /* 363 -- ** -- D U C K S E L E C T I O N M E T H O D S---------------------------- 364 */ 365 366 //! Return first selected duck (handle) has const Duck etl::handle 367 etl::handle<Duck> get_selected_duck()const; 368 //! Return list of selected ducks (handles) 369 /*! 370 ** \return ducks (handles) has const DuckList 371 ** \sa get_selected_duck, clear_selected_ducks, count_selected_ducks 372 */ 373 DuckList get_selected_ducks()const; 374 //! Return list of box contained ducks (handles). The box is defined by a vector's pair 375 /*! 376 ** \param tl The top left canvas coordinate has const synfig::Vector 377 ** \param br The bottom right canvas coordinate has const synfig::Vector 378 ** \return ducks (handles) has const DuckList 379 ** \sa toggle_select_ducks_in_box, select_ducks_in_box, find_duck 380 */ 381 DuckList get_ducks_in_box(const synfig::Vector& tl,const synfig::Vector& br)const; 382 383 void refresh_selected_ducks(); 384 //! Clear all selected ducks 385 void clear_selected_ducks(); 386 //! Return the number of selected ducks 387 /*! 388 ** \return the number of selected ducks (handles) has int 389 */ 390 int count_selected_ducks()const; 391 //! Give the selection status of a duck 392 /*! 393 ** \return \a true if the given duck (handle) is currently selected 394 */ 395 bool duck_is_selected(const etl::handle<Duck> &duck)const; 396 //! Toggle the duck (handle) 397 /*! 398 ** \param duck The duck (handle) to toggle has etl::handle parameter 399 */ 400 void toggle_select_duck(const etl::handle<Duck> &duck); 401 //! Select the duck (handle) 402 /*! 403 ** \param duck The duck (handle) to select has etl::handle parameter 404 */ 405 void select_duck(const etl::handle<Duck> &duck); 406 //! Unselect the duck (handle) 407 /*! 408 ** \param duck The duck (handle) to unselect has etl::handle parameter 409 */ 410 void unselect_duck(const etl::handle<Duck> &duck); 411 412 //! Toggle the ducks (handles) contained in the box defined by a pair of vectors 413 /*! 414 ** \param tl The top left canvas coordinate has const synfig::Vector 415 ** \param br The bottom right canvas coordinate has const synfig::Vector 416 ** \sa toggle_select_duck, select_ducks_in_box, get_ducks_in_box 417 */ 418 void toggle_select_ducks_in_box(const synfig::Vector& tl,const synfig::Vector& br); 419 //! Select the ducks (handles) contained in the box defined by a pair of vectors 420 /*! 421 ** \param tl The top left canvas coordinate has const synfig::Vector 422 ** \param br The bottom right canvas coordinate has const synfig::Vector 423 ** \sa toggle_select_ducks_in_box, select_ducks_in_box, clear_selected_ducks 424 */ 425 void select_ducks_in_box(const synfig::Vector& tl,const synfig::Vector& br); 426 427 get_curr_transform_stack()428 const synfig::TransformStack& get_curr_transform_stack()const { return curr_transform_stack; } 429 clear_curr_transform_stack()430 inline void clear_curr_transform_stack() { curr_transform_stack.clear(); curr_transform_stack_set=false; } 431 432 433 etl::handle<Bezier> get_selected_bezier()const; 434 435 //! Begin dragging ducks 436 /*! 437 ** \param offset Canvas coordinates of the mouse when the drag began 438 */ 439 void start_duck_drag(const synfig::Vector& offset); 440 441 //! Continue dragging the selected ducks 442 /*! The overall vector of the drag is vector-offset 443 ** (where offset was given in start_duck_drag) 444 ** \param vector Canvas coordinates of the mouse at this moment 445 */ 446 void translate_selected_ducks(const synfig::Vector& vector); 447 448 //! Update the coordinates of tangents and linked-to-bline ducks 449 void update_ducks(); 450 451 //! Ends the duck drag 452 bool end_duck_drag(); 453 454 //! \todo writeme 455 // bezier drags (similar to duck drags) 456 void start_bezier_drag(const synfig::Vector& offset, float bezier_click_pos); 457 458 void translate_selected_bezier(const synfig::Vector& vector); 459 460 bool end_bezier_drag(); 461 462 463 //! Signals to each selected duck that it has been clicked 464 void signal_user_click_selected_ducks(int button); 465 466 //! Calls a single duck's edited signal 467 /*! Updates the corresponding valuenodes after a drag */ 468 void signal_edited_duck(const etl::handle<Duck> &duck, bool moving = false); 469 470 //! Calls all of the ducks' edited signals 471 /*! Updates corresponding valuenodes after a drag */ 472 void signal_edited_selected_ducks(bool moving = false); 473 474 bool on_duck_changed(const studio::Duck &duck,const synfigapp::ValueDesc& value_desc); 475 476 etl::handle<Duck> find_similar_duck(etl::handle<Duck> duck); 477 etl::handle<Duck> add_similar_duck(etl::handle<Duck> duck); 478 479 void add_stroke(etl::smart_ptr<std::list<synfig::Point> > stroke_point_list, const synfig::Color& color=synfig::Color(0,0,0)); 480 481 void add_persistent_stroke(etl::smart_ptr<std::list<synfig::Point> > stroke_point_list, const synfig::Color& color=synfig::Color(0,0,0)); 482 483 void clear_persistent_strokes(); 484 485 void add_duck(const etl::handle<Duck> &duck); 486 487 void add_bezier(const etl::handle<Bezier> &bezier); 488 489 void erase_duck(const etl::handle<Duck> &duck); 490 491 void erase_bezier(const etl::handle<Bezier> &bezier); 492 493 //! Returns the last duck added 494 etl::handle<Duck> last_duck()const; 495 496 etl::handle<Bezier> last_bezier()const; 497 498 //! \note parameter is in canvas coordinates 499 /*! A radius of "zero" will have an unlimited radius */ 500 etl::handle<Duck> find_duck(synfig::Point pos, synfig::Real radius=0, Duck::Type type=Duck::TYPE_DEFAULT); 501 502 GuideList::iterator find_guide_x(synfig::Point pos, float radius=0.1); 503 GuideList::iterator find_guide_y(synfig::Point pos, float radius=0.1); 504 GuideList::const_iterator find_guide_x(synfig::Point pos, float radius=0.1)const { return const_cast<Duckmatic*>(this)->find_guide_x(pos,radius); } 505 GuideList::const_iterator find_guide_y(synfig::Point pos, float radius=0.1)const { return const_cast<Duckmatic*>(this)->find_guide_y(pos,radius); } 506 507 //! \note parameter is in canvas coordinates 508 /*! A radius of "zero" will have an unlimited radius */ 509 //etl::handle<Bezier> find_bezier(synfig::Point pos, synfig::Real radius=0); 510 511 //! \note parameter is in canvas coordinates 512 /*! A radius of "zero" will have an unlimited radius */ 513 etl::handle<Bezier> find_bezier(synfig::Point pos, synfig::Real radius=0, float* location=0); 514 515 etl::handle<Bezier> find_bezier(synfig::Point pos, synfig::Real scale, synfig::Real radius, float* location=0); 516 517 //! if transform_count is set function will not restore transporm stack 518 void add_ducks_layers(synfig::Canvas::Handle canvas, std::set<synfig::Layer::Handle>& selected_layer_set, etl::handle<CanvasView> canvas_view, synfig::TransformStack& transform_stack, int *transform_count = NULL); 519 520 bool add_to_ducks(const synfigapp::ValueDesc& value_desc,etl::handle<CanvasView> canvas_view, const synfig::TransformStack& transform_stack_, synfig::ParamDesc *param_desc=0); 521 522 //! Set the type mask, which determines what types of ducks are shown 523 //! \Param[in] x Duck::Type set to backup when toggling handles 524 //! \Sa get_type_mask(), CanvasView::toggle_duck_all() set_type_mask(Type x)525 void set_type_mask(Type x) { type_mask=x; } 526 //! Get the type mask, which determines what types of ducks are shown 527 //! \Sa set_type_mask(), CanvasView::toggle_duck_all() get_type_mask()528 Type get_type_mask()const { return type_mask; } 529 530 //! Set the type mask state, which determines what types of ducks are shown on toggle 531 //! \Param[in] x Duck::Type set to backup when toggling handles 532 //! \Sa get_type_mask_state(), CanvasView::toggle_duck_mask_all() set_type_mask_state(Type x)533 void set_type_mask_state(Type x) { type_mask_state=x; } 534 //! Get the type mask state, which determines what types of ducks are shown on toggle 535 //! \Sa set_type_mask_state(), CanvasView::toggle_duck_mask_all() get_type_mask_state()536 Type get_type_mask_state()const { return type_mask_state; } 537 538 void select_all_ducks(); 539 void unselect_all_ducks(); 540 541 void clear_ducks(); 542 543 bool save_sketch(const synfig::String& filename)const; 544 bool load_sketch(const synfig::String& filename); get_sketch_filename()545 const synfig::String& get_sketch_filename()const { return sketch_filename_; } 546 set_duck_dragger(etl::handle<DuckDrag_Base> x)547 void set_duck_dragger(etl::handle<DuckDrag_Base> x) { duck_dragger_=x; } get_duck_dragger()548 etl::handle<DuckDrag_Base> get_duck_dragger()const { return duck_dragger_; } clear_duck_dragger()549 void clear_duck_dragger() { duck_dragger_=new DuckDrag_Translate(); } 550 551 set_bezier_dragger(etl::handle<BezierDrag_Base> x)552 void set_bezier_dragger(etl::handle<BezierDrag_Base> x) { bezier_dragger_=x; } get_bezier_dragger()553 etl::handle<BezierDrag_Base> get_bezier_dragger()const { return bezier_dragger_; } clear_bezier_dragger()554 void clear_bezier_dragger() { bezier_dragger_=new BezierDrag_Default(); } 555 }; // END of class Duckmatic 556 557 558 /*! \class Duckmatic::Push 559 ** \writeme */ 560 class Duckmatic::Push 561 { 562 Duckmatic *duckmatic_; 563 DuckMap duck_map; 564 std::list<etl::handle<Bezier> > bezier_list_; 565 std::list<etl::handle<Stroke> > stroke_list_; 566 DuckDataMap duck_data_share_map; 567 etl::handle<DuckDrag_Base> duck_dragger_; 568 569 bool needs_restore; 570 571 public: 572 Push(Duckmatic *duckmatic_); 573 ~Push(); 574 void restore(); 575 }; // END of class Duckmatic::Push 576 577 /*! \struct Duckmatic::Bezier 578 ** \writeme */ 579 struct Duckmatic::Bezier : public etl::shared_object 580 { 581 private: 582 sigc::signal<void,float> signal_user_click_[5]; 583 public: 584 585 etl::handle<Duck> p1,p2,c1,c2; is_validBezier586 bool is_valid()const { return p1 && p2 && c1 && c2; } 587 588 sigc::signal<void,float> &signal_user_click(int i=0) { assert(i>=0); assert(i<5); return signal_user_click_[i]; } 589 }; // END of struct Duckmatic::Bezier 590 591 /*! \struct Duckmatic::Stroke 592 ** \writeme */ 593 struct Duckmatic::Stroke : public etl::shared_object 594 { 595 private: 596 sigc::signal<void,float> signal_user_click_[5]; 597 public: 598 599 etl::smart_ptr<std::list<synfig::Point> > stroke_data; 600 601 synfig::Color color; 602 is_validStroke603 bool is_valid()const { return (bool)stroke_data; } 604 605 sigc::signal<void,float> &signal_user_click(int i=0) { assert(i>=0); assert(i<5); return signal_user_click_[i]; } 606 }; // END of struct Duckmatic::Stroke 607 608 }; // END of namespace studio 609 610 /* === E N D =============================================================== */ 611 612 #endif 613