]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/elements/CanvasElement.hxx
Canvas::Path: reduce number of OpenGL state changes.
[simgear.git] / simgear / canvas / elements / CanvasElement.hxx
1 // Interface for 2D Canvas elements
2 //
3 // Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Library General Public
7 // License as published by the Free Software Foundation; either
8 // version 2 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // Library General Public License for more details.
14 //
15 // You should have received a copy of the GNU Library General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
18
19 #ifndef CANVAS_ELEMENT_HXX_
20 #define CANVAS_ELEMENT_HXX_
21
22 #include <simgear/canvas/canvas_fwd.hxx>
23 #include <simgear/canvas/CanvasEvent.hxx>
24 #include <simgear/props/PropertyBasedElement.hxx>
25 #include <simgear/misc/stdint.hxx> // for uint32_t
26
27 #include <osg/BoundingBox>
28 #include <osg/MatrixTransform>
29
30 #include <boost/bind.hpp>
31 #include <boost/function.hpp>
32 #include <boost/type_traits/is_base_of.hpp>
33
34 namespace osg
35 {
36   class Drawable;
37 }
38
39 namespace simgear
40 {
41 namespace canvas
42 {
43
44   class Element:
45     public PropertyBasedElement
46   {
47     public:
48
49       /**
50        * Store pointer to window as user data
51        */
52       class OSGUserData:
53         public osg::Referenced
54       {
55         public:
56           ElementPtr element;
57           OSGUserData(ElementPtr element);
58       };
59
60       typedef boost::function<bool(Element&, const SGPropertyNode*)>
61               StyleSetterFunc;
62       typedef boost::function<void(Element&, const SGPropertyNode*)>
63               StyleSetterFuncUnchecked;
64       struct StyleSetter:
65         public SGReferenced
66       {
67         StyleSetterFunc func;
68         SGSharedPtr<StyleSetter> next;
69       };
70       struct StyleInfo
71       {
72         StyleSetter setter; ///!< Function(s) for setting this style
73         std::string type;   ///!< Interpolation type
74         bool inheritable;   ///!< Whether children can inherit this style from
75                             ///   their parents
76       };
77
78       /**
79        * Coordinate reference frame (eg. "clip" property)
80        */
81       enum ReferenceFrame
82       {
83         GLOBAL, ///!< Global coordinates
84         PARENT, ///!< Coordinates relative to parent coordinate frame
85         LOCAL   ///!< Coordinates relative to local coordinates (parent
86                 ///   coordinates with local transformations applied)
87       };
88
89       /**
90        *
91        */
92       virtual ~Element() = 0;
93
94       virtual void setSelf(const PropertyBasedElementPtr& self);
95       virtual void onDestroy();
96
97       ElementWeakPtr getWeakPtr() const;
98       ElementPtr getParent();
99
100       /**
101        * Called every frame to update internal state
102        *
103        * @param dt  Frame time in seconds
104        */
105       virtual void update(double dt);
106
107       bool addEventListener(const std::string& type, const EventListener& cb);
108       virtual void clearEventListener();
109
110       virtual bool accept(EventVisitor& visitor);
111       virtual bool ascend(EventVisitor& visitor);
112       virtual bool traverse(EventVisitor& visitor);
113
114       virtual bool handleEvent(canvas::EventPtr event);
115
116       /**
117        *
118        * @param global_pos Position in global (canvas) coordinate frame
119        * @param parent_pos Position in parent coordinate frame
120        * @param local_pos  Position in local (element) coordinate frame
121        */
122       virtual bool hitBound( const osg::Vec2f& global_pos,
123                              const osg::Vec2f& parent_pos,
124                              const osg::Vec2f& local_pos ) const;
125
126       /**
127        * Set visibility of the element.
128        */
129       void setVisible(bool visible);
130
131       /**
132        * Get whether the element is visible or hidden.
133        */
134       bool isVisible() const;
135
136       osg::MatrixTransform* getMatrixTransform();
137       osg::MatrixTransform const* getMatrixTransform() const;
138
139       /**
140        * Transform position to local coordinages.
141        */
142       osg::Vec2f posToLocal(const osg::Vec2f& pos) const;
143
144       virtual void childAdded( SGPropertyNode * parent,
145                                SGPropertyNode * child );
146       virtual void childRemoved( SGPropertyNode * parent,
147                                  SGPropertyNode * child );
148       virtual void valueChanged(SGPropertyNode * child);
149
150       virtual bool setStyle( const SGPropertyNode* child,
151                              const StyleInfo* style_info = 0 );
152
153       /**
154        * Set clipping shape
155        *
156        * @note Only "rect(<top>, <right>, <bottom>, <left>)" is supported
157        * @see http://www.w3.org/TR/CSS21/visufx.html#propdef-clip
158        */
159       void setClip(const std::string& clip);
160
161       /**
162        * Clipping coordinates reference frame
163        */
164       void setClipFrame(ReferenceFrame rf);
165
166       /**
167        * Get bounding box (may not be as tight as bounding box returned by
168        * #getTightBounds)
169        */
170       osg::BoundingBox getBoundingBox() const;
171
172       /**
173        * Get tight bounding box (child points are transformed to elements
174        * coordinate space before calculating the bounding box).
175        */
176       osg::BoundingBox getTightBoundingBox() const;
177
178       /**
179        * Get bounding box with children/drawables transformed by passed matrix
180        */
181       virtual osg::BoundingBox getTransformedBounds(const osg::Matrix& m) const;
182
183       /**
184        * Get the transformation matrix (product of all transforms)
185        */
186       osg::Matrix getMatrix() const;
187
188       /**
189        * Create an canvas Element
190        *
191        * @tparam Derived    Type of element (needs to derive from Element)
192        */
193       template<typename Derived>
194       static
195       typename boost::enable_if<
196         boost::is_base_of<Element, Derived>,
197         ElementPtr
198       >::type create( const CanvasWeakPtr& canvas,
199                       const SGPropertyNode_ptr& node,
200                       const Style& style,
201                       Element* parent )
202       {
203         ElementPtr el( new Derived(canvas, node, style, parent) );
204         el->setSelf(el);
205         return el;
206       }
207
208     protected:
209
210       enum Attributes
211       {
212         TRANSFORM       = 1,
213         BLEND_FUNC      = TRANSFORM << 1,
214         SCISSOR_COORDS  = BLEND_FUNC << 1,
215         LAST_ATTRIBUTE  = SCISSOR_COORDS << 1
216       };
217
218       enum TransformType
219       {
220         TT_NONE,
221         TT_MATRIX,
222         TT_TRANSLATE,
223         TT_ROTATE,
224         TT_SCALE
225       };
226
227       class RelativeScissor;
228
229       CanvasWeakPtr _canvas;
230       Element      *_parent;
231
232       mutable uint32_t _attributes_dirty;
233
234       osg::observer_ptr<osg::MatrixTransform> _transform;
235       std::vector<TransformType>              _transform_types;
236
237       Style             _style;
238       RelativeScissor  *_scissor;
239
240       typedef std::vector<EventListener> Listener;
241       typedef std::map<Event::Type, Listener> ListenerMap;
242
243       ListenerMap _listener;
244
245       typedef std::map<std::string, StyleInfo> StyleSetters;
246       static StyleSetters _style_setters;
247
248       static void staticInit();
249
250       Element( const CanvasWeakPtr& canvas,
251                const SGPropertyNode_ptr& node,
252                const Style& parent_style,
253                Element* parent );
254
255       /**
256        * Returns false on first call and true on any successive call. Use to
257        * perform initialization tasks which are only required once per element
258        * type.
259        *
260        * @tparam Derived    (Derived) class type
261        */
262       template<class Derived>
263       static bool isInit()
264       {
265         static bool is_init = false;
266         if( is_init )
267           return true;
268
269         is_init = true;
270         return false;
271       }
272
273       /**
274        * Register a function for setting a style specified by the given property
275        *
276        * @param name    Property name
277        * @param type    Interpolation type
278        * @param setter  Setter function
279        *
280        * @tparam T1         Type of value used to retrieve value from property
281        *                    node
282        * @tparam T2         Type of value the setter function expects
283        * @tparam Derived    Type of class the setter can be applied to
284        *
285        * @note T1 needs to be convertible to T2
286        */
287       template<
288         typename T1,
289         typename T2,
290         class Derived
291       >
292       static
293       StyleSetter
294       addStyle( const std::string& name,
295                 const std::string& type,
296                 const boost::function<void (Derived&, T2)>& setter,
297                 bool inheritable = true )
298       {
299         StyleInfo& style_info = _style_setters[ name ];
300         if( !type.empty() )
301         {
302           if( !style_info.type.empty() && type != style_info.type )
303             SG_LOG
304             (
305               SG_GENERAL,
306               SG_WARN,
307               "changing animation type for '" << name << "': "
308                 << style_info.type << " -> " << type
309             );
310
311           style_info.type = type;
312         }
313         // TODO check if changed?
314         style_info.inheritable = inheritable;
315
316         StyleSetter* style = &style_info.setter;
317         while( style->next )
318           style = style->next;
319         if( style->func )
320           style = style->next = new StyleSetter;
321
322         style->func = boost::bind
323         (
324           &type_match<Derived>::call,
325           _1,
326           _2,
327           bindStyleSetter<T1>(name, setter)
328         );
329         return *style;
330       }
331
332       template<
333         typename T,
334         class Derived
335       >
336       static
337       StyleSetter
338       addStyle( const std::string& name,
339                 const std::string& type,
340                 const boost::function<void (Derived&, T)>& setter,
341                 bool inheritable = true )
342       {
343         return addStyle<T, T>(name, type, setter, inheritable);
344       }
345
346       template<
347         typename T,
348         class Derived
349       >
350       static
351       StyleSetter
352       addStyle( const std::string& name,
353                 const std::string& type,
354                 void (Derived::*setter)(T),
355                 bool inheritable = true )
356       {
357         return addStyle<T, T>
358         (
359           name,
360           type,
361           boost::function<void (Derived&, T)>(setter),
362           inheritable
363         );
364       }
365
366       template<
367         typename T1,
368         typename T2,
369         class Derived
370       >
371       static
372       StyleSetterFunc
373       addStyle( const std::string& name,
374                 const std::string& type,
375                 void (Derived::*setter)(T2),
376                 bool inheritable = true )
377       {
378         return addStyle<T1>
379         (
380           name,
381           type,
382           boost::function<void (Derived&, T2)>(setter),
383           inheritable
384         );
385       }
386
387       template<
388         class Derived
389       >
390       static
391       StyleSetter
392       addStyle( const std::string& name,
393                 const std::string& type,
394                 void (Derived::*setter)(const std::string&),
395                 bool inheritable = true )
396       {
397         return addStyle<const char*, const std::string&>
398         (
399           name,
400           type,
401           boost::function<void (Derived&, const std::string&)>(setter),
402           inheritable
403         );
404       }
405
406       template<
407         typename T,
408         class Derived,
409         class Other,
410         class OtherRef
411       >
412       static
413       StyleSetter
414       addStyle( const std::string& name,
415                 const std::string& type,
416                 void (Other::*setter)(T),
417                 OtherRef Derived::*instance_ref,
418                 bool inheritable = true )
419       {
420         return addStyle<T, T>
421         (
422           name,
423           type,
424           bindOther(setter, instance_ref),
425           inheritable
426         );
427       }
428
429       template<
430         typename T1,
431         typename T2,
432         class Derived,
433         class Other,
434         class OtherRef
435       >
436       static
437       StyleSetter
438       addStyle( const std::string& name,
439                 const std::string& type,
440                 void (Other::*setter)(T2),
441                 OtherRef Derived::*instance_ref,
442                 bool inheritable = true )
443       {
444         return addStyle<T1>
445         (
446           name,
447           type,
448           bindOther(setter, instance_ref),
449           inheritable
450         );
451       }
452
453       template<
454         typename T1,
455         typename T2,
456         class Derived,
457         class Other,
458         class OtherRef
459       >
460       static
461       StyleSetter
462       addStyle( const std::string& name,
463                 const std::string& type,
464                 const boost::function<void (Other&, T2)>& setter,
465                 OtherRef Derived::*instance_ref,
466                 bool inheritable = true )
467       {
468         return addStyle<T1>
469         (
470           name,
471           type,
472           bindOther(setter, instance_ref),
473           inheritable
474         );
475       }
476
477       template<
478         class Derived,
479         class Other,
480         class OtherRef
481       >
482       static
483       StyleSetter
484       addStyle( const std::string& name,
485                 const std::string& type,
486                 void (Other::*setter)(const std::string&),
487                 OtherRef Derived::*instance_ref,
488                 bool inheritable = true )
489       {
490         return addStyle<const char*, const std::string&>
491         (
492           name,
493           type,
494           boost::function<void (Other&, const std::string&)>(setter),
495           instance_ref,
496           inheritable
497         );
498       }
499
500       template<typename T, class Derived, class Other, class OtherRef>
501       static
502       boost::function<void (Derived&, T)>
503       bindOther( void (Other::*setter)(T), OtherRef Derived::*instance_ref )
504       {
505         return boost::bind(setter, boost::bind(instance_ref, _1), _2);
506       }
507
508       template<typename T, class Derived, class Other, class OtherRef>
509       static
510       boost::function<void (Derived&, T)>
511       bindOther( const boost::function<void (Other&, T)>& setter,
512                  OtherRef Derived::*instance_ref )
513       {
514         return boost::bind
515         (
516           setter,
517           boost::bind
518           (
519             &reference_from_pointer<Other, OtherRef>,
520             boost::bind(instance_ref, _1)
521           ),
522           _2
523         );
524       }
525
526       template<typename T1, typename T2, class Derived>
527       static
528       StyleSetterFuncUnchecked
529       bindStyleSetter( const std::string& name,
530                        const boost::function<void (Derived&, T2)>& setter )
531       {
532         return boost::bind
533         (
534           setter,
535           // We will only call setters with Derived instances, so we can safely
536           // cast here.
537           boost::bind(&derived_cast<Derived>, _1),
538           boost::bind(&getValue<T1>, _2)
539         );
540       }
541
542       bool isStyleEmpty(const SGPropertyNode* child) const;
543       bool canApplyStyle(const SGPropertyNode* child) const;
544       bool setStyleImpl( const SGPropertyNode* child,
545                          const StyleInfo* style_info = 0 );
546
547       const StyleInfo* getStyleInfo(const std::string& name) const;
548       const StyleSetter* getStyleSetter(const std::string& name) const;
549       const SGPropertyNode* getParentStyle(const SGPropertyNode* child) const;
550
551       virtual void childAdded(SGPropertyNode * child)  {}
552       virtual void childRemoved(SGPropertyNode * child){}
553       virtual void childChanged(SGPropertyNode * child){}
554
555       void setDrawable(osg::Drawable* drawable);
556
557       /**
558        * Get stateset of drawable if available or use transform otherwise
559        */
560       osg::StateSet* getOrCreateStateSet();
561
562       void setupStyle();
563
564     private:
565
566       osg::ref_ptr<osg::Drawable> _drawable;
567
568       Element(const Element&);// = delete
569
570       template<class Derived>
571       static Derived& derived_cast(Element& el)
572       {
573         return static_cast<Derived&>(el);
574       }
575
576       template<class T, class SharedPtr>
577       static T& reference_from_pointer(const SharedPtr& p)
578       {
579         return *get_pointer(p);
580       }
581
582       /**
583        * Helper to call a function only of the element type can be converted to
584        * the required type.
585        *
586        * @return Whether the function has been called
587        */
588       template<class Derived>
589       struct type_match
590       {
591         static bool call( Element& el,
592                           const SGPropertyNode* prop,
593                           const StyleSetterFuncUnchecked& func )
594         {
595           Derived* d = dynamic_cast<Derived*>(&el);
596           if( !d )
597             return false;
598           func(*d, prop);
599           return true;
600         }
601       };
602   };
603
604 } // namespace canvas
605
606   template<>
607   struct enum_traits<canvas::Element::ReferenceFrame>
608   {
609     static const char* name()
610     {
611       return "canvas::Element::ReferenceFrame";
612     }
613
614     static canvas::Element::ReferenceFrame defVal()
615     {
616       return canvas::Element::GLOBAL;
617     }
618
619     static bool validate(int frame)
620     {
621       return frame >= canvas::Element::GLOBAL
622           && frame <= canvas::Element::LOCAL;
623     }
624   };
625
626 } // namespace simgear
627
628 #endif /* CANVAS_ELEMENT_HXX_ */