]> git.mxchange.org Git - simgear.git/commitdiff
Refactor Canvas and add some helpers.
authorThomas Geymayer <tomgey@gmail.com>
Sun, 4 Nov 2012 13:12:05 +0000 (14:12 +0100)
committerThomas Geymayer <tomgey@gmail.com>
Sun, 4 Nov 2012 13:12:05 +0000 (14:12 +0100)
 - Refactor Canvas from FlightGear and fix lazy rendering with
   recursive canvases.
 - New classes PropertyBasedElement and PropertyBasedManager for
   creating subsystems controlled by the property tree.
 - New method parseColor to parse CSS 3 conformant color strings
   (only basic color schemes supported)
 - New Rect class representing a rectangle.

41 files changed:
simgear/canvas/CMakeLists.txt
simgear/canvas/Canvas.cxx [new file with mode: 0644]
simgear/canvas/Canvas.hxx [new file with mode: 0644]
simgear/canvas/CanvasMgr.cxx [new file with mode: 0644]
simgear/canvas/CanvasMgr.hxx [new file with mode: 0644]
simgear/canvas/CanvasPlacement.cxx [new file with mode: 0644]
simgear/canvas/CanvasPlacement.hxx [new file with mode: 0644]
simgear/canvas/CanvasSystemAdapter.hxx [new file with mode: 0644]
simgear/canvas/MouseEvent.hxx [new file with mode: 0644]
simgear/canvas/ODGauge.cxx
simgear/canvas/ODGauge.hxx
simgear/canvas/ShivaVG/src/CMakeLists.txt
simgear/canvas/VGInitOperation.hxx
simgear/canvas/canvas_fwd.hxx [new file with mode: 0644]
simgear/canvas/elements/CMakeLists.txt [new file with mode: 0644]
simgear/canvas/elements/CanvasElement.cxx [new file with mode: 0644]
simgear/canvas/elements/CanvasElement.hxx [new file with mode: 0644]
simgear/canvas/elements/CanvasGroup.cxx [new file with mode: 0644]
simgear/canvas/elements/CanvasGroup.hxx [new file with mode: 0644]
simgear/canvas/elements/CanvasImage.cxx [new file with mode: 0644]
simgear/canvas/elements/CanvasImage.hxx [new file with mode: 0644]
simgear/canvas/elements/CanvasMap.cxx [new file with mode: 0644]
simgear/canvas/elements/CanvasMap.hxx [new file with mode: 0644]
simgear/canvas/elements/CanvasPath.cxx [new file with mode: 0644]
simgear/canvas/elements/CanvasPath.hxx [new file with mode: 0644]
simgear/canvas/elements/CanvasText.cxx [new file with mode: 0644]
simgear/canvas/elements/CanvasText.hxx [new file with mode: 0644]
simgear/canvas/elements/map/geo_node_pair.hxx [new file with mode: 0644]
simgear/canvas/elements/map/projection.hxx [new file with mode: 0644]
simgear/canvas/elements/text-alignment.hxx [new file with mode: 0644]
simgear/math/CMakeLists.txt
simgear/math/Rect.hxx [new file with mode: 0644]
simgear/misc/CMakeLists.txt
simgear/misc/parse_color.cxx [new file with mode: 0644]
simgear/misc/parse_color.hxx [new file with mode: 0644]
simgear/misc/parse_color_test.cxx [new file with mode: 0644]
simgear/props/PropertyBasedElement.cxx
simgear/props/PropertyBasedElement.hxx
simgear/props/PropertyBasedMgr.cxx
simgear/props/PropertyBasedMgr.hxx
simgear/props/props.hxx

index 0a10e585ea32899f3ce436e014ebb30a2d85ca92..169c2d6520d8f8ed446ff97437b05bb040aa0c99 100644 (file)
@@ -1,14 +1,25 @@
 include (SimGearComponent)
 
 set(HEADERS
-    ODGauge.hxx
-    VGInitOperation.hxx
-    )
+  canvas_fwd.hxx
+  Canvas.hxx
+  CanvasMgr.hxx
+  CanvasPlacement.hxx
+  CanvasSystemAdapter.hxx
+  MouseEvent.hxx
+  ODGauge.hxx
+  VGInitOperation.hxx
+)
 
 set(SOURCES
-    ODGauge.cxx
-    )
+  Canvas.cxx
+  CanvasMgr.cxx
+  CanvasPlacement.cxx
+  ODGauge.cxx
+)
 
 add_subdirectory(ShivaVG/src)
+include_directories(ShivaVG/include)
 
+add_subdirectory(elements)
 simgear_scene_component(canvas canvas "${SOURCES}" "${HEADERS}")
