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}")
--- /dev/null
+// 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
--- /dev/null
+// 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_ */
--- /dev/null
+// 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
--- /dev/null
+// 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_ */
--- /dev/null
+// 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
--- /dev/null
+// 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_ */
--- /dev/null
+// 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_ */
--- /dev/null
+// 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_ */
// 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
// 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;
namespace simgear
{
+namespace canvas
+{
+
/**
* Owner Drawn Gauge (aka render-to-texture) helper class
*/
{
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.
*
// Real initialization function. Bad name.
void allocRT(osg::NodeCallback* camera_cull_callback = 0);
- private:
+ protected:
+
+ SystemAdapterPtr _system_adapter;
int _size_x,
_size_y,
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
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
// 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_
--- /dev/null
+// 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_ */
--- /dev/null
+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
--- /dev/null
+// 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
--- /dev/null
+// 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_ */
--- /dev/null
+// 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
--- /dev/null
+// 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_ */
--- /dev/null
+// 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
--- /dev/null
+// 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_ */
--- /dev/null
+// 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
--- /dev/null
+// 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_ */
--- /dev/null
+// 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
--- /dev/null
+// 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_ */
--- /dev/null
+// 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
--- /dev/null
+// 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_ */
--- /dev/null
+// 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_ */
--- /dev/null
+// 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_ */
--- /dev/null
+// 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")
include (SimGearComponent)
-set(HEADERS
+set(HEADERS
+ Rect.hxx
SGBox.hxx
SGCMath.hxx
SGGeoc.hxx
--- /dev/null
+// 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_ */
set(HEADERS
ResourceManager.hxx
interpolator.hxx
+ parse_color.hxx
sg_dir.hxx
sg_path.hxx
sgstream.hxx
set(SOURCES
ResourceManager.cxx
interpolator.cxx
+ parse_color.cxx
sg_dir.cxx
sg_path.cxx
sgstream.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)
--- /dev/null
+// 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
--- /dev/null
+// 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_ */
--- /dev/null
+#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;
+}
// 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"
return _node;
}
+ //----------------------------------------------------------------------------
+ void PropertyBasedElement::setSelf(const PropertyBasedElementPtr& self)
+ {
+ _self = self;
+ }
+
} // namespace simgear
// 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_
SGConstPropertyNode_ptr getProps() const;
SGPropertyNode_ptr getProps();
- protected:
+ virtual void setSelf(const PropertyBasedElementPtr& self);
- friend class PropertyBasedMgr;
+ protected:
SGPropertyNode_ptr _node;
PropertyBasedElementWeakPtr _self;
// 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"
);
PropertyBasedElementPtr el = _element_factory(child);
- el->_self = el;
+ el->setSelf( el );
_elements[index] = el;
elementCreated( el );
}
// 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_
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.
*/
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