1 ///@file
2 /// Interface for 2D Canvas element
3 //
4 // Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Library General Public
8 // License as published by the Free Software Foundation; either
9 // version 2 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Library General Public License for more details.
15 //
16 // You should have received a copy of the GNU Library General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
19 
20 #include <simgear_config.h>
21 
22 #include "CanvasElement.hxx"
23 #include <simgear/canvas/Canvas.hxx>
24 #include <simgear/canvas/CanvasEventVisitor.hxx>
25 #include <simgear/canvas/events/MouseEvent.hxx>
26 #include <simgear/math/SGMisc.hxx>
27 #include <simgear/misc/strutils.hxx>
28 #include <simgear/scene/material/parseBlendFunc.hxx>
29 
30 #include <osg/Drawable>
31 #include <osg/Geode>
32 #include <osg/StateAttribute>
33 #include <osg/Version>
34 
35 #include <cassert>
36 #include <cmath>
37 #include <cstring>
38 
39 namespace simgear
40 {
41 namespace canvas
42 {
43   const std::string NAME_TRANSFORM = "tf";
44 
45   /**
46    * glScissor with coordinates relative to different reference frames.
47    */
48   class Element::RelativeScissor:
49     public osg::StateAttribute
50   {
51     public:
52 
53       ReferenceFrame                _coord_reference;
54       osg::observer_ptr<osg::Node>  _node;
55 
RelativeScissor(osg::Node * node=NULL)56       explicit RelativeScissor(osg::Node* node = NULL):
57         _coord_reference(GLOBAL),
58         _node(node),
59         _x(0),
60         _y(0),
61         _width(0),
62         _height(0)
63       {
64 
65       }
66 
67       /** Copy constructor using CopyOp to manage deep vs shallow copy. */
RelativeScissor(const RelativeScissor & vp,const osg::CopyOp & copyop=osg::CopyOp::SHALLOW_COPY)68       RelativeScissor( const RelativeScissor& vp,
69                        const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY ):
70         StateAttribute(vp, copyop),
71         _coord_reference(vp._coord_reference),
72         _node(vp._node),
73         _x(vp._x),
74         _y(vp._y),
75         _width(vp._width),
76         _height(vp._height)
77       {}
78 
79       META_StateAttribute(simgear, RelativeScissor, SCISSOR);
80 
81       /** Return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs. */
compare(const StateAttribute & sa) const82       virtual int compare(const StateAttribute& sa) const
83       {
84         // check the types are equal and then create the rhs variable
85         // used by the COMPARE_StateAttribute_Parameter macros below.
86         COMPARE_StateAttribute_Types(RelativeScissor,sa)
87 
88         // compare each parameter in turn against the rhs.
89         COMPARE_StateAttribute_Parameter(_x)
90         COMPARE_StateAttribute_Parameter(_y)
91         COMPARE_StateAttribute_Parameter(_width)
92         COMPARE_StateAttribute_Parameter(_height)
93         COMPARE_StateAttribute_Parameter(_coord_reference)
94         COMPARE_StateAttribute_Parameter(_node)
95 
96         return 0; // passed all the above comparison macros, must be equal.
97       }
98 
getModeUsage(StateAttribute::ModeUsage & usage) const99       virtual bool getModeUsage(StateAttribute::ModeUsage& usage) const
100       {
101         usage.usesMode(GL_SCISSOR_TEST);
102         return true;
103       }
104 
x()105       inline float& x() { return _x; }
x() const106       inline float x() const { return _x; }
107 
y()108       inline float& y() { return _y; }
y() const109       inline float y() const { return _y; }
110 
width()111       inline float& width() { return _width; }
width() const112       inline float width() const { return _width; }
113 
height()114       inline float& height() { return _height; }
height() const115       inline float height() const { return _height; }
116 
apply(osg::State & state) const117       virtual void apply(osg::State& state) const
118       {
119         if( _width <= 0 || _height <= 0 )
120           return;
121 
122         const osg::Viewport* vp = state.getCurrentViewport();
123         float w2 = 0.5 * vp->width(),
124               h2 = 0.5 * vp->height();
125 
126         osg::Matrix model_view
127         (
128           w2, 0,  0, 0,
129           0,  h2, 0, 0,
130           0,  0,  1, 0,
131           w2, h2, 0, 1
132         );
133         model_view.preMult(state.getProjectionMatrix());
134 
135         if( _coord_reference != GLOBAL )
136         {
137           osg::Node* ref_obj = _node.get();
138 
139           if( _coord_reference == PARENT )
140           {
141             if( _node->getNumParents() < 1 )
142             {
143               SG_LOG(SG_GL, SG_WARN, "RelativeScissor: missing parent.");
144               return;
145             }
146 
147             ref_obj = _node->getParent(0);
148           }
149 
150           osg::MatrixList const& parent_matrices = ref_obj->getWorldMatrices();
151           assert( !parent_matrices.empty() );
152           model_view.preMult(parent_matrices.front());
153         }
154 
155         const osg::Vec2 scale( model_view(0,0), model_view(1,1)),
156                         offset(model_view(3,0), model_view(3,1));
157 
158         // TODO check/warn for rotation?
159         GLint x = SGMiscf::roundToInt(scale.x() * _x + offset.x()),
160               y = SGMiscf::roundToInt(scale.y() * _y + offset.y()),
161               w = SGMiscf::roundToInt(std::fabs(scale.x()) * _width),
162               h = SGMiscf::roundToInt(std::fabs(scale.y()) * _height);
163 
164         if( scale.x() < 0 )
165           x -= w;
166         if( scale.y() < 0 )
167           y -= h;
168 
169         glScissor(x, y, w, h);
170       }
171 
contains(const osg::Vec2f & pos) const172       bool contains(const osg::Vec2f& pos) const
173       {
174         return _x <= pos.x() && pos.x() <= _x + _width
175             && _y <= pos.y() && pos.y() <= _y + _height;
176       }
177 
contains(const osg::Vec2f & global_pos,const osg::Vec2f & parent_pos,const osg::Vec2f & local_pos) const178       bool contains( const osg::Vec2f& global_pos,
179                      const osg::Vec2f& parent_pos,
180                      const osg::Vec2f& local_pos ) const
181       {
182         switch( _coord_reference )
183         {
184           case GLOBAL: return contains(global_pos);
185           case PARENT: return contains(parent_pos);
186           case LOCAL:  return contains(local_pos);
187         }
188 
189         return false;
190       }
191 
192     protected:
193       float _x,
194             _y,
195             _width,
196             _height;
197   };
198 
199   //----------------------------------------------------------------------------
OSGUserData(ElementPtr element)200   Element::OSGUserData::OSGUserData(ElementPtr element):
201     element(element)
202   {
203 
204   }
205 
206   //----------------------------------------------------------------------------
~Element()207   Element::~Element()
208   {
209   }
210 
211   //----------------------------------------------------------------------------
onDestroy()212   void Element::onDestroy()
213   {
214     if( !_scene_group.valid() )
215       return;
216 
217     // The transform node keeps a reference on this element, so ensure it is
218     // deleted.
219     for(osg::Group* parent: _scene_group->getParents())
220     {
221       parent->removeChild(_scene_group.get());
222     }
223 
224     // Hide in case someone still holds a reference
225     setVisible(false);
226     removeListener();
227 
228     _parent = nullptr;
229     _scene_group = nullptr;
230   }
231 
232   //----------------------------------------------------------------------------
getParent() const233   ElementPtr Element::getParent() const
234   {
235     return _parent.lock();
236   }
237 
238   //----------------------------------------------------------------------------
getCanvas() const239   CanvasWeakPtr Element::getCanvas() const
240   {
241     return _canvas;
242   }
243 
244   //----------------------------------------------------------------------------
update(double dt)245   void Element::update(double dt)
246   {
247     if( isVisible() )
248       updateImpl(dt);
249   }
250 
251   //----------------------------------------------------------------------------
addEventListener(const std::string & type_str,const EventListener & cb)252   bool Element::addEventListener( const std::string& type_str,
253                                   const EventListener& cb )
254   {
255     SG_LOG
256     (
257       SG_GENERAL,
258       SG_INFO,
259       "addEventListener(" << _node->getPath() << ", " << type_str << ")"
260     );
261 
262     _listener[ Event::getOrRegisterType(type_str) ].push_back(cb);
263     return true;
264   }
265 
266   //----------------------------------------------------------------------------
clearEventListener()267   void Element::clearEventListener()
268   {
269     _listener.clear();
270   }
271 
272   //----------------------------------------------------------------------------
setFocus()273   void Element::setFocus()
274   {
275     if( auto canvas = _canvas.lock() )
276       canvas->setFocusElement(this);
277   }
278 
279   //----------------------------------------------------------------------------
accept(EventVisitor & visitor)280   bool Element::accept(EventVisitor& visitor)
281   {
282     if( !isVisible() )
283       return false;
284 
285     return visitor.apply(*this);
286   }
287 
288   //----------------------------------------------------------------------------
ascend(EventVisitor & visitor)289   bool Element::ascend(EventVisitor& visitor)
290   {
291     ElementPtr parent = getParent();
292     if( parent )
293       return parent->accept(visitor);
294     return true;
295   }
296 
297   //----------------------------------------------------------------------------
traverse(EventVisitor & visitor)298   bool Element::traverse(EventVisitor& visitor)
299   {
300     return true;
301   }
302 
303   //----------------------------------------------------------------------------
numEventHandler(int type) const304   size_t Element::numEventHandler(int type) const
305   {
306     ListenerMap::const_iterator listeners = _listener.find(type);
307     if( listeners != _listener.end() )
308       return listeners->second.size();
309     return 0;
310   }
311 
312   //----------------------------------------------------------------------------
handleEvent(const EventPtr & event)313   bool Element::handleEvent(const EventPtr& event)
314   {
315     ListenerMap::iterator listeners = _listener.find(event->getType());
316     if( listeners == _listener.end() )
317       return false;
318 
319     for(auto const& listener: listeners->second)
320     {
321       try
322       {
323         listener(event);
324       }
325       catch( std::exception const& ex )
326       {
327         SG_LOG(
328           SG_GENERAL,
329           SG_WARN,
330           "canvas::Element: event handler error: '" << ex.what() << "'"
331         );
332       }
333     }
334 
335     return true;
336   }
337 
338   //----------------------------------------------------------------------------
dispatchEvent(const EventPtr & event)339   bool Element::dispatchEvent(const EventPtr& event)
340   {
341     EventPropagationPath path;
342     path.push_back( EventTarget(this) );
343 
344     for( ElementPtr parent = getParent();
345                     parent.valid();
346                     parent = parent->getParent() )
347       path.push_front( EventTarget(parent) );
348 
349     CanvasPtr canvas = _canvas.lock();
350     if( !canvas )
351       return false;
352 
353     return canvas->propagateEvent(event, path);
354   }
355 
356   //----------------------------------------------------------------------------
hitBound(const osg::Vec2f & global_pos,const osg::Vec2f & parent_pos,const osg::Vec2f & local_pos) const357   bool Element::hitBound( const osg::Vec2f& global_pos,
358                           const osg::Vec2f& parent_pos,
359                           const osg::Vec2f& local_pos ) const
360   {
361     if( _scissor && !_scissor->contains(global_pos, parent_pos, local_pos) )
362       return false;
363 
364     const osg::Vec3f pos3(parent_pos, 0);
365 
366     // Drawables have a bounding box...
367     if( _drawable )
368       return _drawable->
369 #if OSG_VERSION_LESS_THAN(3,3,2)
370       getBound()
371 #else
372       getBoundingBox()
373 #endif
374       .contains(osg::Vec3f(local_pos, 0));
375     else if( _scene_group.valid() )
376       // ... for other elements, i.e. groups only a bounding sphere is available
377       return _scene_group->getBound().contains(osg::Vec3f(parent_pos, 0));
378     else
379       return false;
380   }
381 
382 
383   //----------------------------------------------------------------------------
setVisible(bool visible)384   void Element::setVisible(bool visible)
385   {
386     if( _scene_group.valid() )
387       // TODO check if we need another nodemask
388       _scene_group->setNodeMask(visible ? 0xffffffff : 0);
389   }
390 
391   //----------------------------------------------------------------------------
isVisible() const392   bool Element::isVisible() const
393   {
394     return _scene_group.valid() && _scene_group->getNodeMask() != 0;
395   }
396 
397   //----------------------------------------------------------------------------
getSceneGroup() const398   osg::MatrixTransform* Element::getSceneGroup() const
399   {
400     return _scene_group.get();
401   }
402 
403   //----------------------------------------------------------------------------
posToLocal(const osg::Vec2f & pos) const404   osg::Vec2f Element::posToLocal(const osg::Vec2f& pos) const
405   {
406     if( !_scene_group )
407       // TODO log warning?
408       return pos;
409 
410     updateMatrix();
411     const osg::Matrix& m = _scene_group->getInverseMatrix();
412     return osg::Vec2f
413     (
414       m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0),
415       m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1)
416     );
417   }
418 
419   //----------------------------------------------------------------------------
childAdded(SGPropertyNode * parent,SGPropertyNode * child)420   void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
421   {
422     if(    parent == _node
423         && child->getNameString() == NAME_TRANSFORM )
424     {
425       if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
426         _transform_types.resize( child->getIndex() + 1 );
427 
428       _transform_types[ child->getIndex() ] = TT_NONE;
429       _attributes_dirty |= TRANSFORM;
430       return;
431     }
432     else if(    parent->getParent() == _node
433              && parent->getNameString() == NAME_TRANSFORM )
434     {
435       assert(parent->getIndex() < static_cast<int>(_transform_types.size()));
436 
437       const std::string& name = child->getNameString();
438 
439       TransformType& type = _transform_types[parent->getIndex()];
440 
441       if(      name == "m" )
442         type = TT_MATRIX;
443       else if( name == "t" )
444         type = TT_TRANSLATE;
445       else if( name == "rot" )
446         type = TT_ROTATE;
447       else if( name == "s" )
448         type = TT_SCALE;
449 
450       _attributes_dirty |= TRANSFORM;
451       return;
452     }
453 
454     childAdded(child);
455   }
456 
457   //----------------------------------------------------------------------------
childRemoved(SGPropertyNode * parent,SGPropertyNode * child)458   void Element::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
459   {
460     if( parent == _node )
461     {
462       if( child->getNameString() == NAME_TRANSFORM )
463       {
464         if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
465         {
466           SG_LOG
467           (
468             SG_GENERAL,
469             SG_WARN,
470             "Element::childRemoved: unknown transform: " << child->getPath()
471           );
472           return;
473         }
474 
475         _transform_types[ child->getIndex() ] = TT_NONE;
476 
477         while( !_transform_types.empty() && _transform_types.back() == TT_NONE )
478           _transform_types.pop_back();
479 
480         _attributes_dirty |= TRANSFORM;
481         return;
482       }
483       else if( StyleInfo const* style = getStyleInfo(child->getNameString()) )
484       {
485         if( setStyle(getParentStyle(child), style) )
486           return;
487       }
488     }
489 
490     childRemoved(child);
491   }
492 
493   //----------------------------------------------------------------------------
valueChanged(SGPropertyNode * child)494   void Element::valueChanged(SGPropertyNode* child)
495   {
496     SGPropertyNode *parent = child->getParent();
497     if( parent == _node )
498     {
499       const std::string& name = child->getNameString();
500       if( strutils::starts_with(name, "data-") )
501         return;
502       else if( StyleInfo const* style_info = getStyleInfo(name) )
503       {
504         SGPropertyNode const* style = child;
505         if( isStyleEmpty(child) )
506         {
507           child->clearValue();
508           style = getParentStyle(child);
509         }
510         setStyle(style, style_info);
511         return;
512       }
513       else if( name == "update" )
514         return update(0);
515       else if( strutils::starts_with(name, "blend-") )
516         return (void)(_attributes_dirty |= BLEND_FUNC);
517     }
518     else if(   parent
519             && parent->getParent() == _node
520             && parent->getNameString() == NAME_TRANSFORM )
521     {
522       _attributes_dirty |= TRANSFORM;
523       return;
524     }
525 
526     childChanged(child);
527   }
528 
529   //----------------------------------------------------------------------------
setStyle(const SGPropertyNode * child,const StyleInfo * style_info)530   bool Element::setStyle( const SGPropertyNode* child,
531                           const StyleInfo* style_info )
532   {
533     return canApplyStyle(child) && setStyleImpl(child, style_info);
534   }
535 
536   //----------------------------------------------------------------------------
setClip(const std::string & clip)537   void Element::setClip(const std::string& clip)
538   {
539     if( !_scene_group )
540       // TODO warn?
541       return;
542 
543     osg::StateSet* ss = getOrCreateStateSet();
544     if( !ss )
545       return;
546 
547     if( clip.empty() || clip == "auto" )
548     {
549       ss->removeAttribute(osg::StateAttribute::SCISSOR);
550       _scissor = 0;
551       return;
552     }
553 
554     // TODO generalize CSS property parsing
555     const std::string RECT("rect(");
556     if(    !strutils::ends_with(clip, ")")
557         || !strutils::starts_with(clip, RECT) )
558     {
559       SG_LOG(SG_GENERAL, SG_WARN, "Canvas: invalid clip: " << clip);
560       return;
561     }
562 
563     const std::string sep(", \t\npx");
564     int comp = 0;
565     float values[4];
566 
567     for(size_t pos = RECT.size(); comp < 4; ++comp)
568     {
569       pos = clip.find_first_not_of(sep, pos);
570       if( pos == std::string::npos || pos == clip.size() - 1 )
571         break;
572 
573       char *end = 0;
574       values[comp] = strtod(&clip[pos], &end);
575       if( end == &clip[pos] || !end )
576         break;
577 
578       pos = end - &clip[0];
579     }
580 
581     if( comp < 4 )
582     {
583       SG_LOG(SG_GENERAL, SG_WARN, "Canvas: invalid clip: " << clip);
584       return;
585     }
586 
587     float width = values[1] - values[3],
588           height = values[2] - values[0];
589 
590     if( width < 0 || height < 0 )
591     {
592       SG_LOG(SG_GENERAL, SG_WARN, "Canvas: negative clip size: " << clip);
593       return;
594     }
595 
596     if( !_scissor )
597       _scissor = new RelativeScissor(_scene_group.get());
598 
599     // <top>, <right>, <bottom>, <left>
600     _scissor->x() = values[3];
601     _scissor->y() = values[0];
602     _scissor->width() = width;
603     _scissor->height() = height;
604 
605     SGPropertyNode* clip_frame = _node->getChild("clip-frame", 0);
606     if( clip_frame )
607       valueChanged(clip_frame);
608     else
609       _scissor->_coord_reference = GLOBAL;
610 
611     ss->setAttributeAndModes(_scissor);
612   }
613 
614   //----------------------------------------------------------------------------
setClipFrame(ReferenceFrame rf)615   void Element::setClipFrame(ReferenceFrame rf)
616   {
617     if( _scissor )
618       _scissor->_coord_reference = rf;
619   }
620 
621   //----------------------------------------------------------------------------
setRotation(unsigned int index,double r)622   void Element::setRotation(unsigned int index, double r)
623   {
624     _node->getChild(NAME_TRANSFORM, index, true)->setDoubleValue("rot", r);
625   }
626 
627   //----------------------------------------------------------------------------
setTranslation(unsigned int index,double x,double y)628   void Element::setTranslation(unsigned int index, double x, double y)
629   {
630     SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true);
631     tf->getChild("t", 0, true)->setDoubleValue(x);
632     tf->getChild("t", 1, true)->setDoubleValue(y);
633   }
634 
635   //----------------------------------------------------------------------------
setTransformEnabled(unsigned int index,bool enabled)636   void Element::setTransformEnabled(unsigned int index, bool enabled)
637   {
638     SGPropertyNode* tf = _node->getChild(NAME_TRANSFORM, index, true);
639     tf->setBoolValue("enabled", enabled);
640   }
641 
642   //----------------------------------------------------------------------------
getBoundingBox() const643   osg::BoundingBox Element::getBoundingBox() const
644   {
645     if( _drawable )
646 #if OSG_VERSION_LESS_THAN(3,3,2)
647       return _drawable->getBound();
648 #else
649       return _drawable->getBoundingBox();
650 #endif
651 
652     osg::BoundingBox bb;
653 
654     if( _scene_group.valid() )
655       bb.expandBy( _scene_group->getBound() );
656 
657     return bb;
658   }
659 
660   //----------------------------------------------------------------------------
getTightBoundingBox() const661   osg::BoundingBox Element::getTightBoundingBox() const
662   {
663     return getTransformedBounds(getMatrix());
664   }
665 
666   //----------------------------------------------------------------------------
getTransformedBounds(const osg::Matrix & m) const667   osg::BoundingBox Element::getTransformedBounds(const osg::Matrix& m) const
668   {
669     if( !_drawable )
670       return osg::BoundingBox();
671 
672     osg::BoundingBox transformed;
673     const osg::BoundingBox& bb =
674 #if OSG_VERSION_LESS_THAN(3,3,2)
675       _drawable->getBound();
676 #else
677       _drawable->getBoundingBox();
678 #endif
679 
680     for(int i = 0; i < 4; ++i)
681       transformed.expandBy( bb.corner(i) * m );
682 
683     return transformed;
684   }
685 
686   //----------------------------------------------------------------------------
getMatrix() const687   osg::Matrix Element::getMatrix() const
688   {
689     if( !_scene_group )
690       return osg::Matrix::identity();
691 
692     updateMatrix();
693     return _scene_group->getMatrix();
694   }
695 
696   //----------------------------------------------------------------------------
697   Element::StyleSetters Element::_style_setters;
698 
699   //----------------------------------------------------------------------------
Element(const CanvasWeakPtr & canvas,const SGPropertyNode_ptr & node,const Style & parent_style,ElementWeakPtr parent)700   Element::Element( const CanvasWeakPtr& canvas,
701                     const SGPropertyNode_ptr& node,
702                     const Style& parent_style,
703                     ElementWeakPtr parent ):
704     PropertyBasedElement(node),
705     _canvas( canvas ),
706     _parent( parent ),
707     _scene_group( new osg::MatrixTransform ),
708     _style( parent_style )
709   {
710     staticInit();
711 
712     SG_LOG
713     (
714       SG_GL,
715       SG_DEBUG,
716       "New canvas element " << node->getPath()
717     );
718 
719     // Ensure elements are drawn in order they appear in the element tree
720     _scene_group
721       ->getOrCreateStateSet()
722       ->setRenderBinDetails(
723         0,
724         "PreOrderBin",
725         osg::StateSet::OVERRIDE_RENDERBIN_DETAILS
726       );
727 
728     _scene_group->setUserData( new OSGUserData(this) );
729   }
730 
731   //----------------------------------------------------------------------------
staticInit()732   void Element::staticInit()
733   {
734     if( isInit<Element>() )
735       return;
736 
737     addStyle("clip", "", &Element::setClip, false);
738     addStyle("clip-frame", "", &Element::setClipFrame, false);
739     addStyle("visible", "", &Element::setVisible, false);
740   }
741 
742   //----------------------------------------------------------------------------
isStyleEmpty(const SGPropertyNode * child) const743   bool Element::isStyleEmpty(const SGPropertyNode* child) const
744   {
745     return !child
746         || simgear::strutils::strip(child->getStringValue()).empty();
747   }
748 
749   //----------------------------------------------------------------------------
canApplyStyle(const SGPropertyNode * child) const750   bool Element::canApplyStyle(const SGPropertyNode* child) const
751   {
752     if( _node == child->getParent() )
753       return true;
754 
755     // Parent values do not override if element has own value
756     return isStyleEmpty( _node->getChild(child->getName()) );
757   }
758 
759   //----------------------------------------------------------------------------
setStyleImpl(const SGPropertyNode * child,const StyleInfo * style_info)760   bool Element::setStyleImpl( const SGPropertyNode* child,
761                               const StyleInfo* style_info )
762   {
763     const StyleSetter* style_setter = style_info
764                                     ? &style_info->setter
765                                     : getStyleSetter(child->getNameString());
766     while( style_setter )
767     {
768       if( style_setter->func(*this, child) )
769         return true;
770       style_setter = style_setter->next;
771     }
772     return false;
773   }
774 
775   //----------------------------------------------------------------------------
776   const Element::StyleInfo*
getStyleInfo(const std::string & name) const777   Element::getStyleInfo(const std::string& name) const
778   {
779     StyleSetters::const_iterator setter = _style_setters.find(name);
780     if( setter == _style_setters.end() )
781       return 0;
782 
783     return &setter->second;
784   }
785 
786   //----------------------------------------------------------------------------
787   const Element::StyleSetter*
getStyleSetter(const std::string & name) const788   Element::getStyleSetter(const std::string& name) const
789   {
790     const StyleInfo* info = getStyleInfo(name);
791     return info ? &info->setter : 0;
792   }
793 
794   //----------------------------------------------------------------------------
795   const SGPropertyNode*
getParentStyle(const SGPropertyNode * child) const796   Element::getParentStyle(const SGPropertyNode* child) const
797   {
798     // Try to get value from parent...
799     ElementPtr parent = getParent();
800     if( parent )
801     {
802       Style::const_iterator style =
803         parent->_style.find(child->getNameString());
804       if( style != parent->_style.end() )
805         return style->second;
806     }
807 
808     // ...or reset to default if none is available
809     return child; // TODO somehow get default value for each style?
810   }
811 
812   //----------------------------------------------------------------------------
setDrawable(osg::Drawable * drawable)813   void Element::setDrawable( osg::Drawable* drawable )
814   {
815     _drawable = drawable;
816 
817     if( !_drawable )
818     {
819       SG_LOG(SG_GL, SG_WARN, "canvas::Element::setDrawable: NULL drawable");
820       return;
821     }
822     if( !_scene_group )
823     {
824       SG_LOG(SG_GL, SG_WARN, "canvas::Element::setDrawable: NULL scenegroup");
825       return;
826     }
827 
828     auto geode = new osg::Geode;
829     geode->addDrawable(_drawable);
830     _scene_group->addChild(geode);
831   }
832 
833   //----------------------------------------------------------------------------
getOrCreateStateSet()834   osg::StateSet* Element::getOrCreateStateSet()
835   {
836     if( _drawable.valid() )
837       return _drawable->getOrCreateStateSet();
838     else if( _scene_group.valid() )
839       return _scene_group->getOrCreateStateSet();
840     else
841       return nullptr;
842   }
843 
844   //----------------------------------------------------------------------------
setupStyle()845   void Element::setupStyle()
846   {
847     for(auto const& style: _style)
848       setStyle(style.second);
849   }
850 
851   //----------------------------------------------------------------------------
updateMatrix() const852   void Element::updateMatrix() const
853   {
854     if( !(_attributes_dirty & TRANSFORM) || !_scene_group )
855       return;
856 
857     osg::Matrix m;
858     for( size_t i = 0; i < _transform_types.size(); ++i )
859     {
860       // Skip unused indizes...
861       if( _transform_types[i] == TT_NONE )
862         continue;
863 
864       SGPropertyNode* tf_node = _node->getChild("tf", i, true);
865       if (!tf_node->getBoolValue("enabled", true)) {
866         continue; // skip disabled transforms
867       }
868 
869       // Build up the matrix representation of the current transform node
870       osg::Matrix tf;
871       switch( _transform_types[i] )
872       {
873         case TT_MATRIX:
874           tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1),
875                             tf_node->getDoubleValue("m[1]", 0),
876                             0,
877                             tf_node->getDoubleValue("m[6]", 0),
878 
879                             tf_node->getDoubleValue("m[2]", 0),
880                             tf_node->getDoubleValue("m[3]", 1),
881                             0,
882                             tf_node->getDoubleValue("m[7]", 0),
883 
884                             0,
885                             0,
886                             1,
887                             0,
888 
889                             tf_node->getDoubleValue("m[4]", 0),
890                             tf_node->getDoubleValue("m[5]", 0),
891                             0,
892                             tf_node->getDoubleValue("m[8]", 1) );
893           break;
894         case TT_TRANSLATE:
895           tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0),
896                                         tf_node->getDoubleValue("t[1]", 0),
897                                         0 ) );
898           break;
899         case TT_ROTATE:
900           tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 );
901           break;
902         case TT_SCALE:
903         {
904           float sx = tf_node->getDoubleValue("s[0]", 1);
905           // sy defaults to sx...
906           tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 );
907           break;
908         }
909         default:
910           break;
911       }
912       m.postMult( tf );
913     }
914     _scene_group->setMatrix(m);
915     _attributes_dirty &= ~TRANSFORM;
916   }
917 
918   //----------------------------------------------------------------------------
updateImpl(double dt)919   void Element::updateImpl(double dt)
920   {
921     updateMatrix();
922 
923     // Update bounding box on manual update (manual updates pass zero dt)
924     if( dt == 0 && _drawable )
925       _drawable->getBound();
926 
927     if( (_attributes_dirty & BLEND_FUNC) )
928     {
929       parseBlendFunc(
930         _scene_group->getOrCreateStateSet(),
931         _node->getChild("blend-source"),
932         _node->getChild("blend-destination"),
933         _node->getChild("blend-source-rgb"),
934         _node->getChild("blend-destination-rgb"),
935         _node->getChild("blend-source-alpha"),
936         _node->getChild("blend-destination-alpha")
937       );
938       _attributes_dirty &= ~BLEND_FUNC;
939     }
940   }
941 
942 } // namespace canvas
943 } // namespace simgear
944