diff --git a/simgear/canvas/Canvas.cxx b/simgear/canvas/Canvas.cxx
new file mode 100644 (file)
index 0000000..acb8da8
--- /dev/null
@@ -0,0 +1,502 @@
+// The canvas for rendering with the 2d API
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#include "Canvas.hxx"
+#include <simgear/canvas/MouseEvent.hxx>
+#include <simgear/misc/parse_color.hxx>
+#include <simgear/scene/util/RenderConstants.hxx>
+
+#include <osg/Camera>
+#include <osg/Geode>
+#include <osgText/Text>
+#include <osgViewer/Viewer>
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/foreach.hpp>
+#include <iostream>
+
+namespace simgear
+{
+namespace canvas
+{
+
+  //----------------------------------------------------------------------------
+  Canvas::CullCallback::CullCallback(const CanvasWeakPtr& canvas):
+    _canvas( canvas )
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::CullCallback::operator()( osg::Node* node,
+                                         osg::NodeVisitor* nv )
+  {
+    if( (nv->getTraversalMask() & simgear::MODEL_BIT) && !_canvas.expired() )
+      _canvas.lock()->enableRendering();
+
+    traverse(node, nv);
+  }
+
+  //----------------------------------------------------------------------------
+  Canvas::Canvas(SGPropertyNode* node):
+    PropertyBasedElement(node),
+    _canvas_mgr(0),
+    _size_x(-1),
+    _size_y(-1),
+    _view_width(-1),
+    _view_height(-1),
+    _status(node, "status"),
+    _status_msg(node, "status-msg"),
+    _mouse_x(node, "mouse/x"),
+    _mouse_y(node, "mouse/y"),
+    _mouse_dx(node, "mouse/dx"),
+    _mouse_dy(node, "mouse/dy"),
+    _mouse_button(node, "mouse/button"),
+    _mouse_state(node, "mouse/state"),
+    _mouse_mod(node, "mouse/mod"),
+    _mouse_scroll(node, "mouse/scroll"),
+    _mouse_event(node, "mouse/event"),
+    _sampling_dirty(false),
+    _render_dirty(true),
+    _visible(true),
+    _render_always(false)
+  {
+    _status = 0;
+    setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
+  }
+
+  //----------------------------------------------------------------------------
+  Canvas::~Canvas()
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::setSystemAdapter(const SystemAdapterPtr& system_adapter)
+  {
+    _system_adapter = system_adapter;
+    _texture.setSystemAdapter(system_adapter);
+  }
+
+  //----------------------------------------------------------------------------
+  SystemAdapterPtr Canvas::getSystemAdapter() const
+  {
+    return _system_adapter;
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::setCanvasMgr(CanvasMgr* canvas_mgr)
+  {
+    _canvas_mgr = canvas_mgr;
+  }
+
+  //----------------------------------------------------------------------------
+  CanvasMgr* Canvas::getCanvasMgr() const
+  {
+    return _canvas_mgr;
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::addDependentCanvas(const CanvasWeakPtr& canvas)
+  {
+    if( canvas.expired() )
+    {
+      SG_LOG
+      (
+        SG_GENERAL,
+        SG_WARN,
+        "Canvas::addDependentCanvas: got an expired Canvas dependent on "
+        << _node->getPath()
+      );
+      return;
+    }
+
+    _dependent_canvases.insert(canvas);
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::removeDependentCanvas(const CanvasWeakPtr& canvas)
+  {
+    _dependent_canvases.erase(canvas);
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::enableRendering(bool force)
+  {
+    _visible = true;
+    if( force )
+      _render_dirty = true;
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::update(double delta_time_sec)
+  {
+    if( !_texture.serviceable() )
+    {
+      if( _status != STATUS_OK )
+        return;
+
+      _texture.setSize(_size_x, _size_y);
+      _texture.useImageCoords(true);
+      _texture.useStencil(true);
+      _texture.allocRT(/*_camera_callback*/);
+
+      osg::Camera* camera = _texture.getCamera();
+
+      osg::Vec4 clear_color(0.0f, 0.0f, 0.0f , 1.0f);
+      parseColor(_node->getStringValue("background"), clear_color);
+      camera->setClearColor(clear_color);
+
+      camera->addChild(_root_group->getMatrixTransform());
+
+      // Ensure objects are drawn in order of traversal
+      camera->getOrCreateStateSet()->setBinName("TraversalOrderBin");
+
+      if( _texture.serviceable() )
+      {
+        setStatusFlags(STATUS_OK);
+      }
+      else
+      {
+        setStatusFlags(CREATE_FAILED);
+        return;
+      }
+    }
+
+    if( _visible || _render_always )
+    {
+      if( _render_dirty )
+      {
+        // Also mark all dependent (eg. recursively used) canvases as dirty
+        BOOST_FOREACH(CanvasWeakPtr canvas, _dependent_canvases)
+        {
+          if( !canvas.expired() )
+            canvas.lock()->_render_dirty = true;
+        }
+      }
+
+      _texture.setRender(_render_dirty);
+
+      _render_dirty = false;
+      _visible = false;
+    }
+    else
+      _texture.setRender(false);
+
+    _root_group->update(delta_time_sec);
+
+    if( _sampling_dirty )
+    {
+      _texture.setSampling(
+        _node->getBoolValue("mipmapping"),
+        _node->getIntValue("coverage-samples"),
+        _node->getIntValue("color-samples")
+      );
+      _sampling_dirty = false;
+      _render_dirty = true;
+    }
+
+    while( !_dirty_placements.empty() )
+    {
+      SGPropertyNode *node = _dirty_placements.back();
+      _dirty_placements.pop_back();
+
+      if( node->getIndex() >= static_cast<int>(_placements.size()) )
+        // New placement
+        _placements.resize(node->getIndex() + 1);
+      else
+        // Remove possibly existing placements
+        _placements[ node->getIndex() ].clear();
+
+      // Get new placements
+      PlacementFactoryMap::const_iterator placement_factory =
+        _placement_factories.find( node->getStringValue("type", "object") );
+      if( placement_factory != _placement_factories.end() )
+      {
+        Placements& placements = _placements[ node->getIndex() ] =
+          placement_factory->second
+          (
+            node,
+            boost::static_pointer_cast<Canvas>(_self.lock())
+          );
+        node->setStringValue
+        (
+          "status-msg",
+          placements.empty() ? "No match" : "Ok"
+        );
+      }
+      else
+        node->setStringValue("status-msg", "Unknown placement type");
+    }
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::setSizeX(int sx)
+  {
+    if( _size_x == sx )
+      return;
+    _size_x = sx;
+
+    // TODO resize if texture already allocated
+
+    if( _size_x <= 0 )
+      setStatusFlags(MISSING_SIZE_X);
+    else
+      setStatusFlags(MISSING_SIZE_X, false);
+
+    // reset flag to allow creation with new size
+    setStatusFlags(CREATE_FAILED, false);
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::setSizeY(int sy)
+  {
+    if( _size_y == sy )
+      return;
+    _size_y = sy;
+
+    // TODO resize if texture already allocated
+
+    if( _size_y <= 0 )
+      setStatusFlags(MISSING_SIZE_Y);
+    else
+      setStatusFlags(MISSING_SIZE_Y, false);
+
+    // reset flag to allow creation with new size
+    setStatusFlags(CREATE_FAILED, false);
+  }
+
+  //----------------------------------------------------------------------------
+  int Canvas::getSizeX() const
+  {
+    return _size_x;
+  }
+
+  //----------------------------------------------------------------------------
+  int Canvas::getSizeY() const
+  {
+    return _size_y;
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::setViewWidth(int w)
+  {
+    if( _view_width == w )
+      return;
+    _view_width = w;
+
+    _texture.setViewSize(_view_width, _view_height);
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::setViewHeight(int h)
+  {
+    if( _view_height == h )
+      return;
+    _view_height = h;
+
+    _texture.setViewSize(_view_width, _view_height);
+  }
+
+  //----------------------------------------------------------------------------
+  bool Canvas::handleMouseEvent(const MouseEvent& event)
+  {
+    _mouse_x = event.x;
+    _mouse_y = event.y;
+    _mouse_dx = event.dx;
+    _mouse_dy = event.dy;
+    _mouse_button = event.button;
+    _mouse_state = event.state;
+    _mouse_mod = event.mod;
+    _mouse_scroll = event.scroll;
+    // Always set event type last because all listeners are attached to it
+    _mouse_event = event.type;
+
+    if( _root_group.get() )
+      return _root_group->handleMouseEvent(event);
+    else
+      return false;
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::childAdded( SGPropertyNode * parent,
+                           SGPropertyNode * child )
+  {
+    if( parent != _node )
+      return;
+
+    if( child->getNameString() == "placement" )
+      _dirty_placements.push_back(child);
+    else if( _root_group.get() )
+      static_cast<Element*>(_root_group.get())->childAdded(parent, child);
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::childRemoved( SGPropertyNode * parent,
+                             SGPropertyNode * child )
+  {
+    _render_dirty = true;
+
+    if( parent != _node )
+      return;
+
+    if( child->getNameString() == "placement" )
+      _placements[ child->getIndex() ].clear();
+    else if( _root_group.get() )
+      static_cast<Element*>(_root_group.get())->childRemoved(parent, child);
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::valueChanged(SGPropertyNode* node)
+  {
+    if(    boost::starts_with(node->getNameString(), "status")
+        || node->getParent()->getNameString() == "bounding-box" )
+      return;
+    _render_dirty = true;
+
+    bool handled = true;
+    if(    node->getParent()->getParent() == _node
+        && node->getParent()->getNameString() == "placement" )
+    {
+      // prevent double updates...
+      for( size_t i = 0; i < _dirty_placements.size(); ++i )
+      {
+        if( node->getParent() == _dirty_placements[i] )
+          return;
+      }
+
+      _dirty_placements.push_back(node->getParent());
+    }
+    else if( node->getParent() == _node )
+    {
+      if( node->getNameString() == "background" )
+      {
+        osg::Vec4 color;
+        if( _texture.getCamera() && parseColor(node->getStringValue(), color) )
+        {
+          _texture.getCamera()->setClearColor(color);
+          _render_dirty = true;
+        }
+      }
+      else if(    node->getNameString() == "mipmapping"
+              || node->getNameString() == "coverage-samples"
+              || node->getNameString() == "color-samples" )
+      {
+        _sampling_dirty = true;
+      }
+      else if( node->getNameString() == "render-always" )
+      {
+        _render_always = node->getBoolValue();
+      }
+      else if( node->getNameString() == "size" )
+      {
+        if( node->getIndex() == 0 )
+          setSizeX( node->getIntValue() );
+        else if( node->getIndex() == 1 )
+          setSizeY( node->getIntValue() );
+      }
+      else if( node->getNameString() == "view" )
+      {
+        if( node->getIndex() == 0 )
+          setViewWidth( node->getIntValue() );
+        else if( node->getIndex() == 1 )
+          setViewHeight( node->getIntValue() );
+      }
+      else if( node->getNameString() == "freeze" )
+        _texture.setRender( node->getBoolValue() );
+      else
+        handled = false;
+    }
+    else
+      handled = false;
+
+    if( !handled && _root_group.get() )
+      _root_group->valueChanged(node);
+  }
+
+  //----------------------------------------------------------------------------
+  osg::Texture2D* Canvas::getTexture() const
+  {
+    return _texture.getTexture();
+  }
+
+  //----------------------------------------------------------------------------
+  Canvas::CullCallbackPtr Canvas::getCullCallback() const
+  {
+    return _cull_callback;
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::addPlacementFactory( const std::string& type,
+                                    PlacementFactory factory )
+  {
+    if( _placement_factories.find(type) != _placement_factories.end() )
+      SG_LOG
+      (
+        SG_GENERAL,
+        SG_WARN,
+        "Canvas::addPlacementFactory: replace existing factor for type " << type
+      );
+
+    _placement_factories[type] = factory;
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::setSelf(const PropertyBasedElementPtr& self)
+  {
+    PropertyBasedElement::setSelf(self);
+
+    CanvasPtr canvas = boost::static_pointer_cast<Canvas>(self);
+
+    _root_group.reset( new Group(canvas, _node) );
+
+    // Remove automatically created property listener as we forward them on our
+    // own
+    _root_group->removeListener();
+
+    _cull_callback = new CullCallback(canvas);
+  }
+
+  //----------------------------------------------------------------------------
+  void Canvas::setStatusFlags(unsigned int flags, bool set)
+  {
+    if( set )
+      _status = _status | flags;
+    else
+      _status = _status & ~flags;
+    // TODO maybe extend simgear::PropertyObject to allow |=, &= etc.
+
+    if( (_status & MISSING_SIZE_X) && (_status & MISSING_SIZE_Y) )
+      _status_msg = "Missing size";
+    else if( _status & MISSING_SIZE_X )
+      _status_msg = "Missing size-x";
+    else if( _status & MISSING_SIZE_Y )
+      _status_msg = "Missing size-y";
+    else if( _status & CREATE_FAILED )
+      _status_msg = "Creating render target failed";
+    else if( _status == STATUS_OK && !_texture.serviceable() )
+      _status_msg = "Creation pending...";
+    else
+      _status_msg = "Ok";
+  }
+
+  //----------------------------------------------------------------------------
+  Canvas::PlacementFactoryMap Canvas::_placement_factories;
+
+} // namespace canvas
+} // namespace simgear
diff --git a/simgear/canvas/Canvas.hxx b/simgear/canvas/Canvas.hxx
new file mode 100644 (file)
index 0000000..7f0ffb8
--- /dev/null
@@ -0,0 +1,180 @@
+// The canvas for rendering with the 2d API
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef CANVAS_HXX_
+#define CANVAS_HXX_
+
+#include "canvas_fwd.hxx"
+#include "ODGauge.hxx"
+
+#include <simgear/canvas/elements/CanvasGroup.hxx>
+#include <simgear/props/PropertyBasedElement.hxx>
+#include <simgear/props/propertyObject.hxx>
+#include <osg/NodeCallback>
+#include <osg/observer_ptr>
+
+#include <memory>
+#include <string>
+
+namespace simgear
+{
+namespace canvas
+{
+  class CanvasMgr;
+  class MouseEvent;
+
+  class Canvas:
+    public PropertyBasedElement
+  {
+    public:
+
+      enum StatusFlags
+      {
+        STATUS_OK,
+        MISSING_SIZE_X = 0x0001,
+        MISSING_SIZE_Y = 0x0002,
+        CREATE_FAILED  = 0x0004
+      };
+
+      /**
+       * This callback is installed on every placement of the canvas in the
+       * scene to only render the canvas if at least one placement is visible
+       */
+      class CullCallback:
+        public osg::NodeCallback
+      {
+        public:
+          CullCallback(const CanvasWeakPtr& canvas);
+
+        private:
+          CanvasWeakPtr _canvas;
+
+          virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
+      };
+      typedef osg::ref_ptr<CullCallback> CullCallbackPtr;
+
+      Canvas(SGPropertyNode* node);
+      virtual ~Canvas();
+
+      void setSystemAdapter(const SystemAdapterPtr& system_adapter);
+      SystemAdapterPtr getSystemAdapter() const;
+
+      void setCanvasMgr(CanvasMgr* canvas_mgr);
+      CanvasMgr* getCanvasMgr() const;
+
+      /**
+       * Add a canvas which should be mared as dirty upon any change to this
+       * canvas.
+       *
+       * This mechanism is used to eg. redraw a canvas if it's displaying
+       * another canvas (recursive canvases)
+       */
+      void addDependentCanvas(const CanvasWeakPtr& canvas);
+
+      /**
+       * Stop notifying the given canvas upon changes
+       */
+      void removeDependentCanvas(const CanvasWeakPtr& canvas);
+
+      /**
+       * Enable rendering for the next frame
+       *
+       * @param force   Force redraw even if nothing has changed (if dirty flag
+       *                is not set)
+       */
+      void enableRendering(bool force = false);
+
+      void update(double delta_time_sec);
+
+      void setSizeX(int sx);
+      void setSizeY(int sy);
+
+      int getSizeX() const;
+      int getSizeY() const;
+
+      void setViewWidth(int w);
+      void setViewHeight(int h);
+
+      bool handleMouseEvent(const MouseEvent& event);
+
+      virtual void childAdded( SGPropertyNode * parent,
+                               SGPropertyNode * child );
+      virtual void childRemoved( SGPropertyNode * parent,
+                                 SGPropertyNode * child );
+      virtual void valueChanged (SGPropertyNode * node);
+
+      osg::Texture2D* getTexture() const;
+
+      CullCallbackPtr getCullCallback() const;
+
+      static void addPlacementFactory( const std::string& type,
+                                       PlacementFactory factory );
+
+    protected:
+
+      SystemAdapterPtr  _system_adapter;
+      CanvasMgr        *_canvas_mgr;
+
+      int _size_x,
+          _size_y,
+          _view_width,
+          _view_height;
+
+      PropertyObject<int>           _status;
+      PropertyObject<std::string>   _status_msg;
+
+      PropertyObject<int>   _mouse_x, _mouse_y,
+                            _mouse_dx, _mouse_dy,
+                            _mouse_button,
+                            _mouse_state,
+                            _mouse_mod,
+                            _mouse_scroll,
+                            _mouse_event;
+
+      bool _sampling_dirty,
+           _render_dirty,
+           _visible;
+
+      ODGauge _texture;
+      std::auto_ptr<Group> _root_group;
+
+      CullCallbackPtr _cull_callback;
+      bool _render_always; //<! Used to disable automatic lazy rendering (culling)
+
+      std::vector<SGPropertyNode*> _dirty_placements;
+      std::vector<canvas::Placements> _placements;
+      std::set<CanvasWeakPtr> _dependent_canvases; //<! Canvases which use this
+                                                   //   canvas and should be
+                                                   //   notified about changes
+
+      typedef std::map<std::string, canvas::PlacementFactory> PlacementFactoryMap;
+      static PlacementFactoryMap _placement_factories;
+
+      virtual void setSelf(const PropertyBasedElementPtr& self);
+      void setStatusFlags(unsigned int flags, bool set = true);
+
+    private:
+
+      Canvas(const Canvas&); // = delete;
+      Canvas& operator=(const Canvas&); // = delete;
+  };
+
+} // namespace canvas
+} // namespace simgear
+
+#endif /* CANVAS_HXX_ */
diff --git a/simgear/canvas/CanvasMgr.cxx b/simgear/canvas/CanvasMgr.cxx
new file mode 100644 (file)
index 0000000..ff39bd8
--- /dev/null
@@ -0,0 +1,65 @@
+// Canvas with 2D rendering API
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#include "CanvasMgr.hxx"
+#include "Canvas.hxx"
+
+#include <boost/bind.hpp>
+
+namespace simgear
+{
+namespace canvas
+{
+
+  /**
+   * Canvas factory
+   */
+  CanvasPtr canvasFactory(SGPropertyNode* node)
+  {
+    return CanvasPtr(new Canvas(node));
+  }
+
+  //----------------------------------------------------------------------------
+  CanvasMgr::CanvasMgr( SGPropertyNode_ptr node,
+                        SystemAdapterPtr system_adapter ):
+    PropertyBasedMgr(node, "texture", &canvasFactory),
+    _system_adapter(system_adapter)
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  CanvasPtr CanvasMgr::getCanvas(size_t index) const
+  {
+    if(    index >= _elements.size()
+        || !_elements[index] )
+      return CanvasPtr();
+
+    return boost::static_pointer_cast<Canvas>(_elements[index]);
+  }
+
+  //----------------------------------------------------------------------------
+  void CanvasMgr::elementCreated(PropertyBasedElementPtr element)
+  {
+    CanvasPtr canvas = boost::static_pointer_cast<Canvas>(element);
+    canvas->setSystemAdapter(_system_adapter);
+    canvas->setCanvasMgr(this);
+  }
+
+} // namespace canvas
+} // namespace simgear
diff --git a/simgear/canvas/CanvasMgr.hxx b/simgear/canvas/CanvasMgr.hxx
new file mode 100644 (file)
index 0000000..5e51d2d
--- /dev/null
@@ -0,0 +1,61 @@
+// Canvas with 2D rendering API
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef SG_CANVAS_MGR_H_
+#define SG_CANVAS_MGR_H_
+
+#include "canvas_fwd.hxx"
+#include <simgear/props/PropertyBasedMgr.hxx>
+
+namespace simgear
+{
+namespace canvas
+{
+
+  class CanvasMgr:
+    public PropertyBasedMgr
+  {
+    public:
+
+      /**
+       * @param node            Root node of branch used to control canvasses
+       * @param system_adapter  Adapter for connecting between canvas and
+       *                        application framework
+       *
+       */
+      CanvasMgr( SGPropertyNode_ptr node,
+                 SystemAdapterPtr system_adapter );
+
+      /**
+       * Get ::Canvas by index
+       *
+       * @param index Index of texture node in /canvas/by-index/
+       */
+      CanvasPtr getCanvas(size_t index) const;
+
+    protected:
+
+      SystemAdapterPtr _system_adapter;
+
+      virtual void elementCreated(PropertyBasedElementPtr element);
+  };
+
+} // namespace canvas
+} // namespace simgear
+
+#endif /* SG_CANVAS_MGR_H_ */
diff --git a/simgear/canvas/CanvasPlacement.cxx b/simgear/canvas/CanvasPlacement.cxx
new file mode 100644 (file)
index 0000000..3024f30
--- /dev/null
@@ -0,0 +1,39 @@
+// Base class for canvas placements
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#include "CanvasPlacement.hxx"
+
+namespace simgear
+{
+namespace canvas
+{
+
+  //----------------------------------------------------------------------------
+  Placement::Placement()
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  Placement::~Placement()
+  {
+
+  }
+
+} // namespace canvas
+} // namespace simgear
diff --git a/simgear/canvas/CanvasPlacement.hxx b/simgear/canvas/CanvasPlacement.hxx
new file mode 100644 (file)
index 0000000..05d85ba
--- /dev/null
@@ -0,0 +1,41 @@
+// Base class for canvas placements
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef CANVAS_PLACEMENT_HXX_
+#define CANVAS_PLACEMENT_HXX_
+
+namespace simgear
+{
+namespace canvas
+{
+
+  class Placement
+  {
+    public:
+      Placement();
+      virtual ~Placement() = 0;
+
+    private:
+      Placement(const Placement&) /* = delete */;
+      Placement& operator=(const Placement&) /* = delete */;
+  };
+
+} // namespace canvas
+} // namespace simgear
+
+#endif /* CANVAS_PLACEMENT_HXX_ */
diff --git a/simgear/canvas/CanvasSystemAdapter.hxx b/simgear/canvas/CanvasSystemAdapter.hxx
new file mode 100644 (file)
index 0000000..91fc274
--- /dev/null
@@ -0,0 +1,43 @@
+// Adapter for using the canvas with different applications
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef SG_CANVAS_SYSTEM_ADAPTER_HXX_
+#define SG_CANVAS_SYSTEM_ADAPTER_HXX_
+
+#include "canvas_fwd.hxx"
+
+namespace simgear
+{
+namespace canvas
+{
+
+  class SystemAdapter
+  {
+    public:
+
+      virtual ~SystemAdapter() {}
+      virtual FontPtr getFont(const std::string& name) const = 0;
+      virtual void addCamera(osg::Camera* camera) const = 0;
+      virtual void removeCamera(osg::Camera* camera) const = 0;
+      virtual osg::Image* getImage(const std::string& path) const = 0;
+  };
+
+} // namespace canvas
+} // namespace simgear
+
+#endif /* SG_CANVAS_SYSTEM_ADAPTER_HXX_ */
diff --git a/simgear/canvas/MouseEvent.hxx b/simgear/canvas/MouseEvent.hxx
new file mode 100644 (file)
index 0000000..2e6a26d
--- /dev/null
@@ -0,0 +1,60 @@
+// Mouse event
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef CANVAS_MOUSE_EVENT_HXX_
+#define CANVAS_MOUSE_EVENT_HXX_
+
+#include <osgGA/GUIEventAdapter>
+
+namespace simgear
+{
+namespace canvas
+{
+
+  class MouseEvent
+  {
+    public:
+      typedef osgGA::GUIEventAdapter::EventType EventType;
+      typedef osgGA::GUIEventAdapter::ScrollingMotion Scroll;
+
+      MouseEvent(EventType type):
+        type(type),
+        x(-1), y(-1),
+        dx(0), dy(0),
+        button(-1),
+        state(-1),
+        mod(-1),
+        scroll(osgGA::GUIEventAdapter::SCROLL_NONE)
+      {}
+
+      osg::Vec2f getPos() const { return osg::Vec2f(x, y); }
+      osg::Vec3f getPos3() const { return osg::Vec3f(x, y, 0); }
+
+      EventType   type;
+      float       x, y,
+                  dx, dy;
+      int         button, //<! Button for this event
+                  state,  //<! Current button state
+                  mod;    //<! Keyboard modifier state
+      Scroll      scroll;
+  };
+
+} // namespace canvas
+} // namespace simgear
+
+#endif /* CANVAS_MOUSE_EVENT_HXX_ */
index d0c5e2c9556e2d7eef7d01d99aece680be5eaaa9..8b27c06f8727240d91db990a4173f874e1694697 100644 (file)
 // Supports now multisampling/mipmapping, usage of the stencil buffer and placing
 // the texture in the scene by certain filter criteria
 //
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
 //
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-//
+// Library General Public License for more details.
 //
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
 
 #ifdef HAVE_CONFIG_H
 #  include <simgear_config.h>
 #endif
 
+#include "ODGauge.hxx"
+#include "CanvasSystemAdapter.hxx"
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/scene/util/RenderConstants.hxx>
+
 #include <osg/Texture2D>
 #include <osg/AlphaFunc>
 #include <osg/BlendFunc>
 #include <osg/StateSet>
 #include <osg/FrameBufferObject> // for GL_DEPTH_STENCIL_EXT on Windows
 
-#include <simgear/debug/logstream.hxx>
-#include <simgear/scene/util/RenderConstants.hxx>
-#include "ODGauge.hxx"
-
 #include <cassert>
 
 namespace simgear
 {
-
-//------------------------------------------------------------------------------
-ODGauge::ODGauge( const CameraRegistrationCallback& cb_camera_add,
-                  const CameraRegistrationCallback& cb_camera_remove ):
-  _size_x( -1 ),
-  _size_y( -1 ),
-  _view_width( -1 ),
-  _view_height( -1 ),
-  _use_image_coords( false ),
-  _use_stencil( false ),
-  _use_mipmapping( false ),
-  _coverage_samples( 0 ),
-  _color_samples( 0 ),
-  rtAvailable( false ),
-  _cb_cam_add( cb_camera_add ),
-  _cb_cam_remove( cb_camera_remove )
+namespace canvas
 {
 
-}
+  //----------------------------------------------------------------------------
+  ODGauge::ODGauge():
+    _size_x( -1 ),
+    _size_y( -1 ),
+    _view_width( -1 ),
+    _view_height( -1 ),
+    _use_image_coords( false ),
+    _use_stencil( false ),
+    _use_mipmapping( false ),
+    _coverage_samples( 0 ),
+    _color_samples( 0 ),
+    rtAvailable( false )
+  {
 
-//------------------------------------------------------------------------------
-ODGauge::~ODGauge()
-{
-  if( camera.valid() )
-    _cb_cam_remove(camera.get());
-}
+  }
 
-//------------------------------------------------------------------------------
-void ODGauge::setSize(int size_x, int size_y)
-{
-  _size_x = size_x;
-  _size_y = size_y < 0 ? size_x : size_y;
+  //----------------------------------------------------------------------------
+  ODGauge::~ODGauge()
+  {
+    if( camera.valid() && _system_adapter )
+      _system_adapter->removeCamera(camera.get());
+  }
 
-  if( texture.valid() )
-    texture->setTextureSize(_size_x, _size_x);
-}
+  //----------------------------------------------------------------------------
+  void ODGauge::setSystemAdapter(const SystemAdapterPtr& system_adapter)
+  {
+    _system_adapter = system_adapter;
+  }
 
-//----------------------------------------------------------------------------
-void ODGauge::setViewSize(int width, int height)
-{
-  _view_width = width;
-  _view_height = height < 0 ? width : height;
+  //----------------------------------------------------------------------------
+  void ODGauge::setSize(int size_x, int size_y)
+  {
+    _size_x = size_x;
+    _size_y = size_y < 0 ? size_x : size_y;
 
-  if( camera )
-    updateCoordinateFrame();
-}
+    if( texture.valid() )
+      texture->setTextureSize(_size_x, _size_x);
+  }
 
-//------------------------------------------------------------------------------
-void ODGauge::useImageCoords(bool use)
-{
-  if( use == _use_image_coords )
-    return;
+  //----------------------------------------------------------------------------
+  void ODGauge::setViewSize(int width, int height)
+  {
+    _view_width = width;
+    _view_height = height < 0 ? width : height;
 
-  _use_image_coords = use;
+    if( camera )
+      updateCoordinateFrame();
+  }
 
-  if( texture )
-    updateCoordinateFrame();
-}
+  //----------------------------------------------------------------------------
+  void ODGauge::useImageCoords(bool use)
+  {
+    if( use == _use_image_coords )
+      return;
 
-//------------------------------------------------------------------------------
-void ODGauge::useStencil(bool use)
-{
-  if( use == _use_stencil )
-    return;
+    _use_image_coords = use;
 
-  _use_stencil = use;
+    if( texture )
+      updateCoordinateFrame();
+  }
 
-  if( texture )
-    updateStencil();
-}
+  //----------------------------------------------------------------------------
+  void ODGauge::useStencil(bool use)
+  {
+    if( use == _use_stencil )
+      return;
 
-//------------------------------------------------------------------------------
-void ODGauge::setSampling( bool mipmapping,
-                             int coverage_samples,
-                             int color_samples )
-{
-  if(    _use_mipmapping == mipmapping
-      && _coverage_samples == coverage_samples
-      && _color_samples == color_samples )
-    return;
+    _use_stencil = use;
 
-  _use_mipmapping = mipmapping;
+    if( texture )
+      updateStencil();
+  }
 
-  if( color_samples > coverage_samples )
+  //----------------------------------------------------------------------------
+  void ODGauge::setSampling( bool mipmapping,
+                               int coverage_samples,
+                               int color_samples )
   {
-    SG_LOG
-    (
-      SG_GL,
-      SG_WARN,
-      "ODGauge::setSampling: color_samples > coverage_samples not allowed!"
-    );
-    color_samples = coverage_samples;
+    if(    _use_mipmapping == mipmapping
+        && _coverage_samples == coverage_samples
+        && _color_samples == color_samples )
+      return;
+
+    _use_mipmapping = mipmapping;
+
+    if( color_samples > coverage_samples )
+    {
+      SG_LOG
+      (
+        SG_GL,
+        SG_WARN,
+        "ODGauge::setSampling: color_samples > coverage_samples not allowed!"
+      );
+      color_samples = coverage_samples;
+    }
+
+    _coverage_samples = coverage_samples;
+    _color_samples = color_samples;
+
+    updateSampling();
   }
 
-  _coverage_samples = coverage_samples;
-  _color_samples = color_samples;
-
-  updateSampling();
-}
-
-//------------------------------------------------------------------------------
-void ODGauge::setRender(bool render)
-{
-  // Only the far camera should trigger this texture to be rendered.
-  camera->setNodeMask(render ? simgear::BACKGROUND_BIT : 0);
-}
-
-//------------------------------------------------------------------------------
-bool ODGauge::serviceable(void)
-{
-  return rtAvailable;
-}
-
-//------------------------------------------------------------------------------
-void ODGauge::allocRT(osg::NodeCallback* camera_cull_callback)
-{
-  camera = new osg::Camera;
-  camera->setDataVariance(osg::Object::DYNAMIC);
-  camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
-  camera->setRenderOrder(osg::Camera::PRE_RENDER);
-  camera->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f , 0.0f));
-  camera->setClearStencil(0);
-  camera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT,
-                                             osg::Camera::FRAME_BUFFER );
-
-  if( camera_cull_callback )
-    camera->setCullCallback(camera_cull_callback);
-
-  setRender(true);
-  updateCoordinateFrame();
-  updateStencil();
-
-  osg::StateSet* stateSet = camera->getOrCreateStateSet();
-  stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
-  stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
-  stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
-  stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
-  stateSet->setAttributeAndModes(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK,
-          osg::PolygonMode::FILL),
-          osg::StateAttribute::ON);
-  stateSet->setAttributeAndModes(new osg::AlphaFunc(osg::AlphaFunc::GREATER,
-          0.0f),
-          osg::StateAttribute::ON);
-  stateSet->setAttribute(new osg::ShadeModel(osg::ShadeModel::FLAT));
-  stateSet->setAttributeAndModes(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA,
-          osg::BlendFunc::ONE_MINUS_SRC_ALPHA),
-          osg::StateAttribute::ON);
-  if( !texture )
+  //----------------------------------------------------------------------------
+  void ODGauge::setRender(bool render)
   {
-    texture = new osg::Texture2D;
-    texture->setTextureSize(_size_x, _size_y);
-    texture->setInternalFormat(GL_RGBA);
+    // Only the far camera should trigger this texture to be rendered.
+    camera->setNodeMask(render ? simgear::BACKGROUND_BIT : 0);
   }
 
-  updateSampling();
-
-  _cb_cam_add(camera.get());
-  rtAvailable = true;
-}
-
-//------------------------------------------------------------------------------
-void ODGauge::updateCoordinateFrame()
-{
-  assert( camera );
-
-  if( _view_width < 0 )
-    _view_width = _size_x;
-  if( _view_height < 0 )
-    _view_height = _size_y;
-
-  camera->setViewport(0, 0, _size_x, _size_y);
-
-  if( _use_image_coords )
-    camera->setProjectionMatrix(
-      osg::Matrix::ortho2D(0, _view_width, _view_height, 0)
-    );
-  else
-    camera->setProjectionMatrix(
-      osg::Matrix::ortho2D( -_view_width/2.,  _view_width/2.,
-                            -_view_height/2., _view_height/2. )
-    );
-}
+  //----------------------------------------------------------------------------
+  bool ODGauge::serviceable(void)
+  {
+    return rtAvailable;
+  }
 
-//------------------------------------------------------------------------------
-void ODGauge::updateStencil()
-{
-  assert( camera );
+  //----------------------------------------------------------------------------
+  void ODGauge::allocRT(osg::NodeCallback* camera_cull_callback)
+  {
+    camera = new osg::Camera;
+    camera->setDataVariance(osg::Object::DYNAMIC);
+    camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
+    camera->setRenderOrder(osg::Camera::PRE_RENDER);
+    camera->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f , 0.0f));
+    camera->setClearStencil(0);
+    camera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT,
+                                               osg::Camera::FRAME_BUFFER );
+
+    if( camera_cull_callback )
+      camera->setCullCallback(camera_cull_callback);
+
+    setRender(true);
+    updateCoordinateFrame();
+    updateStencil();
 
-  GLbitfield mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
+    osg::StateSet* stateSet = camera->getOrCreateStateSet();
+    stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
+    stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
+    stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
+    stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
+    stateSet->setAttributeAndModes(
+      new osg::PolygonMode( osg::PolygonMode::FRONT_AND_BACK,
+                            osg::PolygonMode::FILL ),
+      osg::StateAttribute::ON );
+    stateSet->setAttributeAndModes(
+      new osg::AlphaFunc(osg::AlphaFunc::GREATER, 0.0f),
+      osg::StateAttribute::ON );
+    stateSet->setAttribute(new osg::ShadeModel(osg::ShadeModel::FLAT));
+    stateSet->setAttributeAndModes(
+      new osg::BlendFunc( osg::BlendFunc::SRC_ALPHA,
+                          osg::BlendFunc::ONE_MINUS_SRC_ALPHA),
+      osg::StateAttribute::ON );
+    if( !texture )
+    {
+      texture = new osg::Texture2D;
+      texture->setTextureSize(_size_x, _size_y);
+      texture->setInternalFormat(GL_RGBA);
+    }
+
+    updateSampling();
+
+    if( _system_adapter )
+      _system_adapter->addCamera(camera.get());
+
+    rtAvailable = true;
+  }
 
-  if( _use_stencil)
+  //----------------------------------------------------------------------------
+  void ODGauge::updateCoordinateFrame()
   {
-    camera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER,
-                     GL_DEPTH_STENCIL_EXT );
-    mask |= GL_STENCIL_BUFFER_BIT;
+    assert( camera );
+
+    if( _view_width < 0 )
+      _view_width = _size_x;
+    if( _view_height < 0 )
+      _view_height = _size_y;
+
+    camera->setViewport(0, 0, _size_x, _size_y);
+
+    if( _use_image_coords )
+      camera->setProjectionMatrix(
+        osg::Matrix::ortho2D(0, _view_width, _view_height, 0)
+      );
+    else
+      camera->setProjectionMatrix(
+        osg::Matrix::ortho2D( -_view_width/2.,  _view_width/2.,
+                              -_view_height/2., _view_height/2. )
+      );
   }
-  else
+
+  //----------------------------------------------------------------------------
+  void ODGauge::updateStencil()
   {
-    camera->detach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER);
+    assert( camera );
+
+    GLbitfield mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
+
+    if( _use_stencil)
+    {
+      camera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER,
+                       GL_DEPTH_STENCIL_EXT );
+      mask |= GL_STENCIL_BUFFER_BIT;
+    }
+    else
+    {
+      camera->detach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER);
+    }
+
+    camera->setClearMask(mask);
   }
 
-  camera->setClearMask(mask);
-}
+  //----------------------------------------------------------------------------
+  void ODGauge::updateSampling()
+  {
+    assert( camera );
+    assert( texture );
 
-//------------------------------------------------------------------------------
-void ODGauge::updateSampling()
-{
-  assert( camera );
-  assert( texture );
-
-  texture->setFilter(
-    osg::Texture2D::MIN_FILTER,
-    _use_mipmapping ? osg::Texture2D::LINEAR_MIPMAP_LINEAR
-                    : osg::Texture2D::LINEAR
-  );
-  camera->attach(
-    osg::Camera::COLOR_BUFFER,
-    texture.get(),
-    0, 0,
-    _use_mipmapping,
-    _coverage_samples,
-    _color_samples
-  );
-}
+    texture->setFilter(
+      osg::Texture2D::MIN_FILTER,
+      _use_mipmapping ? osg::Texture2D::LINEAR_MIPMAP_LINEAR
+                      : osg::Texture2D::LINEAR
+    );
+    camera->attach(
+      osg::Camera::COLOR_BUFFER,
+      texture.get(),
+      0, 0,
+      _use_mipmapping,
+      _coverage_samples,
+      _color_samples
+    );
+  }
 
+} // namespace canvas
 } // namespace simgear
index 64efa84e936ce46335989d2befab1b7047501cb4..5a07d69e2e53c59c498794e7f88e992e6861f0d8 100644 (file)
 // Supports now multisampling/mipmapping, usage of the stencil buffer and placing
 // the texture in the scene by certain filter criteria
 //
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
 //
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-//
+// Library General Public License for more details.
 //
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
 
 #ifndef _SG_OD_GAUGE_HXX
 #define _SG_OD_GAUGE_HXX
 
+#include "canvas_fwd.hxx"
+
 #include <osg/NodeCallback>
 #include <osg/Group>
 
-#include <boost/function.hpp>
-
 namespace osg
 {
   class Camera;
@@ -42,6 +40,9 @@ namespace osg
 
 namespace simgear
 {
+namespace canvas
+{
+
   /**
    * Owner Drawn Gauge (aka render-to-texture) helper class
    */
@@ -49,12 +50,11 @@ namespace simgear
   {
     public:
 
-      typedef boost::function<void(osg::Camera*)> CameraRegistrationCallback;
-
-      ODGauge( const CameraRegistrationCallback& cb_camera_add,
-               const CameraRegistrationCallback& cb_camera_remove );
+      ODGauge();
       virtual ~ODGauge();
 
+      void setSystemAdapter(const SystemAdapterPtr& system_adapter);
+
       /**
        * Set the size of the render target.
        *
@@ -124,7 +124,9 @@ namespace simgear
       // Real initialization function. Bad name.
       void allocRT(osg::NodeCallback* camera_cull_callback = 0);
 
-    private:
+    protected:
+
+      SystemAdapterPtr _system_adapter;
 
       int _size_x,
           _size_y,
@@ -142,15 +144,13 @@ namespace simgear
       osg::ref_ptr<osg::Camera> camera;
       osg::ref_ptr<osg::Texture2D> texture;
 
-      CameraRegistrationCallback _cb_cam_add,
-                                 _cb_cam_remove;
-
       void updateCoordinateFrame();
       void updateStencil();
       void updateSampling();
 
   };
 
+} // namespace canvas
 } // namespace simgear
 
 #endif // _SG_OD_GAUGE_HXX
index 63dcdef78820879ada63f52e07752f65ba36816a..7b9bc78500afdaaee388244ee10cdf237c8c9710 100644 (file)
@@ -45,4 +45,8 @@ TARGET_LINK_LIBRARIES(
     ShivaVG
     ${OPENGL_gl_LIBRARY}
     ${OPENGL_glu_LIBRARY}
-)
\ No newline at end of file
+)
+
+if(NOT SIMGEAR_HEADLESS)
+  install(TARGETS ShivaVG ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+endif()
\ No newline at end of file
index 9fb650cbb4538504a4ca4866be612fc429ae074e..b18ace17e3be14db4f136bb2f75c503dc659976b 100644 (file)
@@ -12,9 +12,9 @@
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 // Library General Public License for more details.
 //
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
 
 #ifndef CANVAS_VG_INITOPERATION_HXX_
 #define CANVAS_VG_INITOPERATION_HXX_
diff --git a/simgear/canvas/canvas_fwd.hxx b/simgear/canvas/canvas_fwd.hxx
new file mode 100644 (file)
index 0000000..2588460
--- /dev/null
@@ -0,0 +1,58 @@
+// Canvas forward declarations
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef SG_CANVAS_FWD_HXX_
+#define SG_CANVAS_FWD_HXX_
+
+#include <simgear/props/propsfwd.hxx>
+#include <simgear/structure/SGWeakPtr.hxx>
+
+#include <osg/ref_ptr>
+#include <osgText/Font>
+
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+
+#include <vector>
+
+namespace simgear
+{
+namespace canvas
+{
+
+  class Canvas;
+  typedef boost::shared_ptr<Canvas> CanvasPtr;
+  typedef boost::weak_ptr<Canvas> CanvasWeakPtr;
+
+  typedef osg::ref_ptr<osgText::Font> FontPtr;
+
+  class Placement;
+  typedef boost::shared_ptr<Placement> PlacementPtr;
+  typedef std::vector<PlacementPtr> Placements;
+  typedef boost::function<Placements( const SGPropertyNode*,
+                                      CanvasPtr )> PlacementFactory;
+
+  class SystemAdapter;
+  typedef boost::shared_ptr<SystemAdapter> SystemAdapterPtr;
+
+} // namespace canvas
+} // namespace simgear
+
+
+#endif /* SG_CANVAS_FWD_HXX_ */
diff --git a/simgear/canvas/elements/CMakeLists.txt b/simgear/canvas/elements/CMakeLists.txt
new file mode 100644 (file)
index 0000000..6e5c2e9
--- /dev/null
@@ -0,0 +1,21 @@
+include (SimGearComponent)
+
+set(HEADERS
+  CanvasElement.hxx
+  CanvasGroup.hxx
+  CanvasImage.hxx
+  CanvasMap.hxx
+  CanvasPath.hxx
+  CanvasText.hxx  
+)
+
+set(SOURCES
+  CanvasElement.cxx
+  CanvasGroup.cxx
+  CanvasImage.cxx
+  CanvasMap.cxx
+  CanvasPath.cxx
+  CanvasText.cxx
+)
+
+simgear_scene_component(canvas-elements canvas/elements "${SOURCES}" "${HEADERS}")
\ No newline at end of file
diff --git a/simgear/canvas/elements/CanvasElement.cxx b/simgear/canvas/elements/CanvasElement.cxx
new file mode 100644 (file)
index 0000000..e81cc20
--- /dev/null
@@ -0,0 +1,304 @@
+// Interface for 2D Canvas element
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#include "CanvasElement.hxx"
+#include <simgear/canvas/MouseEvent.hxx>
+
+#include <osg/Drawable>
+#include <osg/Geode>
+
+#include <boost/foreach.hpp>
+
+#include <cassert>
+#include <cstring>
+
+namespace simgear
+{
+namespace canvas
+{
+  const std::string NAME_TRANSFORM = "tf";
+
+  //----------------------------------------------------------------------------
+  void Element::removeListener()
+  {
+    _node->removeChangeListener(this);
+  }
+
+  //----------------------------------------------------------------------------
+  Element::~Element()
+  {
+    removeListener();
+
+    BOOST_FOREACH(osg::Group* parent, _transform->getParents())
+    {
+      parent->removeChild(_transform);
+    }
+  }
+
+  //----------------------------------------------------------------------------
+  void Element::update(double dt)
+  {
+    if( !_transform->getNodeMask() )
+      // Don't do anything if element is hidden
+      return;
+
+    if( _transform_dirty )
+    {
+      osg::Matrix m;
+      for( size_t i = 0; i < _transform_types.size(); ++i )
+      {
+        // Skip unused indizes...
+        if( _transform_types[i] == TT_NONE )
+          continue;
+
+        SGPropertyNode* tf_node = _node->getChild("tf", i, true);
+
+        // Build up the matrix representation of the current transform node
+        osg::Matrix tf;
+        switch( _transform_types[i] )
+        {
+          case TT_MATRIX:
+            tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1),
+                              tf_node->getDoubleValue("m[1]", 0), 0, 0,
+
+                              tf_node->getDoubleValue("m[2]", 0),
+                              tf_node->getDoubleValue("m[3]", 1), 0, 0,
+
+                              0,    0,    1, 0,
+                              tf_node->getDoubleValue("m[4]", 0),
+                              tf_node->getDoubleValue("m[5]", 0), 0, 1 );
+            break;
+          case TT_TRANSLATE:
+            tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0),
+                                          tf_node->getDoubleValue("t[1]", 0),
+                                          0 ) );
+            break;
+          case TT_ROTATE:
+            tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 );
+            break;
+          case TT_SCALE:
+          {
+            float sx = tf_node->getDoubleValue("s[0]", 1);
+            // sy defaults to sx...
+            tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 );
+            break;
+          }
+          default:
+            break;
+        }
+        m.postMult( tf );
+      }
+      _transform->setMatrix(m);
+      _transform_dirty = false;
+    }
+  }
+
+  //----------------------------------------------------------------------------
+  bool Element::handleMouseEvent(const MouseEvent& event)
+  {
+    // Transform event to local coordinates
+    const osg::Matrixd& m = _transform->getInverseMatrix();
+    MouseEvent local_event = event;
+    local_event.x = m(0, 0) * event.x + m(1, 0) * event.y + m(3, 0);
+    local_event.y = m(0, 1) * event.x + m(1, 1) * event.y + m(3, 1);
+
+    // Drawables have a bounding box...
+    if( _drawable )
+    {
+      if( !_drawable->getBound().contains(local_event.getPos3()) )
+        return false;
+    }
+    // ... for other elements, i.e. groups only a bounding sphere is available
+    else if( !_transform->getBound().contains(local_event.getPos3()) )
+      return false;
+
+    local_event.dx = m(0, 0) * event.dx + m(1, 0) * event.dy;
+    local_event.dy = m(0, 1) * event.dx + m(1, 1) * event.dy;
+
+    return handleLocalMouseEvent(local_event);
+  }
+
+  //----------------------------------------------------------------------------
+  osg::ref_ptr<osg::MatrixTransform> Element::getMatrixTransform()
+  {
+    return _transform;
+  }
+
+  //----------------------------------------------------------------------------
+  void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
+  {
+    if(    parent == _node
+        && child->getNameString() == NAME_TRANSFORM )
+    {
+      if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
+        _transform_types.resize( child->getIndex() + 1 );
+
+      _transform_types[ child->getIndex() ] = TT_NONE;
+      _transform_dirty = true;
+      return;
+    }
+    else if(    parent->getParent() == _node
+             && parent->getNameString() == NAME_TRANSFORM )
+    {
+      assert(parent->getIndex() < static_cast<int>(_transform_types.size()));
+
+      const std::string& name = child->getNameString();
+
+      TransformType& type = _transform_types[parent->getIndex()];
+
+      if(      name == "m" )
+        type = TT_MATRIX;
+      else if( name == "t" )
+        type = TT_TRANSLATE;
+      else if( name == "rot" )
+        type = TT_ROTATE;
+      else if( name == "s" )
+        type = TT_SCALE;
+
+      _transform_dirty = true;
+      return;
+    }
+
+    childAdded(child);
+  }
+
+  //----------------------------------------------------------------------------
+  void Element::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
+  {
+    if( parent == _node && child->getNameString() == NAME_TRANSFORM )
+    {
+      assert(child->getIndex() < static_cast<int>(_transform_types.size()));
+      _transform_types[ child->getIndex() ] = TT_NONE;
+
+      while( !_transform_types.empty() && _transform_types.back() == TT_NONE )
+        _transform_types.pop_back();
+
+      _transform_dirty = true;
+      return;
+    }
+
+    childRemoved(child);
+  }
+
+  //----------------------------------------------------------------------------
+  void Element::valueChanged(SGPropertyNode* child)
+  {
+    SGPropertyNode *parent = child->getParent();
+    if( parent == _node )
+    {
+      if( setStyle(child) )
+        return;
+      else if( child->getNameString() == "update" )
+        return update(0);
+      else if( child->getNameString() == "visible" )
+        // TODO check if we need another nodemask
+        return _transform->setNodeMask( child->getBoolValue() ? 0xffffffff : 0 );
+    }
+    else if(   parent->getParent() == _node
+            && parent->getNameString() == NAME_TRANSFORM )
+    {
+      _transform_dirty = true;
+      return;
+    }
+
+    childChanged(child);
+  }
+
+  //----------------------------------------------------------------------------
+  void Element::setBoundingBox(const osg::BoundingBox& bb)
+  {
+    if( _bounding_box.empty() )
+    {
+      SGPropertyNode* bb_node = _node->getChild("bounding-box", 0, true);
+      _bounding_box.resize(4);
+      _bounding_box[0] = bb_node->getChild("min-x", 0, true);
+      _bounding_box[1] = bb_node->getChild("min-y", 0, true);
+      _bounding_box[2] = bb_node->getChild("max-x", 0, true);
+      _bounding_box[3] = bb_node->getChild("max-y", 0, true);
+    }
+
+    _bounding_box[0]->setFloatValue(bb._min.x());
+    _bounding_box[1]->setFloatValue(bb._min.y());
+    _bounding_box[2]->setFloatValue(bb._max.x());
+    _bounding_box[3]->setFloatValue(bb._max.y());
+  }
+
+  //----------------------------------------------------------------------------
+  Element::Element( const CanvasWeakPtr& canvas,
+                    SGPropertyNode_ptr node,
+                    const Style& parent_style ):
+    _canvas( canvas ),
+    _transform_dirty( false ),
+    _transform( new osg::MatrixTransform ),
+    _node( node ),
+    _style( parent_style ),
+    _drawable( 0 )
+  {
+    assert( _node );
+    _node->addChangeListener(this);
+
+    SG_LOG
+    (
+      SG_GL,
+      SG_DEBUG,
+      "New canvas element " << node->getPath()
+    );
+  }
+
+  //----------------------------------------------------------------------------
+  bool Element::handleLocalMouseEvent(const MouseEvent& event)
+  {
+//    std::cout << _node->getPath()
+//              << " local: pos=(" << event.x << "|" << event.y << ") "
+//              <<         "d=(" << event.dx << "|" << event.dx << ")"
+//              << std::endl;
+    return true;
+  }
+
+  //----------------------------------------------------------------------------
+  void Element::setDrawable( osg::Drawable* drawable )
+  {
+    _drawable = drawable;
+    assert( _drawable );
+
+    osg::ref_ptr<osg::Geode> geode = new osg::Geode;
+    geode->addDrawable(_drawable);
+    _transform->addChild(geode);
+  }
+
+  //----------------------------------------------------------------------------
+  void Element::setupStyle()
+  {
+    BOOST_FOREACH( Style::value_type style, _style )
+      setStyle(style.second);
+  }
+
+  //----------------------------------------------------------------------------
+  bool Element::setStyle(const SGPropertyNode* child)
+  {
+    StyleSetters::const_iterator setter =
+      _style_setters.find(child->getNameString());
+    if( setter == _style_setters.end() )
+      return false;
+
+    setter->second(child);
+    return true;
+  }
+
+} // namespace canvas
+} // namespace simgear
diff --git a/simgear/canvas/elements/CanvasElement.hxx b/simgear/canvas/elements/CanvasElement.hxx
new file mode 100644 (file)
index 0000000..a079cd6
--- /dev/null
@@ -0,0 +1,178 @@
+// Interface for 2D Canvas elements
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef CANVAS_ELEMENT_HXX_
+#define CANVAS_ELEMENT_HXX_
+
+#include <simgear/canvas/canvas_fwd.hxx>
+#include <simgear/props/props.hxx>
+#include <simgear/misc/stdint.hxx> // for uint32_t
+#include <osg/BoundingBox>
+#include <osg/MatrixTransform>
+
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+
+namespace osg
+{
+  class Drawable;
+}
+
+namespace simgear
+{
+namespace canvas
+{
+
+  class MouseEvent;
+  class Element:
+    public SGPropertyChangeListener
+  {
+    public:
+      typedef std::map<std::string, const SGPropertyNode*> Style;
+      typedef boost::function<void(const SGPropertyNode*)> StyleSetter;
+      typedef std::map<std::string, StyleSetter> StyleSetters;
+
+      /**
+       * Remove the property listener of the element.
+       *
+       * You will need to call the appropriate methods (#childAdded,
+       * #childRemoved, #valueChanged) yourself to ensure the element still
+       * receives the needed events.
+       */
+      void removeListener();
+
+      /**
+       *
+       */
+      virtual ~Element() = 0;
+
+      /**
+       * Called every frame to update internal state
+       *
+       * @param dt  Frame time in seconds
+       */
+      virtual void update(double dt);
+
+      /**
+       * Handle mouse event (transforms coordinates to local coordinate frame
+       * and forwards event to #handleLocalMouseEvent)
+       */
+      virtual bool handleMouseEvent(const canvas::MouseEvent& event);
+
+      osg::ref_ptr<osg::MatrixTransform> getMatrixTransform();
+
+      virtual void childAdded( SGPropertyNode * parent,
+                               SGPropertyNode * child );
+      virtual void childRemoved( SGPropertyNode * parent,
+                                 SGPropertyNode * child );
+      virtual void valueChanged(SGPropertyNode * child);
+
+      /**
+       * Write the given bounding box to the property tree
+       */
+      void setBoundingBox(const osg::BoundingBox& bb);
+
+    protected:
+
+      enum Attributes
+      {
+        LAST_ATTRIBUTE  = 0x0001
+      };
+
+      enum TransformType
+      {
+        TT_NONE,
+        TT_MATRIX,
+        TT_TRANSLATE,
+        TT_ROTATE,
+        TT_SCALE
+      };
+
+      CanvasWeakPtr _canvas;
+      uint32_t _attributes_dirty;
+
+      bool _transform_dirty;
+      osg::ref_ptr<osg::MatrixTransform>    _transform;
+      std::vector<TransformType>            _transform_types;
+
+      SGPropertyNode_ptr                _node;
+      Style                             _style;
+      StyleSetters                      _style_setters;
+      std::vector<SGPropertyNode_ptr>   _bounding_box;
+
+      Element( const CanvasWeakPtr& canvas,
+               SGPropertyNode_ptr node,
+               const Style& parent_style );
+
+      template<typename T, class C1, class C2>
+      Element::StyleSetter
+      addStyle(const std::string& name, void (C1::*setter)(T), C2 instance)
+      {
+        return _style_setters[ name ] =
+                 bindStyleSetter<T>(name, setter, instance);
+      }
+
+      template<typename T1, typename T2, class C1, class C2>
+      Element::StyleSetter
+      addStyle(const std::string& name, void (C1::*setter)(T2), C2 instance)
+      {
+        return _style_setters[ name ] =
+                 bindStyleSetter<T1>(name, setter, instance);
+      }
+
+      template<class C1, class C2>
+      Element::StyleSetter
+      addStyle( const std::string& name,
+                void (C1::*setter)(const std::string&),
+                C2 instance )
+      {
+        return _style_setters[ name ] =
+                 bindStyleSetter<const char*>(name, setter, instance);
+      }
+
+      template<typename T1, typename T2, class C1, class C2>
+      Element::StyleSetter
+      bindStyleSetter( const std::string& name,
+                       void (C1::*setter)(T2),
+                       C2 instance )
+      {
+        return boost::bind(setter, instance, boost::bind(&getValue<T1>, _1));
+      }
+
+      virtual bool handleLocalMouseEvent(const canvas::MouseEvent& event);
+
+      virtual void childAdded(SGPropertyNode * child)  {}
+      virtual void childRemoved(SGPropertyNode * child){}
+      virtual void childChanged(SGPropertyNode * child){}
+
+      void setDrawable(osg::Drawable* drawable);
+
+      void setupStyle();
+      bool setStyle(const SGPropertyNode* child);
+
+    private:
+
+      osg::ref_ptr<osg::Drawable> _drawable;
+
+      Element(const Element&);// = delete
+  };
+
+} // namespace canvas
+} // namespace simgear
+
+#endif /* CANVAS_ELEMENT_HXX_ */
diff --git a/simgear/canvas/elements/CanvasGroup.cxx b/simgear/canvas/elements/CanvasGroup.cxx
new file mode 100644 (file)
index 0000000..ff4be60
--- /dev/null
@@ -0,0 +1,219 @@
+// A group of 2D Canvas elements
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#include "CanvasGroup.hxx"
+#include "CanvasImage.hxx"
+#include "CanvasMap.hxx"
+#include "CanvasPath.hxx"
+#include "CanvasText.hxx"
+
+#include <boost/foreach.hpp>
+
+namespace simgear
+{
+namespace canvas
+{
+
+  //----------------------------------------------------------------------------
+  Group::Group( const CanvasWeakPtr& canvas,
+                SGPropertyNode_ptr node,
+                const Style& parent_style ):
+    Element(canvas, node, parent_style)
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  Group::~Group()
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  void Group::update(double dt)
+  {
+    BOOST_FOREACH( ChildList::value_type child, _children )
+      child.second->update(dt);
+
+    Element::update(dt);
+  }
+
+  //----------------------------------------------------------------------------
+  bool Group::handleLocalMouseEvent(const canvas::MouseEvent& event)
+  {
+    // Iterate in reverse order as last child is displayed on top
+    BOOST_REVERSE_FOREACH( ChildList::value_type child, _children )
+    {
+      if( child.second->handleMouseEvent(event) )
+        return true;
+    }
+    return false;
+  }
+
+  //----------------------------------------------------------------------------
+  void Group::childAdded(SGPropertyNode* child)
+  {
+    if( child->getParent() != _node )
+      return;
+
+    boost::shared_ptr<Element> element;
+
+    // TODO create map of child factories and use also to check for element
+    //      on deletion in ::childRemoved
+    if( child->getNameString() == "text" )
+      element.reset( new Text(_canvas, child, _style) );
+    else if( child->getNameString() == "group" )
+      element.reset( new Group(_canvas, child, _style) );
+    else if( child->getNameString() == "map" )
+      element.reset( new Map(_canvas, child, _style) );
+    else if( child->getNameString() == "path" )
+      element.reset( new Path(_canvas, child, _style) );
+    else if( child->getNameString() == "image" )
+      element.reset( new Image(_canvas, child, _style) );
+
+    if( element )
+    {
+      // Add to osg scene graph...
+      _transform->addChild( element->getMatrixTransform() );
+      _children.push_back( ChildList::value_type(child, element) );
+      return;
+    }
+
+    _style[ child->getNameString() ] = child;
+  }
+
+  //----------------------------------------------------------------------------
+  struct ChildFinder
+  {
+    public:
+      ChildFinder(SGPropertyNode *node):
+        _node(node)
+      {}
+
+      bool operator()(const Group::ChildList::value_type& el) const
+      {
+        return el.first == _node;
+      }
+
+    private:
+      SGPropertyNode *_node;
+  };
+
+  //----------------------------------------------------------------------------
+  void Group::childRemoved(SGPropertyNode* node)
+  {
+    if( node->getParent() != _node )
+      return;
+
+    if(    node->getNameString() == "text"
+        || node->getNameString() == "group"
+        || node->getNameString() == "map"
+        || node->getNameString() == "path"
+        || node->getNameString() == "image" )
+    {
+      ChildFinder pred(node);
+      ChildList::iterator child =
+        std::find_if(_children.begin(), _children.end(), pred);
+
+      if( child == _children.end() )
+        SG_LOG
+        (
+          SG_GL,
+          SG_WARN,
+          "can't removed unknown child " << node->getDisplayName()
+        );
+      else
+      {
+        _transform->removeChild( child->second->getMatrixTransform() );
+        _children.erase(child);
+      }
+    }
+    else
+    {
+      Style::iterator style = _style.find(node->getNameString());
+      if( style != _style.end() )
+        _style.erase(style);
+    }
+  }
+
+  //----------------------------------------------------------------------------
+  void Group::childChanged(SGPropertyNode* node)
+  {
+    if(    node->getParent()->getParent() == _node
+        && node->getNameString() == "z-index" )
+      return handleZIndexChanged(node->getParent(), node->getIntValue());
+  }
+
+  //----------------------------------------------------------------------------
+  void Group::handleZIndexChanged(SGPropertyNode* node, int z_index)
+  {
+    ChildFinder pred(node);
+    ChildList::iterator child =
+      std::find_if(_children.begin(), _children.end(), pred);
+
+    if( child == _children.end() )
+      return;
+
+    osg::Node* tf = child->second->getMatrixTransform();
+    int index = _transform->getChildIndex(tf),
+        index_new = index;
+
+    ChildList::iterator next = child;
+    ++next;
+
+    while(    next != _children.end()
+           && next->first->getIntValue("z-index", 0) <= z_index )
+    {
+      ++index_new;
+      ++next;
+    }
+
+    if( index_new != index )
+    {
+      _children.insert(next, *child);
+    }
+    else
+    {
+      ChildList::iterator prev = child;
+      while(    prev != _children.begin()
+             && (--prev)->first->getIntValue("z-index", 0) > z_index)
+      {
+        --index_new;
+      }
+
+      if( index == index_new )
+        return;
+
+      _children.insert(prev, *child);
+    }
+
+    _transform->removeChild(index);
+    _transform->insertChild(index_new, tf);
+
+    _children.erase(child);
+
+    SG_LOG
+    (
+      SG_GENERAL,
+      SG_INFO,
+      "canvas::Group: Moved element " << index << " to position " << index_new
+    );
+  }
+
+} // namespace canvas
+} // namespace simgear
diff --git a/simgear/canvas/elements/CanvasGroup.hxx b/simgear/canvas/elements/CanvasGroup.hxx
new file mode 100644 (file)
index 0000000..d57cea7
--- /dev/null
@@ -0,0 +1,67 @@
+// A group of 2D Canvas elements
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef CANVAS_GROUP_HXX_
+#define CANVAS_GROUP_HXX_
+
+#include "CanvasElement.hxx"
+
+#include <boost/shared_ptr.hpp>
+#include <list>
+#include <map>
+
+namespace simgear
+{
+namespace canvas
+{
+
+  typedef boost::shared_ptr<Element> ElementPtr;
+
+  class Group:
+    public Element
+  {
+    public:
+      typedef std::list< std::pair< const SGPropertyNode*,
+                                    ElementPtr
+                                  >
+                       > ChildList;
+
+      Group( const CanvasWeakPtr& canvas,
+             SGPropertyNode_ptr node,
+             const Style& parent_style = Style() );
+      virtual ~Group();
+
+      virtual void update(double dt);
+
+    protected:
+
+      ChildList _children;
+
+      virtual bool handleLocalMouseEvent(const canvas::MouseEvent& event);
+
+      virtual void childAdded(SGPropertyNode * child);
+      virtual void childRemoved(SGPropertyNode * child);
+      virtual void childChanged(SGPropertyNode * child);
+
+      void handleZIndexChanged(SGPropertyNode* node, int z_index);
+  };
+
+} // namespace canvas
+} // namespace simgear
+
+#endif /* CANVAS_GROUP_HXX_ */
diff --git a/simgear/canvas/elements/CanvasImage.cxx b/simgear/canvas/elements/CanvasImage.cxx
new file mode 100644 (file)
index 0000000..811c6c1
--- /dev/null
@@ -0,0 +1,352 @@
+// An image on the Canvas
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#include "CanvasImage.hxx"
+
+#include <simgear/canvas/Canvas.hxx>
+#include <simgear/canvas/CanvasMgr.hxx>
+#include <simgear/canvas/CanvasSystemAdapter.hxx>
+#include <simgear/misc/parse_color.hxx>
+#include <simgear/misc/sg_path.hxx>
+
+#include <osg/Array>
+#include <osg/Geometry>
+#include <osg/PrimitiveSet>
+
+#include <boost/algorithm/string/predicate.hpp>
+
+namespace simgear
+{
+namespace canvas
+{
+  /**
+   * Callback to enable/disable rendering of canvas displayed inside windows or
+   * other canvases.
+   */
+  class CullCallback:
+    public osg::Drawable::CullCallback
+  {
+    public:
+      CullCallback(const CanvasWeakPtr& canvas);
+
+    private:
+      CanvasWeakPtr _canvas;
+
+      virtual bool cull( osg::NodeVisitor* nv,
+                         osg::Drawable* drawable,
+                         osg::RenderInfo* renderInfo ) const;
+  };
+
+  //----------------------------------------------------------------------------
+  CullCallback::CullCallback(const CanvasWeakPtr& canvas):
+    _canvas( canvas )
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  bool CullCallback::cull( osg::NodeVisitor* nv,
+                           osg::Drawable* drawable,
+                           osg::RenderInfo* renderInfo ) const
+  {
+    if( !_canvas.expired() )
+      _canvas.lock()->enableRendering();
+
+    // TODO check if window/image should be culled
+    return false;
+  }
+
+  //----------------------------------------------------------------------------
+  Image::Image( const CanvasWeakPtr& canvas,
+                SGPropertyNode_ptr node,
+                const Style& parent_style ):
+    Element(canvas, node, parent_style),
+    _texture(new osg::Texture2D),
+    _node_src_rect( node->getNode("source", 0, true) )
+  {
+    _geom = new osg::Geometry;
+    _geom->setUseDisplayList(false);
+
+    osg::StateSet *stateSet = _geom->getOrCreateStateSet();
+    stateSet->setTextureAttributeAndModes(0, _texture.get());
+    stateSet->setDataVariance(osg::Object::STATIC);
+
+    // allocate arrays for the image
+    _vertices = new osg::Vec3Array(4);
+    _vertices->setDataVariance(osg::Object::STATIC);
+    _geom->setVertexArray(_vertices);
+
+    _texCoords = new osg::Vec2Array(4);
+    _texCoords->setDataVariance(osg::Object::STATIC);
+    _geom->setTexCoordArray(0, _texCoords);
+
+    _colors = new osg::Vec4Array(4);
+    _colors->setDataVariance(osg::Object::STATIC);
+    _geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
+    _geom->setColorArray(_colors);
+
+    osg::DrawArrays* prim = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
+    prim->set(osg::PrimitiveSet::QUADS, 0, 4);
+    prim->setDataVariance(osg::Object::STATIC);
+    _geom->addPrimitiveSet(prim);
+
+    setDrawable(_geom);
+
+    addStyle("fill", &Image::setFill, this);
+    setFill("#ffffff"); // TODO how should we handle default values?
+
+    setupStyle();
+  }
+
+  //----------------------------------------------------------------------------
+  Image::~Image()
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  void Image::update(double dt)
+  {
+    Element::update(dt);
+
+    if( _attributes_dirty & DEST_SIZE )
+    {
+      (*_vertices)[0].set(_region.l(), _region.t(), 0);
+      (*_vertices)[1].set(_region.r(), _region.t(), 0);
+      (*_vertices)[2].set(_region.r(), _region.b(), 0);
+      (*_vertices)[3].set(_region.l(), _region.b(), 0);
+      _vertices->dirty();
+
+      _attributes_dirty &= ~DEST_SIZE;
+      _geom->dirtyBound();
+      setBoundingBox(_geom->getBound());
+    }
+
+    if( _attributes_dirty & SRC_RECT )
+    {
+      double u0 = _src_rect.l(),
+             u1 = _src_rect.r(),
+             v0 = _src_rect.b(),
+             v1 = _src_rect.t();
+
+      if( !_node_src_rect->getBoolValue("normalized", true) )
+      {
+        const Rect<int>& tex_dim = getTextureDimensions();
+
+        u0 /= tex_dim.width();
+        u1 /= tex_dim.width();
+        v0 /= tex_dim.height();
+        v1 /= tex_dim.height();
+      }
+
+      (*_texCoords)[0].set(u0, v0);
+      (*_texCoords)[1].set(u1, v0);
+      (*_texCoords)[2].set(u1, v1);
+      (*_texCoords)[3].set(u0, v1);
+      _texCoords->dirty();
+
+      _attributes_dirty &= ~SRC_RECT;
+    }
+  }
+
+  //----------------------------------------------------------------------------
+  void Image::setSrcCanvas(CanvasPtr canvas)
+  {
+    if( !_src_canvas.expired() )
+      _src_canvas.lock()->removeDependentCanvas(_canvas);
+
+    _src_canvas = canvas;
+    _geom->getOrCreateStateSet()
+         ->setTextureAttribute(0, canvas ? canvas->getTexture() : 0);
+    _geom->setCullCallback(canvas ? new CullCallback(canvas) : 0);
+
+    if( !_src_canvas.expired() )
+    {
+      setupDefaultDimensions();
+      _src_canvas.lock()->addDependentCanvas(_canvas);
+    }
+  }
+
+  //----------------------------------------------------------------------------
+  CanvasWeakPtr Image::getSrcCanvas() const
+  {
+    return _src_canvas;
+  }
+
+  //----------------------------------------------------------------------------
+  void Image::setImage(osg::Image *img)
+  {
+    // remove canvas...
+    setSrcCanvas( CanvasPtr() );
+
+    _texture->setImage(img);
+    _geom->getOrCreateStateSet()
+         ->setTextureAttributeAndModes(0, _texture);
+
+    if( img )
+      setupDefaultDimensions();
+  }
+
+  //----------------------------------------------------------------------------
+  void Image::setFill(const std::string& fill)
+  {
+    osg::Vec4 color;
+    if( !parseColor(fill, color) )
+      return;
+
+    for( int i = 0; i < 4; ++i )
+      (*_colors)[i] = color;
+    _colors->dirty();
+  }
+
+  //----------------------------------------------------------------------------
+  const Rect<float>& Image::getRegion() const
+  {
+    return _region;
+  }
+
+  //----------------------------------------------------------------------------
+  void Image::childChanged(SGPropertyNode* child)
+  {
+    const std::string& name = child->getNameString();
+
+    if( child->getParent() == _node_src_rect )
+    {
+      _attributes_dirty |= SRC_RECT;
+
+      if(      name == "left" )
+        _src_rect.setLeft( child->getFloatValue() );
+      else if( name == "right" )
+        _src_rect.setRight( child->getFloatValue() );
+      else if( name == "top" )
+        _src_rect.setTop( child->getFloatValue() );
+      else if( name == "bottom" )
+        _src_rect.setBottom( child->getFloatValue() );
+
+      return;
+    }
+    else if( child->getParent() != _node )
+      return;
+
+    if( name == "x" )
+    {
+      _region.setX( child->getFloatValue() );
+      _attributes_dirty |= DEST_SIZE;
+    }
+    else if( name == "y" )
+    {
+      _region.setY( child->getFloatValue() );
+      _attributes_dirty |= DEST_SIZE;
+    }
+    else if( name == "size" )
+    {
+      if( child->getIndex() == 0 )
+        _region.setWidth( child->getFloatValue() );
+      else
+        _region.setHeight( child->getFloatValue() );
+
+      _attributes_dirty |= DEST_SIZE;
+    }
+    else if( name == "file" )
+    {
+      static const std::string CANVAS_PROTOCOL = "canvas://";
+      const std::string& path = child->getStringValue();
+
+      CanvasPtr canvas = _canvas.lock();
+      if( !canvas )
+      {
+        SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No canvas available");
+        return;
+      }
+
+      if( boost::starts_with(path, CANVAS_PROTOCOL) )
+      {
+        CanvasMgr* canvas_mgr = canvas->getCanvasMgr();
+        if( !canvas_mgr )
+        {
+          SG_LOG(SG_GL, SG_ALERT, "canvas::Image: Failed to get CanvasMgr");
+          return;
+        }
+
+        const SGPropertyNode* canvas_node =
+          canvas_mgr->getPropertyRoot()
+                    ->getParent()
+                    ->getNode( path.substr(CANVAS_PROTOCOL.size()) );
+        if( !canvas_node )
+        {
+          SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No such canvas: " << path);
+          return;
+        }
+
+        // TODO add support for other means of addressing canvases (eg. by
+        // name)
+        CanvasPtr src_canvas = canvas_mgr->getCanvas( canvas_node->getIndex() );
+        if( !src_canvas )
+        {
+          SG_LOG(SG_GL, SG_ALERT, "canvas::Image: Invalid canvas: " << path);
+          return;
+        }
+
+        setSrcCanvas(src_canvas);
+      }
+      else
+      {
+        setImage( canvas->getSystemAdapter()->getImage(path) );
+      }
+    }
+  }
+
+  //----------------------------------------------------------------------------
+  void Image::setupDefaultDimensions()
+  {
+    if( !_src_rect.width() || !_src_rect.height() )
+    {
+      const Rect<int>& tex_dim = getTextureDimensions();
+
+      _node_src_rect->setBoolValue("normalized", false);
+      _node_src_rect->setFloatValue("right", tex_dim.width());
+      _node_src_rect->setFloatValue("bottom", tex_dim.height());
+    }
+
+    if( !_region.width() || !_region.height() )
+    {
+      _node->setFloatValue("size[0]", _src_rect.width());
+      _node->setFloatValue("size[1]", _src_rect.height());
+    }
+  }
+
+  //----------------------------------------------------------------------------
+  Rect<int> Image::getTextureDimensions() const
+  {
+    osg::Texture2D *texture = !_src_canvas.expired()
+                            ? _src_canvas.lock()->getTexture()
+                            : _texture.get();
+
+    if( !texture )
+      return Rect<int>();
+
+    return Rect<int>
+    (
+      0,0,
+      texture->getTextureWidth(),
+      texture->getTextureHeight()
+    );
+  }
+
+} // namespace canvas
+} // namespace simgear
diff --git a/simgear/canvas/elements/CanvasImage.hxx b/simgear/canvas/elements/CanvasImage.hxx
new file mode 100644 (file)
index 0000000..700c475
--- /dev/null
@@ -0,0 +1,89 @@
+// An image on the Canvas
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef CANVAS_IMAGE_HXX_
+#define CANVAS_IMAGE_HXX_
+
+#include "CanvasElement.hxx"
+
+#include <simgear/canvas/canvas_fwd.hxx>
+#include <simgear/math/Rect.hxx>
+#include <osg/Texture2D>
+
+namespace simgear
+{
+namespace canvas
+{
+
+  class Image:
+    public Element
+  {
+    public:
+      /**
+       * @param node    Property node containing settings for this image:
+       *                  rect/[left/right/top/bottom]  Dimensions of source
+       *                                                rect
+       *                  size[0-1]                     Dimensions of rectangle
+       *                  [x,y]                         Position of rectangle
+       */
+      Image( const CanvasWeakPtr& canvas,
+             SGPropertyNode_ptr node,
+             const Style& parent_style );
+      virtual ~Image();
+
+      virtual void update(double dt);
+
+      void setSrcCanvas(CanvasPtr canvas);
+      CanvasWeakPtr getSrcCanvas() const;
+
+      void setImage(osg::Image *img);
+      void setFill(const std::string& fill);
+
+      const Rect<float>& getRegion() const;
+
+    protected:
+
+      enum ImageAttributes
+      {
+        SRC_RECT       = LAST_ATTRIBUTE << 1, // Source image rectangle
+        DEST_SIZE      = SRC_RECT << 1        // Element size
+      };
+
+      virtual void childChanged(SGPropertyNode * child);
+
+      void setupDefaultDimensions();
+      Rect<int> getTextureDimensions() const;
+
+      osg::ref_ptr<osg::Texture2D> _texture;
+      // TODO optionally forward events to canvas
+      CanvasWeakPtr _src_canvas;
+
+      osg::ref_ptr<osg::Geometry>  _geom;
+      osg::ref_ptr<osg::Vec3Array> _vertices;
+      osg::ref_ptr<osg::Vec2Array> _texCoords;
+      osg::ref_ptr<osg::Vec4Array> _colors;
+
+      SGPropertyNode *_node_src_rect;
+      Rect<float>     _src_rect,
+                      _region;
+  };
+
+} // namespace canvas
+} // namespace canvas
+
+#endif /* CANVAS_IMAGE_HXX_ */
diff --git a/simgear/canvas/elements/CanvasMap.cxx b/simgear/canvas/elements/CanvasMap.cxx
new file mode 100644 (file)
index 0000000..1145e1d
--- /dev/null
@@ -0,0 +1,230 @@
+// A group of 2D Canvas elements which get automatically transformed according
+// to the map parameters.
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#include "CanvasMap.hxx"
+#include "map/geo_node_pair.hxx"
+#include "map/projection.hxx"
+
+#include <cmath>
+
+#include <boost/algorithm/string/predicate.hpp>
+
+#define LOG_GEO_RET(msg) \
+  {\
+    SG_LOG\
+    (\
+      SG_GENERAL,\
+      SG_WARN,\
+      msg << " (" << child->getStringValue()\
+                  << ", " << child->getPath() << ")"\
+    );\
+    return;\
+  }
+
+namespace simgear
+{
+namespace canvas
+{
+
+  const std::string GEO = "-geo";
+
+  //----------------------------------------------------------------------------
+  Map::Map( const CanvasWeakPtr& canvas,
+            SGPropertyNode_ptr node,
+            const Style& parent_style ):
+    Group(canvas, node, parent_style),
+    // TODO make projection configurable
+    _projection(new SansonFlamsteedProjection),
+    _projection_dirty(true)
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  Map::~Map()
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  void Map::update(double dt)
+  {
+    for( GeoNodes::iterator it = _geo_nodes.begin();
+         it != _geo_nodes.end();
+         ++it )
+    {
+      GeoNodePair* geo_node = it->second.get();
+      if(    !geo_node->isComplete()
+          || (!geo_node->isDirty() && !_projection_dirty) )
+        continue;
+
+      GeoCoord lat = parseGeoCoord(geo_node->getLat());
+      if( lat.type != GeoCoord::LATITUDE )
+        continue;
+
+      GeoCoord lon = parseGeoCoord(geo_node->getLon());
+      if( lon.type != GeoCoord::LONGITUDE )
+        continue;
+
+      Projection::ScreenPosition pos =
+        _projection->worldToScreen(lat.value, lon.value);
+
+      geo_node->setScreenPos(pos.x, pos.y);
+
+//      geo_node->print();
+      geo_node->setDirty(false);
+    }
+    _projection_dirty = false;
+
+    Group::update(dt);
+  }
+
+  //----------------------------------------------------------------------------
+  void Map::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
+  {
+    if( !boost::ends_with(child->getNameString(), GEO) )
+      return Element::childAdded(parent, child);
+
+    _geo_nodes[child].reset(new GeoNodePair());
+  }
+
+  //----------------------------------------------------------------------------
+  void Map::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
+  {
+    if( !boost::ends_with(child->getNameString(), GEO) )
+      return Element::childRemoved(parent, child);
+
+    // TODO remove from other node
+    _geo_nodes.erase(child);
+  }
+
+  //----------------------------------------------------------------------------
+  void Map::valueChanged(SGPropertyNode * child)
+  {
+    const std::string& name = child->getNameString();
+
+    if( !boost::ends_with(name, GEO) )
+      return Group::valueChanged(child);
+
+    GeoNodes::iterator it_geo_node = _geo_nodes.find(child);
+    if( it_geo_node == _geo_nodes.end() )
+      LOG_GEO_RET("geo node not found!")
+    GeoNodePair* geo_node = it_geo_node->second.get();
+
+    geo_node->setDirty();
+
+    if( geo_node->getStatus() & GeoNodePair::INCOMPLETE )
+    {
+      // Detect lat, lon tuples...
+      GeoCoord coord = parseGeoCoord(child->getStringValue());
+      int index_other = -1;
+
+      switch( coord.type )
+      {
+        case GeoCoord::LATITUDE:
+          index_other = child->getIndex() + 1;
+          geo_node->setNodeLat(child);
+          break;
+        case GeoCoord::LONGITUDE:
+          index_other = child->getIndex() - 1;
+          geo_node->setNodeLon(child);
+          break;
+        default:
+          LOG_GEO_RET("Invalid geo coord")
+      }
+
+      SGPropertyNode *other = child->getParent()->getChild(name, index_other);
+      if( !other )
+        return;
+
+      GeoCoord coord_other = parseGeoCoord(other->getStringValue());
+      if(    coord_other.type == GeoCoord::INVALID
+          || coord_other.type == coord.type )
+        return;
+
+      GeoNodes::iterator it_geo_node_other = _geo_nodes.find(other);
+      if( it_geo_node_other == _geo_nodes.end() )
+        LOG_GEO_RET("other geo node not found!")
+      GeoNodePair* geo_node_other = it_geo_node_other->second.get();
+
+      // Let use both nodes use the same GeoNodePair instance
+      if( geo_node_other != geo_node )
+        it_geo_node_other->second = it_geo_node->second;
+
+      if( coord_other.type == GeoCoord::LATITUDE )
+        geo_node->setNodeLat(other);
+      else
+        geo_node->setNodeLon(other);
+
+      // Set name for resulting screen coordinate nodes
+      geo_node->setTargetName( name.substr(0, name.length() - GEO.length()) );
+    }
+  }
+
+  //----------------------------------------------------------------------------
+  void Map::childChanged(SGPropertyNode * child)
+  {
+    if( child->getParent() != _node )
+      return Group::childChanged(child);
+
+    if(    child->getNameString() == "ref-lat"
+        || child->getNameString() == "ref-lon" )
+      _projection->setWorldPosition( _node->getDoubleValue("ref-lat"),
+                                     _node->getDoubleValue("ref-lon") );
+    else if( child->getNameString() == "hdg" )
+      _projection->setOrientation(child->getFloatValue());
+    else if( child->getNameString() == "range" )
+      _projection->setRange(child->getDoubleValue());
+    else
+      return Group::childChanged(child);
+
+    _projection_dirty = true;
+  }
+
+  //----------------------------------------------------------------------------
+  Map::GeoCoord Map::parseGeoCoord(const std::string& val) const
+  {
+    GeoCoord coord;
+    if( val.length() < 2 )
+      return coord;
+
+    if( val[0] == 'N' || val[0] == 'S' )
+      coord.type = GeoCoord::LATITUDE;
+    else if( val[0] == 'E' || val[0] == 'W' )
+      coord.type = GeoCoord::LONGITUDE;
+    else
+      return coord;
+
+    char* end;
+    coord.value = strtod(&val[1], &end);
+
+    if( end != &val[val.length()] )
+    {
+      coord.type = GeoCoord::INVALID;
+      return coord;
+    }
+
+    if( val[0] == 'S' || val[0] == 'W' )
+      coord.value *= -1;
+
+    return coord;
+  }
+
+} // namespace canvas
+} // namespace simgear
diff --git a/simgear/canvas/elements/CanvasMap.hxx b/simgear/canvas/elements/CanvasMap.hxx
new file mode 100644 (file)
index 0000000..518c0b2
--- /dev/null
@@ -0,0 +1,82 @@
+// A group of 2D Canvas elements which get automatically transformed according
+// to the map parameters.
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef CANVAS_MAP_HXX_
+#define CANVAS_MAP_HXX_
+
+#include "CanvasGroup.hxx"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/unordered_map.hpp>
+
+namespace simgear
+{
+namespace canvas
+{
+  class GeoNodePair;
+  class HorizontalProjection;
+  class Map:
+    public Group
+  {
+    public:
+      Map( const CanvasWeakPtr& canvas,
+           SGPropertyNode_ptr node,
+           const Style& parent_style );
+      virtual ~Map();
+
+      virtual void update(double dt);
+
+      virtual void childAdded( SGPropertyNode * parent,
+                               SGPropertyNode * child );
+      virtual void childRemoved( SGPropertyNode * parent,
+                                 SGPropertyNode * child );
+      virtual void valueChanged(SGPropertyNode * child);
+
+    protected:
+
+      virtual void childChanged(SGPropertyNode * child);
+
+      typedef boost::unordered_map< SGPropertyNode*,
+                                    boost::shared_ptr<GeoNodePair>
+                                  > GeoNodes;
+      GeoNodes _geo_nodes;
+      boost::shared_ptr<HorizontalProjection> _projection;
+      bool _projection_dirty;
+
+      struct GeoCoord
+      {
+        GeoCoord():
+          type(INVALID)
+        {}
+        enum
+        {
+          INVALID,
+          LATITUDE,
+          LONGITUDE
+        } type;
+        double value;
+      };
+
+      GeoCoord parseGeoCoord(const std::string& val) const;
+  };
+
+} // namespace canvas
+} // namespace simgear
+
+#endif /* CANVAS_MAP_HXX_ */
diff --git a/simgear/canvas/elements/CanvasPath.cxx b/simgear/canvas/elements/CanvasPath.cxx
new file mode 100644 (file)
index 0000000..ac9d6a9
--- /dev/null
@@ -0,0 +1,439 @@
+// An OpenVG path on the Canvas
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#include "CanvasPath.hxx"
+#include <simgear/misc/parse_color.hxx>
+
+#include <osg/Drawable>
+#include <osg/BlendFunc>
+
+#include <vg/openvg.h>
+#include <cassert>
+
+namespace simgear
+{
+namespace canvas
+{
+  typedef std::vector<VGubyte>  CmdList;
+  typedef std::vector<VGfloat>  CoordList;
+
+  /**
+   * Helper to split and convert comma/whitespace separated floating point
+   * values
+   */
+  std::vector<float> splitAndConvert(const char del[], const std::string& str);
+
+  class Path::PathDrawable:
+    public osg::Drawable
+  {
+    public:
+      PathDrawable(Path* path):
+        _path_element(path),
+        _path(VG_INVALID_HANDLE),
+        _paint(VG_INVALID_HANDLE),
+        _paint_fill(VG_INVALID_HANDLE),
+        _attributes_dirty(~0),
+        _mode(0),
+        _fill_rule(VG_EVEN_ODD),
+        _stroke_width(1),
+        _stroke_linecap(VG_CAP_BUTT)
+      {
+        setSupportsDisplayList(false);
+        setDataVariance(Object::DYNAMIC);
+
+        setUpdateCallback(new PathUpdateCallback());
+      }
+
+      virtual ~PathDrawable()
+      {
+        if( _path != VG_INVALID_HANDLE )
+          vgDestroyPath(_path);
+        if( _paint != VG_INVALID_HANDLE )
+          vgDestroyPaint(_paint);
+        if( _paint_fill != VG_INVALID_HANDLE )
+          vgDestroyPaint(_paint_fill);
+      }
+
+      virtual const char* className() const { return "PathDrawable"; }
+      virtual osg::Object* cloneType() const { return new PathDrawable(_path_element); }
+      virtual osg::Object* clone(const osg::CopyOp&) const { return new PathDrawable(_path_element); }
+
+      /**
+       * Replace the current path segments with the new ones
+       *
+       * @param cmds    List of OpenVG path commands
+       * @param coords  List of coordinates/parameters used by #cmds
+       */
+      void setSegments(const CmdList& cmds, const CoordList& coords)
+      {
+        _cmds = cmds;
+        _coords = coords;
+
+        _attributes_dirty |= (PATH | BOUNDING_BOX);
+      }
+
+      /**
+       * Set path fill paint ("none" if not filled)
+       */
+      void setFill(const std::string& fill)
+      {
+        if( fill == "none" )
+        {
+          _mode &= ~VG_FILL_PATH;
+        }
+        else if( parseColor(fill, _fill_color) )
+        {
+          _mode |= VG_FILL_PATH;
+          _attributes_dirty |= FILL_COLOR;
+        }
+        else
+        {
+          SG_LOG
+          (
+            SG_GENERAL,
+            SG_WARN,
+            "canvas::Path Unknown fill: " << fill
+          );
+        }
+      }
+
+      /**
+       * Set path fill rule ("pseudo-nonzero" or "evenodd")
+       *
+       * @warning As the current nonzero implementation causes sever artifacts
+       *          for every concave path we call it pseudo-nonzero, so that
+       *          everyone is warned that it won't work as expected :)
+       */
+      void setFillRule(const std::string& fill_rule)
+      {
+        if( fill_rule == "pseudo-nonzero" )
+          _fill_rule = VG_NON_ZERO;
+        else // if( fill_rule == "evenodd" )
+          _fill_rule = VG_EVEN_ODD;
+      }
+
+      /**
+       * Set path stroke paint ("none" if no stroke)
+       */
+      void setStroke(const std::string& stroke)
+      {
+        if( stroke == "none" )
+        {
+          _mode &= ~VG_STROKE_PATH;
+        }
+        else if( parseColor(stroke, _stroke_color) )
+        {
+          _mode |= VG_STROKE_PATH;
+                    _attributes_dirty |= STROKE_COLOR;
+        }
+        else
+        {
+          SG_LOG
+          (
+            SG_GENERAL,
+            SG_WARN,
+            "canvas::Path Unknown stroke: " << stroke
+          );
+        }
+      }
+
+      /**
+       * Set stroke width
+       */
+      void setStrokeWidth(float width)
+      {
+        _stroke_width = width;
+        _attributes_dirty |= BOUNDING_BOX;
+      }
+
+      /**
+       * Set stroke dash (line stipple)
+       */
+      void setStrokeDashArray(const std::string& dash)
+      {
+        _stroke_dash = splitAndConvert(",\t\n ", dash);
+      }
+
+      /**
+       * Set stroke-linecap
+       *
+       * @see http://www.w3.org/TR/SVG/painting.html#StrokeLinecapProperty
+       */
+      void setStrokeLinecap(const std::string& linecap)
+      {
+        if( linecap == "round" )
+          _stroke_linecap = VG_CAP_ROUND;
+        else if( linecap == "square" )
+          _stroke_linecap = VG_CAP_SQUARE;
+        else
+          _stroke_linecap = VG_CAP_BUTT;
+      }
+
+      /**
+       * Draw callback
+       */
+      virtual void drawImplementation(osg::RenderInfo& renderInfo) const
+      {
+        if( _attributes_dirty & PATH )
+          return;
+
+        osg::State* state = renderInfo.getState();
+        assert(state);
+
+        state->setActiveTextureUnit(0);
+        state->setClientActiveTextureUnit(0);
+        state->disableAllVertexArrays();
+
+        glPushAttrib(~0u); // Don't use GL_ALL_ATTRIB_BITS as on my machine it
+                           // eg. doesn't include GL_MULTISAMPLE_BIT
+        glPushClientAttrib(~0u);
+
+        // Initialize/Update the paint
+        if( _attributes_dirty & STROKE_COLOR )
+        {
+          if( _paint == VG_INVALID_HANDLE )
+            _paint = vgCreatePaint();
+
+          vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, _stroke_color._v);
+
+          _attributes_dirty &= ~STROKE_COLOR;
+        }
+
+        // Initialize/update fill paint
+        if( _attributes_dirty & FILL_COLOR )
+        {
+          if( _paint_fill == VG_INVALID_HANDLE )
+            _paint_fill = vgCreatePaint();
+
+          vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, _fill_color._v);
+
+          _attributes_dirty &= ~FILL_COLOR;
+        }
+
+        // Setup paint
+        if( _mode & VG_STROKE_PATH )
+        {
+          vgSetPaint(_paint, VG_STROKE_PATH);
+
+          vgSetf(VG_STROKE_LINE_WIDTH, _stroke_width);
+          vgSeti(VG_STROKE_CAP_STYLE, _stroke_linecap);
+          vgSetfv( VG_STROKE_DASH_PATTERN,
+                   _stroke_dash.size(),
+                   _stroke_dash.empty() ? 0 : &_stroke_dash[0] );
+        }
+        if( _mode & VG_FILL_PATH )
+        {
+          vgSetPaint(_paint_fill, VG_FILL_PATH);
+
+          vgSeti(VG_FILL_RULE, _fill_rule);
+        }
+
+        // And finally draw the path
+        if( _mode )
+          vgDrawPath(_path, _mode);
+
+        VGErrorCode err = vgGetError();
+        if( err != VG_NO_ERROR )
+          SG_LOG(SG_GL, SG_ALERT, "vgError: " << err);
+
+        glPopAttrib();
+        glPopClientAttrib();
+      }
+
+      /**
+       * Compute the bounding box
+       */
+      virtual osg::BoundingBox computeBound() const
+      {
+        if( _path == VG_INVALID_HANDLE || (_attributes_dirty & PATH) )
+          return osg::BoundingBox();
+
+        VGfloat min[2], size[2];
+        vgPathBounds(_path, &min[0], &min[1], &size[0], &size[1]);
+
+        _attributes_dirty &= ~BOUNDING_BOX;
+
+        // vgPathBounds doesn't take stroke width into account
+        float ext = 0.5 * _stroke_width;
+
+        osg::BoundingBox bb
+        (
+          min[0] - ext,           min[1] - ext,           -0.1,
+          min[0] + size[0] + ext, min[1] + size[1] + ext,  0.1
+        );
+        _path_element->setBoundingBox(bb);
+
+        return bb;
+      }
+
+    private:
+
+      enum Attributes
+      {
+        PATH            = 0x0001,
+        STROKE_COLOR    = PATH << 1,
+        FILL_COLOR      = STROKE_COLOR << 1,
+        BOUNDING_BOX    = FILL_COLOR << 1
+      };
+
+      Path *_path_element;
+
+      mutable VGPath    _path;
+      mutable VGPaint   _paint;
+      mutable VGPaint   _paint_fill;
+      mutable uint32_t  _attributes_dirty;
+
+      CmdList   _cmds;
+      CoordList _coords;
+
+      VGbitfield            _mode;
+      osg::Vec4f            _fill_color;
+      VGFillRule            _fill_rule;
+      osg::Vec4f            _stroke_color;
+      VGfloat               _stroke_width;
+      std::vector<VGfloat>  _stroke_dash;
+      VGCapStyle            _stroke_linecap;
+
+      /**
+       * Initialize/Update the OpenVG path
+       */
+      void update()
+      {
+        if( _attributes_dirty & PATH )
+        {
+          const VGbitfield caps = VG_PATH_CAPABILITY_APPEND_TO
+                                | VG_PATH_CAPABILITY_MODIFY
+                                | VG_PATH_CAPABILITY_PATH_BOUNDS;
+
+          if( _path == VG_INVALID_HANDLE )
+            _path = vgCreatePath(
+              VG_PATH_FORMAT_STANDARD,
+              VG_PATH_DATATYPE_F,
+              1.f, 0.f, // scale,bias
+              _cmds.size(), _coords.size(),
+              caps
+            );
+          else
+            vgClearPath(_path, caps);
+
+          if( !_cmds.empty() && !_coords.empty() )
+            vgAppendPathData(_path, _cmds.size(), &_cmds[0], &_coords[0]);
+
+          _attributes_dirty &= ~PATH;
+          _attributes_dirty |= BOUNDING_BOX;
+        }
+
+        if( _attributes_dirty & BOUNDING_BOX )
+          dirtyBound();
+      }
+
+      struct PathUpdateCallback:
+        public osg::Drawable::UpdateCallback
+      {
+        virtual void update(osg::NodeVisitor*, osg::Drawable* drawable)
+        {
+          static_cast<PathDrawable*>(drawable)->update();
+        }
+      };
+  };
+
+  //----------------------------------------------------------------------------
+  Path::Path( const CanvasWeakPtr& canvas,
+              SGPropertyNode_ptr node,
+              const Style& parent_style ):
+    Element(canvas, node, parent_style),
+    _path( new PathDrawable(this) )
+  {
+    setDrawable(_path);
+    PathDrawable *path = _path.get();
+
+    addStyle("fill", &PathDrawable::setFill, path);
+    addStyle("fill-rule", &PathDrawable::setFillRule, path);
+    addStyle("stroke", &PathDrawable::setStroke, path);
+    addStyle("stroke-width", &PathDrawable::setStrokeWidth, path);
+    addStyle("stroke-dasharray", &PathDrawable::setStrokeDashArray, path);
+    addStyle("stroke-linecap", &PathDrawable::setStrokeLinecap, path);
+
+    setupStyle();
+  }
+
+  //----------------------------------------------------------------------------
+  Path::~Path()
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  void Path::update(double dt)
+  {
+    if( _attributes_dirty & (CMDS | COORDS) )
+    {
+      _path->setSegments
+      (
+        _node->getChildValues<VGubyte, int>("cmd"),
+        _node->getChildValues<VGfloat, float>("coord")
+      );
+
+      _attributes_dirty &= ~(CMDS | COORDS);
+    }
+
+    Element::update(dt);
+  }
+
+  //----------------------------------------------------------------------------
+  void Path::childRemoved(SGPropertyNode* child)
+  {
+    childChanged(child);
+  }
+
+  //----------------------------------------------------------------------------
+  void Path::childChanged(SGPropertyNode* child)
+  {
+    if( child->getParent() != _node )
+      return;
+
+    if( child->getNameString() == "cmd" )
+      _attributes_dirty |= CMDS;
+    else if( child->getNameString() == "coord" )
+      _attributes_dirty |= COORDS;
+  }
+
+  //----------------------------------------------------------------------------
+  std::vector<float> splitAndConvert(const char del[], const std::string& str)
+  {
+    std::vector<float> values;
+    size_t pos = 0;
+    for(;;)
+    {
+      pos = str.find_first_not_of(del, pos);
+      if( pos == std::string::npos )
+        break;
+
+      char *end = 0;
+      float val = strtod(&str[pos], &end);
+      if( end == &str[pos] || !end )
+        break;
+
+      values.push_back(val);
+      pos = end - &str[0];
+    }
+    return values;
+  }
+
+} // namespace canvas
+} // namespace simgear
diff --git a/simgear/canvas/elements/CanvasPath.hxx b/simgear/canvas/elements/CanvasPath.hxx
new file mode 100644 (file)
index 0000000..e61897f
--- /dev/null
@@ -0,0 +1,57 @@
+// An OpenVG path on the Canvas
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef CANVAS_PATH_HXX_
+#define CANVAS_PATH_HXX_
+
+#include "CanvasElement.hxx"
+
+namespace simgear
+{
+namespace canvas
+{
+  class Path:
+    public Element
+  {
+    public:
+      Path( const CanvasWeakPtr& canvas,
+            SGPropertyNode_ptr node,
+            const Style& parent_style );
+      virtual ~Path();
+
+      virtual void update(double dt);
+
+    protected:
+
+      enum PathAttributes
+      {
+        CMDS       = LAST_ATTRIBUTE << 1,
+        COORDS     = CMDS << 1
+      };
+
+      class PathDrawable;
+      osg::ref_ptr<PathDrawable> _path;
+
+      virtual void childRemoved(SGPropertyNode * child);
+      virtual void childChanged(SGPropertyNode * child);
+  };
+
+} // namespace canvas
+} // namespace simgear
+
+#endif /* CANVAS_PATH_HXX_ */
diff --git a/simgear/canvas/elements/CanvasText.cxx b/simgear/canvas/elements/CanvasText.cxx
new file mode 100644 (file)
index 0000000..fdaa789
--- /dev/null
@@ -0,0 +1,291 @@
+// A text on the Canvas
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#include "CanvasText.hxx"
+#include <simgear/canvas/Canvas.hxx>
+#include <simgear/canvas/CanvasSystemAdapter.hxx>
+#include <simgear/misc/parse_color.hxx>
+#include <osgText/Text>
+
+namespace simgear
+{
+namespace canvas
+{
+  class Text::TextOSG:
+    public osgText::Text
+  {
+    public:
+
+      TextOSG(canvas::Text* text);
+
+      void setCharacterAspect(float aspect);
+      void setFill(const std::string& fill);
+      void setBackgroundColor(const std::string& fill);
+
+      osg::Vec2 handleHit(float x, float y);
+
+      virtual osg::BoundingBox computeBound() const;
+
+    protected:
+
+      canvas::Text *_text_element;
+  };
+
+  //----------------------------------------------------------------------------
+  Text::TextOSG::TextOSG(canvas::Text* text):
+    _text_element(text)
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  void Text::TextOSG::setCharacterAspect(float aspect)
+  {
+    setCharacterSize(getCharacterHeight(), aspect);
+  }
+
+  //----------------------------------------------------------------------------
+  void Text::TextOSG::setFill(const std::string& fill)
+  {
+//    if( fill == "none" )
+//      TODO No text
+//    else
+    osg::Vec4 color;
+    if( parseColor(fill, color) )
+      setColor( color );
+  }
+
+  //----------------------------------------------------------------------------
+  void Text::TextOSG::setBackgroundColor(const std::string& fill)
+  {
+    osg::Vec4 color;
+    if( parseColor(fill, color) )
+      setBoundingBoxColor( color );
+  }
+
+  //----------------------------------------------------------------------------
+  osg::Vec2 Text::TextOSG::handleHit(float x, float y)
+  {
+    float line_height = _characterHeight + _lineSpacing;
+
+    // TODO check with align other than TOP
+    float first_line_y = -0.5 * _lineSpacing;//_offset.y() - _characterHeight;
+    size_t line = std::max<int>(0, (y - first_line_y) / line_height);
+
+    if( _textureGlyphQuadMap.empty() )
+      return osg::Vec2(-1, -1);
+
+    // TODO check when it can be larger
+    assert( _textureGlyphQuadMap.size() == 1 );
+
+    const GlyphQuads& glyphquad = _textureGlyphQuadMap.begin()->second;
+    const GlyphQuads::Glyphs& glyphs = glyphquad._glyphs;
+    const GlyphQuads::Coords2& coords = glyphquad._coords;
+    const GlyphQuads::LineNumbers& line_numbers = glyphquad._lineNumbers;
+
+    const float HIT_FRACTION = 0.6;
+    const float character_width = getCharacterHeight()
+                                * getCharacterAspectRatio();
+
+    y = (line + 0.5) * line_height;
+
+    bool line_found = false;
+    for(size_t i = 0; i < line_numbers.size(); ++i)
+    {
+      if( line_numbers[i] != line )
+      {
+        if( !line_found )
+        {
+          if( line_numbers[i] < line )
+            // Wait for the correct line...
+            continue;
+
+          // We have already passed the correct line -> It's empty...
+          return osg::Vec2(0, y);
+        }
+
+        // Next line and not returned -> not before any character
+        // -> return position after last character of line
+        return osg::Vec2(coords[(i - 1) * 4 + 2].x(), y);
+      }
+
+      line_found = true;
+
+      // Get threshold for mouse x position for setting cursor before or after
+      // current character
+      float threshold = coords[i * 4].x()
+                      + HIT_FRACTION * glyphs[i]->getHorizontalAdvance()
+                                     * character_width;
+
+      if( x <= threshold )
+      {
+        if( i == 0 || line_numbers[i - 1] != line )
+          // first character of line
+          x = coords[i * 4].x();
+        else if( coords[(i - 1) * 4].x() == coords[(i - 1) * 4 + 2].x() )
+          // If previous character width is zero set to begin of next character
+          // (Happens eg. with spaces)
+          x = coords[i * 4].x();
+        else
+          // position at center between characters
+          x = 0.5 * (coords[(i - 1) * 4 + 2].x() + coords[i * 4].x());
+
+        return osg::Vec2(x, y);
+      }
+    }
+
+    // Nothing found -> return position after last character
+    return osg::Vec2
+    (
+      coords.back().x(),
+      (_lineCount - 0.5) * line_height
+    );
+  }
+
+  //----------------------------------------------------------------------------
+  osg::BoundingBox Text::TextOSG::computeBound() const
+  {
+    osg::BoundingBox bb = osgText::Text::computeBound();
+    if( !bb.valid() )
+      return bb;
+
+    // TODO bounding box still doesn't seem always right (eg. with center
+    //      horizontal alignment not completely accurate)
+    bb._min.y() += _offset.y();
+    bb._max.y() += _offset.y();
+
+    _text_element->setBoundingBox(bb);
+
+    return bb;
+  }
+
+  //----------------------------------------------------------------------------
+  Text::Text( const CanvasWeakPtr& canvas,
+              SGPropertyNode_ptr node,
+              const Style& parent_style ):
+    Element(canvas, node, parent_style),
+    _text( new Text::TextOSG(this) )
+  {
+    setDrawable(_text);
+    _text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
+    _text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
+    _text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
+
+    addStyle("fill", &TextOSG::setFill, _text);
+    addStyle("background", &TextOSG::setBackgroundColor, _text);
+    addStyle("character-size",
+             static_cast<void (TextOSG::*)(float)>(&TextOSG::setCharacterSize),
+             _text);
+    addStyle("character-aspect-ratio", &TextOSG::setCharacterAspect, _text);
+    addStyle("padding", &TextOSG::setBoundingBoxMargin, _text);
+    //  TEXT              = 1 default
+    //  BOUNDINGBOX       = 2
+    //  FILLEDBOUNDINGBOX = 4
+    //  ALIGNMENT         = 8
+    addStyle<int>("draw-mode", &TextOSG::setDrawMode, _text);
+    addStyle("max-width", &TextOSG::setMaximumWidth, _text);
+    addStyle("font", &Text::setFont, this);
+    addStyle("alignment", &Text::setAlignment, this);
+    addStyle("text", &Text::setText, this);
+
+    setupStyle();
+  }
+
+  //----------------------------------------------------------------------------
+  Text::~Text()
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  void Text::setText(const char* text)
+  {
+    _text->setText(text, osgText::String::ENCODING_UTF8);
+  }
+
+  //----------------------------------------------------------------------------
+  void Text::setFont(const char* name)
+  {
+    _text->setFont( _canvas.lock()->getSystemAdapter()->getFont(name) );
+  }
+
+  //----------------------------------------------------------------------------
+  void Text::setAlignment(const char* align)
+  {
+    const std::string align_string(align);
+    if( 0 ) return;
+#define ENUM_MAPPING(enum_val, string_val) \
+    else if( align_string == string_val )\
+      _text->setAlignment( osgText::Text::enum_val );
+#include "text-alignment.hxx"
+#undef ENUM_MAPPING
+    else
+    {
+      if( !align_string.empty() )
+        SG_LOG
+        (
+          SG_GENERAL,
+          SG_WARN,
+          "canvas::Text: unknown alignment '" << align_string << "'"
+        );
+      _text->setAlignment(osgText::Text::LEFT_BASE_LINE);
+    }
+  }
+
+  //----------------------------------------------------------------------------
+#if 0
+  const char* Text::getAlignment() const
+  {
+    switch( _text->getAlignment() )
+    {
+#define ENUM_MAPPING(enum_val, string_val) \
+      case osgText::Text::enum_val:\
+        return string_val;
+#include "text-alignment.hxx"
+#undef ENUM_MAPPING
+      default:
+        return "unknown";
+    }
+  }
+#endif
+
+  //----------------------------------------------------------------------------
+  void Text::childChanged(SGPropertyNode* child)
+  {
+    if( child->getParent() != _node )
+      return;
+
+    const std::string& name = child->getNameString();
+    if( name == "hit-y" )
+      handleHit
+      (
+        _node->getFloatValue("hit-x"),
+        _node->getFloatValue("hit-y")
+      );
+  }
+
+  //----------------------------------------------------------------------------
+  void Text::handleHit(float x, float y)
+  {
+    const osg::Vec2& pos = _text->handleHit(x, y);
+    _node->setFloatValue("cursor-x", pos.x());
+    _node->setFloatValue("cursor-y", pos.y());
+  }
+
+} // namespace canvas
+} // namespace simgear
diff --git a/simgear/canvas/elements/CanvasText.hxx b/simgear/canvas/elements/CanvasText.hxx
new file mode 100644 (file)
index 0000000..22596c5
--- /dev/null
@@ -0,0 +1,59 @@
+// A text on the Canvas
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef CANVAS_TEXT_HXX_
+#define CANVAS_TEXT_HXX_
+
+#include "CanvasElement.hxx"
+#include <osgText/Text>
+#include <map>
+#include <vector>
+
+namespace simgear
+{
+namespace canvas
+{
+
+  class Text:
+    public Element
+  {
+    public:
+      Text( const CanvasWeakPtr& canvas,
+            SGPropertyNode_ptr node,
+            const Style& parent_style );
+      ~Text();
+
+      void setText(const char* text);
+      void setFont(const char* name);
+      void setAlignment(const char* align);
+
+    protected:
+
+      class TextOSG;
+      osg::ref_ptr<TextOSG> _text;
+
+      virtual void childChanged(SGPropertyNode * child);
+
+      void handleHit(float x, float y);
+
+  };
+
+} // namespace canvas
+} // namespace simgear
+
+#endif /* CANVAS_TEXT_HXX_ */
diff --git a/simgear/canvas/elements/map/geo_node_pair.hxx b/simgear/canvas/elements/map/geo_node_pair.hxx
new file mode 100644 (file)
index 0000000..deb4c37
--- /dev/null
@@ -0,0 +1,134 @@
+// Groups together two nodes representing a geographic position (lat + lon)
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef CANVAS_GEO_NODE_PAIR_HXX_
+#define CANVAS_GEO_NODE_PAIR_HXX_
+
+namespace simgear
+{
+namespace canvas
+{
+  class GeoNodePair
+  {
+    public:
+      enum StatusFlags
+      {
+        LAT_MISSING = 1,
+        LON_MISSING = LAT_MISSING << 1,
+        INCOMPLETE = LAT_MISSING | LON_MISSING,
+        DIRTY = LON_MISSING << 1
+      };
+
+      GeoNodePair():
+        _status(INCOMPLETE),
+        _node_lat(0),
+        _node_lon(0)
+      {}
+
+      uint8_t getStatus() const
+      {
+        return _status;
+      }
+
+      void setDirty(bool flag = true)
+      {
+        if( flag )
+          _status |= DIRTY;
+        else
+          _status &= ~DIRTY;
+      }
+
+      bool isDirty() const
+      {
+        return (_status & DIRTY)!=0;
+      }
+
+      bool isComplete() const
+      {
+        return !(_status & INCOMPLETE);
+      }
+
+      void setNodeLat(SGPropertyNode* node)
+      {
+        _node_lat = node;
+        _status &= ~LAT_MISSING;
+
+        if( node == _node_lon )
+        {
+          _node_lon = 0;
+          _status |= LON_MISSING;
+        }
+      }
+
+      void setNodeLon(SGPropertyNode* node)
+      {
+        _node_lon = node;
+        _status &= ~LON_MISSING;
+
+        if( node == _node_lat )
+        {
+          _node_lat = 0;
+          _status |= LAT_MISSING;
+        }
+      }
+
+      const char* getLat() const
+      {
+        return _node_lat ? _node_lat->getStringValue() : "";
+      }
+
+      const char* getLon() const
+      {
+        return _node_lon ? _node_lon->getStringValue() : "";
+      }
+
+      void setTargetName(const std::string& name)
+      {
+        _target_name = name;
+      }
+
+      void setScreenPos(float x, float y)
+      {
+        assert( isComplete() );
+        SGPropertyNode *parent = _node_lat->getParent();
+        parent->getChild(_target_name, _node_lat->getIndex(), true)
+              ->setDoubleValue(x);
+        parent->getChild(_target_name, _node_lon->getIndex(), true)
+              ->setDoubleValue(y);
+      }
+
+      void print()
+      {
+        std::cout << "lat=" << (_node_lat ? _node_lat->getPath() : "")
+                  << ", lon=" << (_node_lon ? _node_lon->getPath() : "")
+                  << std::endl;
+      }
+
+    private:
+
+      uint8_t _status;
+      SGPropertyNode *_node_lat,
+                     *_node_lon;
+      std::string   _target_name;
+
+  };
+
+} // namespace canvas
+} // namespace simgear
+
+#endif /* CANVAS_GEO_NODE_PAIR_HXX_ */
diff --git a/simgear/canvas/elements/map/projection.hxx b/simgear/canvas/elements/map/projection.hxx
new file mode 100644 (file)
index 0000000..9a3f65d
--- /dev/null
@@ -0,0 +1,179 @@
+// Geographic projections for Canvas map element
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef CANVAS_MAP_PROJECTION_HXX_
+#define CANVAS_MAP_PROJECTION_HXX_
+
+#include <simgear/math/SGMisc.hxx>
+
+namespace simgear
+{
+namespace canvas
+{
+
+  /**
+   * Base class for all projections
+   */
+  class Projection
+  {
+    public:
+      struct ScreenPosition
+      {
+        ScreenPosition() {}
+
+        ScreenPosition(double x, double y):
+          x(x),
+          y(y)
+        {}
+
+        double x, y;
+      };
+
+      virtual ~Projection() {}
+
+      void setScreenRange(double range)
+      {
+        _screen_range = range;
+      }
+
+      virtual ScreenPosition worldToScreen(double x, double y) = 0;
+
+    protected:
+
+      double _screen_range;
+  };
+
+  /**
+   * Base class for horizontal projections
+   */
+  class HorizontalProjection:
+    public Projection
+  {
+    public:
+
+      HorizontalProjection():
+        _cos_rot(1),
+        _sin_rot(0),
+        _range(5)
+      {
+        setScreenRange(200);
+      }
+
+      /**
+       * Set world position of center point used for the projection
+       */
+      void setWorldPosition(double lat, double lon)
+      {
+        _ref_lat = SGMiscd::deg2rad(lat);
+        _ref_lon = SGMiscd::deg2rad(lon);
+      }
+
+      /**
+       * Set up heading
+       */
+      void setOrientation(float hdg)
+      {
+        hdg = SGMiscf::deg2rad(hdg);
+        _sin_rot = sin(hdg);
+        _cos_rot = cos(hdg);
+      }
+
+      void setRange(double range)
+      {
+        _range = range;
+      }
+
+      /**
+       * Transform given world position to screen position
+       *
+       * @param lat   Latitude in degrees
+       * @param lon   Longitude in degrees
+       */
+      ScreenPosition worldToScreen(double lat, double lon)
+      {
+        lat = SGMiscd::deg2rad(lat);
+        lon = SGMiscd::deg2rad(lon);
+        ScreenPosition pos = project(lat, lon);
+        double scale = _screen_range / _range;
+        pos.x *= scale;
+        pos.y *= scale;
+        return ScreenPosition
+        (
+          _cos_rot * pos.x - _sin_rot * pos.y,
+         -_sin_rot * pos.x - _cos_rot * pos.y
+        );
+      }
+
+    protected:
+
+      /**
+       * Project given geographic world position to screen space
+       *
+       * @param lat   Latitude in radians
+       * @param lon   Longitude in radians
+       */
+      virtual ScreenPosition project(double lat, double lon) const = 0;
+
+      double  _ref_lat,
+              _ref_lon,
+              _cos_rot,
+              _sin_rot,
+              _range;
+  };
+
+  /**
+   * Sanson-Flamsteed projection, relative to the projection center
+   */
+  class SansonFlamsteedProjection:
+    public HorizontalProjection
+  {
+    protected:
+
+      virtual ScreenPosition project(double lat, double lon) const
+      {
+        double d_lat = lat - _ref_lat,
+               d_lon = lon - _ref_lon;
+        double r = getEarthRadius(lat);
+
+        ScreenPosition pos;
+
+        pos.x = r * cos(lat) * d_lon;
+        pos.y = r * d_lat;
+
+        return pos;
+      }
+
+      /**
+       * Returns Earth radius at a given latitude (Ellipsoide equation with two
+       * equal axis)
+       */
+      float getEarthRadius(float lat) const
+      {
+        const float rec  = 6378137.f / 1852;      // earth radius, equator (?)
+        const float rpol = 6356752.314f / 1852;   // earth radius, polar   (?)
+
+        double a = cos(lat) / rec;
+        double b = sin(lat) / rpol;
+        return 1.0f / sqrt( a * a + b * b );
+      }
+  };
+
+} // namespace canvas
+} // namespace simgear
+
+#endif /* CANVAS_MAP_PROJECTION_HXX_ */
diff --git a/simgear/canvas/elements/text-alignment.hxx b/simgear/canvas/elements/text-alignment.hxx
new file mode 100644 (file)
index 0000000..491ab25
--- /dev/null
@@ -0,0 +1,41 @@
+// Mapping between strings and osg text alignment flags
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef ENUM_MAPPING
+# error "Only include with ENUM_MAPPING defined!"
+#endif
+
+ENUM_MAPPING(LEFT_TOP,      "left-top")
+ENUM_MAPPING(LEFT_CENTER,   "left-center")
+ENUM_MAPPING(LEFT_BOTTOM,   "left-bottom")
+
+ENUM_MAPPING(CENTER_TOP,    "center-top")
+ENUM_MAPPING(CENTER_CENTER, "center-center")
+ENUM_MAPPING(CENTER_BOTTOM, "center-bottom")
+
+ENUM_MAPPING(RIGHT_TOP,     "right-top")
+ENUM_MAPPING(RIGHT_CENTER,  "right-center")
+ENUM_MAPPING(RIGHT_BOTTOM,  "right-bottom")
+
+ENUM_MAPPING(LEFT_BASE_LINE,    "left-baseline")
+ENUM_MAPPING(CENTER_BASE_LINE,  "center-baseline")
+ENUM_MAPPING(RIGHT_BASE_LINE,   "right-baseline")
+
+ENUM_MAPPING(LEFT_BOTTOM_BASE_LINE,     "left-bottom-baseline")
+ENUM_MAPPING(CENTER_BOTTOM_BASE_LINE,   "center-bottom-baseline")
+ENUM_MAPPING(RIGHT_BOTTOM_BASE_LINE,    "right-bottom-baseline")
index e0d3e627c07b68b25472280d8be92b510a7dbaa6..a334680dbdeccb8bdede9c511b174add9ad046b4 100644 (file)
@@ -1,7 +1,8 @@
 include (SimGearComponent)
 
 
