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