]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/elements/CanvasElement.cxx
0ca940d1376ae3667581776fae65f8964ea3ca3a
[simgear.git] / simgear / canvas / elements / CanvasElement.cxx
1 // Interface for 2D Canvas element
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 #include "CanvasElement.hxx"
20 #include <simgear/canvas/Canvas.hxx>
21 #include <simgear/canvas/CanvasEventListener.hxx>
22 #include <simgear/canvas/CanvasEventVisitor.hxx>
23 #include <simgear/canvas/MouseEvent.hxx>
24
25 #include <osg/Drawable>
26 #include <osg/Geode>
27
28 #include <boost/foreach.hpp>
29 #include <boost/make_shared.hpp>
30
31 #include <cassert>
32 #include <cstring>
33
34 namespace simgear
35 {
36 namespace canvas
37 {
38   const std::string NAME_TRANSFORM = "tf";
39
40   //----------------------------------------------------------------------------
41   void Element::removeListener()
42   {
43     _node->removeChangeListener(this);
44   }
45
46   //----------------------------------------------------------------------------
47   Element::~Element()
48   {
49     removeListener();
50
51     BOOST_FOREACH(osg::Group* parent, _transform->getParents())
52     {
53       parent->removeChild(_transform);
54     }
55   }
56
57   //----------------------------------------------------------------------------
58   void Element::update(double dt)
59   {
60     if( !_transform->getNodeMask() )
61       // Don't do anything if element is hidden
62       return;
63
64     if( _transform_dirty )
65     {
66       osg::Matrix m;
67       for( size_t i = 0; i < _transform_types.size(); ++i )
68       {
69         // Skip unused indizes...
70         if( _transform_types[i] == TT_NONE )
71           continue;
72
73         SGPropertyNode* tf_node = _node->getChild("tf", i, true);
74
75         // Build up the matrix representation of the current transform node
76         osg::Matrix tf;
77         switch( _transform_types[i] )
78         {
79           case TT_MATRIX:
80             tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1),
81                               tf_node->getDoubleValue("m[1]", 0), 0, 0,
82
83                               tf_node->getDoubleValue("m[2]", 0),
84                               tf_node->getDoubleValue("m[3]", 1), 0, 0,
85
86                               0,    0,    1, 0,
87                               tf_node->getDoubleValue("m[4]", 0),
88                               tf_node->getDoubleValue("m[5]", 0), 0, 1 );
89             break;
90           case TT_TRANSLATE:
91             tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0),
92                                           tf_node->getDoubleValue("t[1]", 0),
93                                           0 ) );
94             break;
95           case TT_ROTATE:
96             tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 );
97             break;
98           case TT_SCALE:
99           {
100             float sx = tf_node->getDoubleValue("s[0]", 1);
101             // sy defaults to sx...
102             tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 );
103             break;
104           }
105           default:
106             break;
107         }
108         m.postMult( tf );
109       }
110       _transform->setMatrix(m);
111       _transform_dirty = false;
112     }
113   }
114
115   //----------------------------------------------------------------------------
116   naRef Element::addEventListener(const nasal::CallContext& ctx)
117   {
118     const std::string type_str = ctx.requireArg<std::string>(0);
119     naRef code = ctx.requireArg<naRef>(1);
120
121     SG_LOG
122     (
123       SG_NASAL,
124       SG_INFO,
125       "addEventListener(" << _node->getPath() << ", " << type_str << ")"
126     );
127
128     Event::Type type = Event::strToType(type_str);
129     if( type == Event::UNKNOWN )
130       naRuntimeError( ctx.c,
131                       "addEventListener: Unknown event type %s",
132                       type_str.c_str() );
133
134     _listener[ type ].push_back
135     (
136       boost::make_shared<EventListener>( code,
137                                          _canvas.lock()->getSystemAdapter() )
138     );
139
140     return naNil();
141   }
142
143   //----------------------------------------------------------------------------
144   SGConstPropertyNode_ptr Element::getProps() const
145   {
146     return _node;
147   }
148
149   //----------------------------------------------------------------------------
150   SGPropertyNode_ptr Element::getProps()
151   {
152     return  _node;
153   }
154
155   //----------------------------------------------------------------------------
156   bool Element::accept(EventVisitor& visitor)
157   {
158     return visitor.apply(*this);
159   }
160
161   //----------------------------------------------------------------------------
162   bool Element::ascend(EventVisitor& visitor)
163   {
164     if( _parent )
165       return _parent->accept(visitor);
166     return true;
167   }
168
169   //----------------------------------------------------------------------------
170   bool Element::traverse(EventVisitor& visitor)
171   {
172     return true;
173   }
174
175   //----------------------------------------------------------------------------
176   void Element::callListeners(const canvas::EventPtr& event)
177   {
178     ListenerMap::iterator listeners = _listener.find(event->getType());
179     if( listeners == _listener.end() )
180       return;
181
182     BOOST_FOREACH(EventListenerPtr listener, listeners->second)
183       listener->call(event);
184   }
185
186   //----------------------------------------------------------------------------
187   bool Element::hitBound(const osg::Vec2f& pos) const
188   {
189     const osg::Vec3f pos3(pos, 0);
190
191     // Drawables have a bounding box...
192     if( _drawable )
193     {
194       if( !_drawable->getBound().contains(pos3) )
195         return false;
196     }
197     // ... for other elements, i.e. groups only a bounding sphere is available
198     else if( !_transform->getBound().contains(pos3) )
199       return false;
200
201     return true;
202   }
203
204   //----------------------------------------------------------------------------
205   osg::ref_ptr<osg::MatrixTransform> Element::getMatrixTransform()
206   {
207     return _transform;
208   }
209
210   //----------------------------------------------------------------------------
211   void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
212   {
213     if(    parent == _node
214         && child->getNameString() == NAME_TRANSFORM )
215     {
216       if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
217         _transform_types.resize( child->getIndex() + 1 );
218
219       _transform_types[ child->getIndex() ] = TT_NONE;
220       _transform_dirty = true;
221       return;
222     }
223     else if(    parent->getParent() == _node
224              && parent->getNameString() == NAME_TRANSFORM )
225     {
226       assert(parent->getIndex() < static_cast<int>(_transform_types.size()));
227
228       const std::string& name = child->getNameString();
229
230       TransformType& type = _transform_types[parent->getIndex()];
231
232       if(      name == "m" )
233         type = TT_MATRIX;
234       else if( name == "t" )
235         type = TT_TRANSLATE;
236       else if( name == "rot" )
237         type = TT_ROTATE;
238       else if( name == "s" )
239         type = TT_SCALE;
240
241       _transform_dirty = true;
242       return;
243     }
244
245     childAdded(child);
246   }
247
248   //----------------------------------------------------------------------------
249   void Element::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
250   {
251     if( parent == _node && child->getNameString() == NAME_TRANSFORM )
252     {
253       if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
254       {
255         SG_LOG
256         (
257           SG_GENERAL,
258           SG_WARN,
259           "Element::childRemoved: unknown transform: " << child->getPath()
260         );
261         return;
262       }
263
264       _transform_types[ child->getIndex() ] = TT_NONE;
265
266       while( !_transform_types.empty() && _transform_types.back() == TT_NONE )
267         _transform_types.pop_back();
268
269       _transform_dirty = true;
270       return;
271     }
272
273     childRemoved(child);
274   }
275
276   //----------------------------------------------------------------------------
277   void Element::valueChanged(SGPropertyNode* child)
278   {
279     SGPropertyNode *parent = child->getParent();
280     if( parent == _node )
281     {
282       if( setStyle(child) )
283         return;
284       else if( child->getNameString() == "update" )
285         return update(0);
286       else if( child->getNameString() == "visible" )
287         // TODO check if we need another nodemask
288         return _transform->setNodeMask( child->getBoolValue() ? 0xffffffff : 0 );
289     }
290     else if(   parent->getParent() == _node
291             && parent->getNameString() == NAME_TRANSFORM )
292     {
293       _transform_dirty = true;
294       return;
295     }
296
297     childChanged(child);
298   }
299
300   //----------------------------------------------------------------------------
301   void Element::setBoundingBox(const osg::BoundingBox& bb)
302   {
303     if( _bounding_box.empty() )
304     {
305       SGPropertyNode* bb_node = _node->getChild("bounding-box", 0, true);
306       _bounding_box.resize(4);
307       _bounding_box[0] = bb_node->getChild("min-x", 0, true);
308       _bounding_box[1] = bb_node->getChild("min-y", 0, true);
309       _bounding_box[2] = bb_node->getChild("max-x", 0, true);
310       _bounding_box[3] = bb_node->getChild("max-y", 0, true);
311     }
312
313     _bounding_box[0]->setFloatValue(bb._min.x());
314     _bounding_box[1]->setFloatValue(bb._min.y());
315     _bounding_box[2]->setFloatValue(bb._max.x());
316     _bounding_box[3]->setFloatValue(bb._max.y());
317   }
318
319   //----------------------------------------------------------------------------
320   osg::BoundingBox Element::getTransformedBounds(const osg::Matrix& m) const
321   {
322     return osg::BoundingBox();
323   }
324
325   //----------------------------------------------------------------------------
326   Element::Element( const CanvasWeakPtr& canvas,
327                     const SGPropertyNode_ptr& node,
328                     const Style& parent_style,
329                     Element* parent ):
330     _canvas( canvas ),
331     _parent( parent ),
332     _transform_dirty( false ),
333     _transform( new osg::MatrixTransform ),
334     _node( node ),
335     _style( parent_style ),
336     _drawable( 0 )
337   {
338     assert( _node );
339     _node->addChangeListener(this);
340
341     SG_LOG
342     (
343       SG_GL,
344       SG_DEBUG,
345       "New canvas element " << node->getPath()
346     );
347   }
348
349   //----------------------------------------------------------------------------
350   void Element::setDrawable( osg::Drawable* drawable )
351   {
352     _drawable = drawable;
353     assert( _drawable );
354
355     osg::ref_ptr<osg::Geode> geode = new osg::Geode;
356     geode->addDrawable(_drawable);
357     _transform->addChild(geode);
358   }
359
360   //----------------------------------------------------------------------------
361   void Element::setupStyle()
362   {
363     BOOST_FOREACH( Style::value_type style, _style )
364       setStyle(style.second);
365   }
366
367   //----------------------------------------------------------------------------
368   bool Element::setStyle(const SGPropertyNode* child)
369   {
370     StyleSetters::const_iterator setter =
371       _style_setters.find(child->getNameString());
372     if( setter == _style_setters.end() )
373       return false;
374
375     setter->second(child);
376     return true;
377   }
378
379 } // namespace canvas
380 } // namespace simgear