-set(HEADERS 
+set(HEADERS
+    Rect.hxx
     SGBox.hxx
     SGCMath.hxx
     SGGeoc.hxx
diff --git a/simgear/math/Rect.hxx b/simgear/math/Rect.hxx
new file mode 100644 (file)
index 0000000..577cd8c
--- /dev/null
@@ -0,0 +1,85 @@
+// Class representing a rectangular region
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef SG_RECT_HXX_
+#define SG_RECT_HXX_
+
+#include <osg/Vec2>
+
+namespace simgear
+{
+
+  template<typename T>
+  class Rect
+  {
+    public:
+      Rect():
+        _x1(0),
+        _x2(0),
+        _y1(0),
+        _y2(0)
+      {}
+
+      Rect(T x, T y, T w, T h):
+        _x1(x),
+        _x2(x + w),
+        _y1(y),
+        _y2(y + h)
+      {}
+
+      void set(T x, T y, T w, T h)
+      {
+        _x1 = x;
+        _x2 = x + w;
+        _y1 = y;
+        _y2 = y + h;
+      }
+
+      T x() const { return _x1; }
+      T y() const { return _y1; }
+      T width() const { return _x2 - _x1; }
+      T height() const { return _y2 - _y1; }
+
+      void setX(T x) { T w = width(); _x1 = x; _x2 = x + w; }
+      void setY(T y) { T h = height(); _y1 = y; _y2 = y + h; }
+      void setWidth(T w) { _x2 = _x1 + w; }
+      void setHeight(T h) { _y2 = _y1 + h; }
+
+      T l() const { return _x1; }
+      T r() const { return _x2; }
+      T t() const { return _y1; }
+      T b() const { return _y2; }
+
+      void setLeft(T l) { _x1 = l; }
+      void setRight(T r) { _x2 = r; }
+      void setTop(T t) { _y1 = t; }
+      void setBottom(T b) { _y2 = b; }
+
+      bool contains(T x, T y) const
+      {
+        return _x1 <= x && x <= _x2
+            && _y1 <= y && y <= _y2;
+      }
+
+    private:
+      T _x1, _x2, _y1, _y2;
+  };
+
+} // namespace simgear
+
+#endif /* SG_RECT_HXX_ */
index d07b6718b714f2e18a793b532fde310a3cf54d02..8ef0f2e30a56dbe201fbbe8a5e539b49edaed5d2 100644 (file)
@@ -4,6 +4,7 @@ include (SimGearComponent)
 set(HEADERS 
     ResourceManager.hxx
     interpolator.hxx
+    parse_color.hxx
     sg_dir.hxx
     sg_path.hxx
     sgstream.hxx
@@ -19,6 +20,7 @@ set(HEADERS
 set(SOURCES 
     ResourceManager.cxx
     interpolator.cxx
+    parse_color.cxx
     sg_dir.cxx
     sg_path.cxx
     sgstream.cxx
@@ -39,6 +41,10 @@ add_executable(test_strings strutils_test.cxx )
 add_test(test_strings ${EXECUTABLE_OUTPUT_PATH}/test_strings)
 target_link_libraries(test_strings SimGearCore)
 
+add_executable(test_parse_color parse_color_test.cxx )
+add_test(test_parse_color ${EXECUTABLE_OUTPUT_PATH}/test_parse_color)
+target_link_libraries(test_parse_color SimGearCore)
+
 add_executable(test_path path_test.cxx )
 add_test(test_path ${EXECUTABLE_OUTPUT_PATH}/test_path)
 target_link_libraries(test_path SimGearCore)
diff --git a/simgear/misc/parse_color.cxx b/simgear/misc/parse_color.cxx
new file mode 100644 (file)
index 0000000..722ccfa
--- /dev/null
@@ -0,0 +1,90 @@
+// Parse CSS colors
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#include "parse_color.hxx"
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/trim.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/tokenizer.hpp>
+
+namespace simgear
+{
+
+  //----------------------------------------------------------------------------
+  bool parseColor(std::string str, osg::Vec4& result)
+  {
+    boost::trim(str);
+    osg::Vec4 color(0,0,0,1);
+
+    if( str.empty() )
+      return false;
+
+    // #rrggbb
+    if( str[0] == '#' )
+    {
+      const int offsets[] = {2,2,2};
+      const boost::offset_separator hex_separator( boost::begin(offsets),
+                                                   boost::end(offsets) );
+      typedef boost::tokenizer<boost::offset_separator> offset_tokenizer;
+      offset_tokenizer tokens(str.begin() + 1, str.end(), hex_separator);
+
+      int comp = 0;
+      for( offset_tokenizer::const_iterator tok = tokens.begin();
+           tok != tokens.end() && comp < 4;
+           ++tok, ++comp )
+      {
+        color._v[comp] = strtol(std::string(*tok).c_str(), 0, 16) / 255.f;
+      }
+    }
+    // rgb(r,g,b)
+    // rgba(r,g,b,a)
+    else if( boost::ends_with(str, ")") )
+    {
+      const std::string RGB = "rgb(",
+                        RGBA = "rgba(";
+      size_t pos;
+      if( boost::starts_with(str, RGB) )
+        pos = RGB.length();
+      else if( boost::starts_with(str, RGBA) )
+        pos = RGBA.length();
+      else
+        return false;
+
+      typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+      const boost::char_separator<char> del(", \t\n");
+
+      tokenizer tokens(str.begin() + pos, str.end() - 1, del);
+      int comp = 0;
+      for( tokenizer::const_iterator tok = tokens.begin();
+           tok != tokens.end() && comp < 4;
+           ++tok, ++comp )
+      {
+        color._v[comp] = boost::lexical_cast<float>(*tok)
+                       // rgb = [0,255], a = [0,1]
+                       / (comp < 3 ? 255 : 1);
+      }
+    }
+    else
+      return false;
+
+    result = color;
+    return true;
+  }
+
+} // namespace simgear
diff --git a/simgear/misc/parse_color.hxx b/simgear/misc/parse_color.hxx
new file mode 100644 (file)
index 0000000..d4ecad3
--- /dev/null
@@ -0,0 +1,40 @@
+// Parse CSS colors
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
+#ifndef PARSE_COLOR_HXX_
+#define PARSE_COLOR_HXX_
+
+#include <osg/Vec4>
+#include <string>
+
+namespace simgear
+{
+
+  /**
+   * Parse a (CSS) color
+   *
+   * @param str     Text to parse
+   * @param result  Output for parse color
+   *
+   * @return Whether str contained a valid color (and result has been modified)
+   */
+  bool parseColor(std::string str, osg::Vec4& result);
+
+} // namespace simgear
+
+#endif /* PARSE_COLOR_HXX_ */
diff --git a/simgear/misc/parse_color_test.cxx b/simgear/misc/parse_color_test.cxx
new file mode 100644 (file)
index 0000000..c41b264
--- /dev/null
@@ -0,0 +1,35 @@
+#include <simgear/compiler.h>
+
+#include "parse_color.hxx"
+
+#include <iostream>
+
+#define COMPARE(a, b) \
+  if( (a) != (b) ) \
+  { \
+    std::cerr << "failed:" << #a << " != " << #b << std::endl; \
+    return 1; \
+  }
+
+#define VERIFY(a) \
+  if( !(a) ) \
+  { \
+    std::cerr << "failed:" << #a << std::endl; \
+    return 1; \
+  }
+
+#define VERIFY_COLOR(str, r, g, b, a) \
+  VERIFY(simgear::parseColor(str, color)) \
+  COMPARE(color, osg::Vec4(r, g, b, a))
+    
+int main (int ac, char ** av)
+{
+  osg::Vec4 color;
+  VERIFY_COLOR("#ff0000", 1,0,0,1);
+  VERIFY_COLOR("#00ff00", 0,1,0,1);
+  VERIFY_COLOR("#0000ff", 0,0,1,1);
+  VERIFY_COLOR("rgb( 255,\t127.5,0)", 1, 0.5, 0, 1);
+  VERIFY_COLOR("rgba(255,  127.5,0, 0.5)", 1, 0.5, 0, 0.5);
+  std::cout << "all tests passed successfully!" << std::endl;
+  return 0;
+}
index 88a0ad2ce52c7bec7a980e1a06a3f44457f54b74..b1667e8c80623a85ae13a0b5cf7cd5244e261ac4 100644 (file)
@@ -12,9 +12,9 @@
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 // Library General Public License for more details.
 //
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
 
 #include "PropertyBasedElement.hxx"
 
@@ -46,4 +46,10 @@ namespace simgear
     return _node;
   }
 
+  //----------------------------------------------------------------------------
+  void PropertyBasedElement::setSelf(const PropertyBasedElementPtr& self)
+  {
+    _self = self;
+  }
+
 } // namespace simgear
index 23558e7090fc0e93a935631e71f32e2d6eeae1da..05dbcd1bae15d006d744bbf0929a29585e3d2028 100644 (file)
@@ -12,9 +12,9 @@
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 // Library General Public License for more details.
 //
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
 
 #ifndef SG_PROPERTY_BASED_ELEMENT_HXX_
 #define SG_PROPERTY_BASED_ELEMENT_HXX_
@@ -47,9 +47,9 @@ namespace simgear
       SGConstPropertyNode_ptr getProps() const;
       SGPropertyNode_ptr getProps();
 
-    protected:
+      virtual void setSelf(const PropertyBasedElementPtr& self);
 
-      friend class PropertyBasedMgr;
+    protected:
 
       SGPropertyNode_ptr _node;
       PropertyBasedElementWeakPtr _self;
index 893eb34b9f0e59b05a8ee45f805e3edba1ec9fb3..397aa2b6223a94ad81d78c38f4dd7eb382ed70e4 100644 (file)
@@ -12,9 +12,9 @@
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 // Library General Public License for more details.
 //
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
 
 #include "PropertyBasedMgr.hxx"
 
@@ -75,7 +75,7 @@ namespace simgear
       );
 
     PropertyBasedElementPtr el = _element_factory(child);
-    el->_self = el;
+    el->setSelf( el );
     _elements[index] = el;
     elementCreated( el );
   }
index bb189bbc31d9623105a542da37dbbe7f81af00ab..6246822a2a386ab3f12e0da8a4466d0676173b3d 100644 (file)
@@ -12,9 +12,9 @@
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 // Library General Public License for more details.
 //
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
 
 #ifndef SG_PROPERTY_BASED_MGR_HXX_
 #define SG_PROPERTY_BASED_MGR_HXX_
index 6a0f1f93785c541cf79b3308706837203535fd57..f2692710de76cf13d9043d68c8132d8a9944b9d1 100644 (file)
@@ -1162,6 +1162,18 @@ public:
   T getValue(typename boost::disable_if_c<simgear::props::PropertyTraits<T>::Internal>
              ::type* dummy = 0) const;
 
+  /**
+   * Get a list of values from all children with the given name
+   */
+  template<typename T, typename T_get /* = T */> // TODO use C++11 or traits
+  std::vector<T> getChildValues(const std::string& name) const;
+
+  /**
+   * Get a list of values from all children with the given name
+   */
+  template<typename T>
+  std::vector<T> getChildValues(const std::string& name) const;
+
   /**
    * Set a bool value for this node.
    */
@@ -1846,6 +1858,25 @@ inline T SGPropertyNode::getValue(typename boost::enable_if_c<simgear::props
   return ::getValue<T>(this);
 }
 
+template<typename T, typename T_get /* = T */> // TODO use C++11 or traits
+std::vector<T> SGPropertyNode::getChildValues(const std::string& name) const
+{
+  const simgear::PropertyList& props = getChildren(name);
+  std::vector<T> values( props.size() );
+
+  for( size_t i = 0; i < props.size(); ++i )
+    values[i] = props[i]->getValue<T_get>();
+
+  return values;
+}
+
+template<typename T>
+inline
+std::vector<T> SGPropertyNode::getChildValues(const std::string& name) const
+{
+  return getChildValues<T, T>(name);
+}
+
 template<typename T>
 bool SGPropertyNode::setValue(const T& val,
                               typename boost::disable_if_c<simgear::props