From b99f53fda3b06378668bdccd4a9d07161f263366 Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Sun, 4 Nov 2012 14:12:05 +0100 Subject: [PATCH] Refactor Canvas and add some helpers. - Refactor Canvas from FlightGear and fix lazy rendering with recursive canvases. - New classes PropertyBasedElement and PropertyBasedManager for creating subsystems controlled by the property tree. - New method parseColor to parse CSS 3 conformant color strings (only basic color schemes supported) - New Rect class representing a rectangle. --- simgear/canvas/CMakeLists.txt | 21 +- simgear/canvas/Canvas.cxx | 502 ++++++++++++++++++ simgear/canvas/Canvas.hxx | 180 +++++++ simgear/canvas/CanvasMgr.cxx | 65 +++ simgear/canvas/CanvasMgr.hxx | 61 +++ simgear/canvas/CanvasPlacement.cxx | 39 ++ simgear/canvas/CanvasPlacement.hxx | 41 ++ simgear/canvas/CanvasSystemAdapter.hxx | 43 ++ simgear/canvas/MouseEvent.hxx | 60 +++ simgear/canvas/ODGauge.cxx | 428 +++++++-------- simgear/canvas/ODGauge.hxx | 44 +- simgear/canvas/ShivaVG/src/CMakeLists.txt | 6 +- simgear/canvas/VGInitOperation.hxx | 6 +- simgear/canvas/canvas_fwd.hxx | 58 ++ simgear/canvas/elements/CMakeLists.txt | 21 + simgear/canvas/elements/CanvasElement.cxx | 304 +++++++++++ simgear/canvas/elements/CanvasElement.hxx | 178 +++++++ simgear/canvas/elements/CanvasGroup.cxx | 219 ++++++++ simgear/canvas/elements/CanvasGroup.hxx | 67 +++ simgear/canvas/elements/CanvasImage.cxx | 352 ++++++++++++ simgear/canvas/elements/CanvasImage.hxx | 89 ++++ simgear/canvas/elements/CanvasMap.cxx | 230 ++++++++ simgear/canvas/elements/CanvasMap.hxx | 82 +++ simgear/canvas/elements/CanvasPath.cxx | 439 +++++++++++++++ simgear/canvas/elements/CanvasPath.hxx | 57 ++ simgear/canvas/elements/CanvasText.cxx | 291 ++++++++++ simgear/canvas/elements/CanvasText.hxx | 59 ++ simgear/canvas/elements/map/geo_node_pair.hxx | 134 +++++ simgear/canvas/elements/map/projection.hxx | 179 +++++++ simgear/canvas/elements/text-alignment.hxx | 41 ++ simgear/math/CMakeLists.txt | 3 +- simgear/math/Rect.hxx | 85 +++ simgear/misc/CMakeLists.txt | 6 + simgear/misc/parse_color.cxx | 90 ++++ simgear/misc/parse_color.hxx | 40 ++ simgear/misc/parse_color_test.cxx | 35 ++ simgear/props/PropertyBasedElement.cxx | 12 +- simgear/props/PropertyBasedElement.hxx | 10 +- simgear/props/PropertyBasedMgr.cxx | 8 +- simgear/props/PropertyBasedMgr.hxx | 6 +- simgear/props/props.hxx | 31 ++ 41 files changed, 4366 insertions(+), 256 deletions(-) create mode 100644 simgear/canvas/Canvas.cxx create mode 100644 simgear/canvas/Canvas.hxx create mode 100644 simgear/canvas/CanvasMgr.cxx create mode 100644 simgear/canvas/CanvasMgr.hxx create mode 100644 simgear/canvas/CanvasPlacement.cxx create mode 100644 simgear/canvas/CanvasPlacement.hxx create mode 100644 simgear/canvas/CanvasSystemAdapter.hxx create mode 100644 simgear/canvas/MouseEvent.hxx create mode 100644 simgear/canvas/canvas_fwd.hxx create mode 100644 simgear/canvas/elements/CMakeLists.txt create mode 100644 simgear/canvas/elements/CanvasElement.cxx create mode 100644 simgear/canvas/elements/CanvasElement.hxx create mode 100644 simgear/canvas/elements/CanvasGroup.cxx create mode 100644 simgear/canvas/elements/CanvasGroup.hxx create mode 100644 simgear/canvas/elements/CanvasImage.cxx create mode 100644 simgear/canvas/elements/CanvasImage.hxx create mode 100644 simgear/canvas/elements/CanvasMap.cxx create mode 100644 simgear/canvas/elements/CanvasMap.hxx create mode 100644 simgear/canvas/elements/CanvasPath.cxx create mode 100644 simgear/canvas/elements/CanvasPath.hxx create mode 100644 simgear/canvas/elements/CanvasText.cxx create mode 100644 simgear/canvas/elements/CanvasText.hxx create mode 100644 simgear/canvas/elements/map/geo_node_pair.hxx create mode 100644 simgear/canvas/elements/map/projection.hxx create mode 100644 simgear/canvas/elements/text-alignment.hxx create mode 100644 simgear/math/Rect.hxx create mode 100644 simgear/misc/parse_color.cxx create mode 100644 simgear/misc/parse_color.hxx create mode 100644 simgear/misc/parse_color_test.cxx diff --git a/simgear/canvas/CMakeLists.txt b/simgear/canvas/CMakeLists.txt index 0a10e585..169c2d65 100644 --- a/simgear/canvas/CMakeLists.txt +++ b/simgear/canvas/CMakeLists.txt @@ -1,14 +1,25 @@ include (SimGearComponent) set(HEADERS - ODGauge.hxx - VGInitOperation.hxx - ) + canvas_fwd.hxx + Canvas.hxx + CanvasMgr.hxx + CanvasPlacement.hxx + CanvasSystemAdapter.hxx + MouseEvent.hxx + ODGauge.hxx + VGInitOperation.hxx +) set(SOURCES - ODGauge.cxx - ) + Canvas.cxx + CanvasMgr.cxx + CanvasPlacement.cxx + ODGauge.cxx +) add_subdirectory(ShivaVG/src) +include_directories(ShivaVG/include) +add_subdirectory(elements) simgear_scene_component(canvas canvas "${SOURCES}" "${HEADERS}") diff --git a/simgear/canvas/Canvas.cxx b/simgear/canvas/Canvas.cxx new file mode 100644 index 00000000..acb8da88 --- /dev/null +++ b/simgear/canvas/Canvas.cxx @@ -0,0 +1,502 @@ +// The canvas for rendering with the 2d API +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +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(_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(_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(_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(_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(self); + + _root_group.reset( new Group(canvas, _node) ); + + // Remove automatically created property listener as we forward them on our + // own + _root_group->removeListener(); + + _cull_callback = new CullCallback(canvas); + } + + //---------------------------------------------------------------------------- + void Canvas::setStatusFlags(unsigned int flags, bool set) + { + if( set ) + _status = _status | flags; + else + _status = _status & ~flags; + // TODO maybe extend simgear::PropertyObject to allow |=, &= etc. + + if( (_status & MISSING_SIZE_X) && (_status & MISSING_SIZE_Y) ) + _status_msg = "Missing size"; + else if( _status & MISSING_SIZE_X ) + _status_msg = "Missing size-x"; + else if( _status & MISSING_SIZE_Y ) + _status_msg = "Missing size-y"; + else if( _status & CREATE_FAILED ) + _status_msg = "Creating render target failed"; + else if( _status == STATUS_OK && !_texture.serviceable() ) + _status_msg = "Creation pending..."; + else + _status_msg = "Ok"; + } + + //---------------------------------------------------------------------------- + Canvas::PlacementFactoryMap Canvas::_placement_factories; + +} // namespace canvas +} // namespace simgear diff --git a/simgear/canvas/Canvas.hxx b/simgear/canvas/Canvas.hxx new file mode 100644 index 00000000..7f0ffb88 --- /dev/null +++ b/simgear/canvas/Canvas.hxx @@ -0,0 +1,180 @@ +// The canvas for rendering with the 2d API +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 +#include +#include +#include +#include + +#include +#include + +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 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 _status; + PropertyObject _status_msg; + + PropertyObject _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 _root_group; + + CullCallbackPtr _cull_callback; + bool _render_always; // _dirty_placements; + std::vector _placements; + std::set _dependent_canvases; // PlacementFactoryMap; + static PlacementFactoryMap _placement_factories; + + virtual void setSelf(const PropertyBasedElementPtr& self); + void setStatusFlags(unsigned int flags, bool set = true); + + private: + + Canvas(const Canvas&); // = delete; + Canvas& operator=(const Canvas&); // = delete; + }; + +} // namespace canvas +} // namespace simgear + +#endif /* CANVAS_HXX_ */ diff --git a/simgear/canvas/CanvasMgr.cxx b/simgear/canvas/CanvasMgr.cxx new file mode 100644 index 00000000..ff39bd8e --- /dev/null +++ b/simgear/canvas/CanvasMgr.cxx @@ -0,0 +1,65 @@ +// Canvas with 2D rendering API +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 + +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(_elements[index]); + } + + //---------------------------------------------------------------------------- + void CanvasMgr::elementCreated(PropertyBasedElementPtr element) + { + CanvasPtr canvas = boost::static_pointer_cast(element); + canvas->setSystemAdapter(_system_adapter); + canvas->setCanvasMgr(this); + } + +} // namespace canvas +} // namespace simgear diff --git a/simgear/canvas/CanvasMgr.hxx b/simgear/canvas/CanvasMgr.hxx new file mode 100644 index 00000000..5e51d2df --- /dev/null +++ b/simgear/canvas/CanvasMgr.hxx @@ -0,0 +1,61 @@ +// Canvas with 2D rendering API +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 + +namespace simgear +{ +namespace canvas +{ + + class CanvasMgr: + public PropertyBasedMgr + { + public: + + /** + * @param node Root node of branch used to control canvasses + * @param system_adapter Adapter for connecting between canvas and + * application framework + * + */ + CanvasMgr( SGPropertyNode_ptr node, + SystemAdapterPtr system_adapter ); + + /** + * Get ::Canvas by index + * + * @param index Index of texture node in /canvas/by-index/ + */ + CanvasPtr getCanvas(size_t index) const; + + protected: + + SystemAdapterPtr _system_adapter; + + virtual void elementCreated(PropertyBasedElementPtr element); + }; + +} // namespace canvas +} // namespace simgear + +#endif /* SG_CANVAS_MGR_H_ */ diff --git a/simgear/canvas/CanvasPlacement.cxx b/simgear/canvas/CanvasPlacement.cxx new file mode 100644 index 00000000..3024f302 --- /dev/null +++ b/simgear/canvas/CanvasPlacement.cxx @@ -0,0 +1,39 @@ +// Base class for canvas placements +// +// Copyright (C) 2012 Thomas Geymayer +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#include "CanvasPlacement.hxx" + +namespace simgear +{ +namespace canvas +{ + + //---------------------------------------------------------------------------- + Placement::Placement() + { + + } + + //---------------------------------------------------------------------------- + Placement::~Placement() + { + + } + +} // namespace canvas +} // namespace simgear diff --git a/simgear/canvas/CanvasPlacement.hxx b/simgear/canvas/CanvasPlacement.hxx new file mode 100644 index 00000000..05d85ba3 --- /dev/null +++ b/simgear/canvas/CanvasPlacement.hxx @@ -0,0 +1,41 @@ +// Base class for canvas placements +// +// Copyright (C) 2012 Thomas Geymayer +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef CANVAS_PLACEMENT_HXX_ +#define CANVAS_PLACEMENT_HXX_ + +namespace simgear +{ +namespace canvas +{ + + class Placement + { + public: + Placement(); + virtual ~Placement() = 0; + + private: + Placement(const Placement&) /* = delete */; + Placement& operator=(const Placement&) /* = delete */; + }; + +} // namespace canvas +} // namespace simgear + +#endif /* CANVAS_PLACEMENT_HXX_ */ diff --git a/simgear/canvas/CanvasSystemAdapter.hxx b/simgear/canvas/CanvasSystemAdapter.hxx new file mode 100644 index 00000000..91fc2740 --- /dev/null +++ b/simgear/canvas/CanvasSystemAdapter.hxx @@ -0,0 +1,43 @@ +// Adapter for using the canvas with different applications +// +// Copyright (C) 2012 Thomas Geymayer +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef SG_CANVAS_SYSTEM_ADAPTER_HXX_ +#define SG_CANVAS_SYSTEM_ADAPTER_HXX_ + +#include "canvas_fwd.hxx" + +namespace simgear +{ +namespace canvas +{ + + class SystemAdapter + { + public: + + virtual ~SystemAdapter() {} + virtual FontPtr getFont(const std::string& name) const = 0; + virtual void addCamera(osg::Camera* camera) const = 0; + virtual void removeCamera(osg::Camera* camera) const = 0; + virtual osg::Image* getImage(const std::string& path) const = 0; + }; + +} // namespace canvas +} // namespace simgear + +#endif /* SG_CANVAS_SYSTEM_ADAPTER_HXX_ */ diff --git a/simgear/canvas/MouseEvent.hxx b/simgear/canvas/MouseEvent.hxx new file mode 100644 index 00000000..2e6a26dd --- /dev/null +++ b/simgear/canvas/MouseEvent.hxx @@ -0,0 +1,60 @@ +// Mouse event +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 + +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, // #endif +#include "ODGauge.hxx" +#include "CanvasSystemAdapter.hxx" + +#include +#include + #include #include #include @@ -40,237 +44,243 @@ #include #include // for GL_DEPTH_STENCIL_EXT on Windows -#include -#include -#include "ODGauge.hxx" - #include 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 diff --git a/simgear/canvas/ODGauge.hxx b/simgear/canvas/ODGauge.hxx index 64efa84e..5a07d69e 100644 --- a/simgear/canvas/ODGauge.hxx +++ b/simgear/canvas/ODGauge.hxx @@ -10,30 +10,28 @@ // 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 #include -#include - namespace osg { class Camera; @@ -42,6 +40,9 @@ namespace osg namespace simgear { +namespace canvas +{ + /** * Owner Drawn Gauge (aka render-to-texture) helper class */ @@ -49,12 +50,11 @@ namespace simgear { public: - typedef boost::function CameraRegistrationCallback; - - ODGauge( const CameraRegistrationCallback& cb_camera_add, - const CameraRegistrationCallback& cb_camera_remove ); + ODGauge(); virtual ~ODGauge(); + void setSystemAdapter(const SystemAdapterPtr& system_adapter); + /** * Set the size of the render target. * @@ -124,7 +124,9 @@ namespace simgear // Real initialization function. Bad name. void allocRT(osg::NodeCallback* camera_cull_callback = 0); - private: + protected: + + SystemAdapterPtr _system_adapter; int _size_x, _size_y, @@ -142,15 +144,13 @@ namespace simgear osg::ref_ptr camera; osg::ref_ptr texture; - CameraRegistrationCallback _cb_cam_add, - _cb_cam_remove; - void updateCoordinateFrame(); void updateStencil(); void updateSampling(); }; +} // namespace canvas } // namespace simgear #endif // _SG_OD_GAUGE_HXX diff --git a/simgear/canvas/ShivaVG/src/CMakeLists.txt b/simgear/canvas/ShivaVG/src/CMakeLists.txt index 63dcdef7..7b9bc785 100644 --- a/simgear/canvas/ShivaVG/src/CMakeLists.txt +++ b/simgear/canvas/ShivaVG/src/CMakeLists.txt @@ -45,4 +45,8 @@ TARGET_LINK_LIBRARIES( ShivaVG ${OPENGL_gl_LIBRARY} ${OPENGL_glu_LIBRARY} -) \ No newline at end of file +) + +if(NOT SIMGEAR_HEADLESS) + install(TARGETS ShivaVG ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +endif() \ No newline at end of file diff --git a/simgear/canvas/VGInitOperation.hxx b/simgear/canvas/VGInitOperation.hxx index 9fb650cb..b18ace17 100644 --- a/simgear/canvas/VGInitOperation.hxx +++ b/simgear/canvas/VGInitOperation.hxx @@ -12,9 +12,9 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA #ifndef CANVAS_VG_INITOPERATION_HXX_ #define CANVAS_VG_INITOPERATION_HXX_ diff --git a/simgear/canvas/canvas_fwd.hxx b/simgear/canvas/canvas_fwd.hxx new file mode 100644 index 00000000..2588460a --- /dev/null +++ b/simgear/canvas/canvas_fwd.hxx @@ -0,0 +1,58 @@ +// Canvas forward declarations +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 +#include + +#include +#include + +#include +#include +#include + +#include + +namespace simgear +{ +namespace canvas +{ + + class Canvas; + typedef boost::shared_ptr CanvasPtr; + typedef boost::weak_ptr CanvasWeakPtr; + + typedef osg::ref_ptr FontPtr; + + class Placement; + typedef boost::shared_ptr PlacementPtr; + typedef std::vector Placements; + typedef boost::function PlacementFactory; + + class SystemAdapter; + typedef boost::shared_ptr SystemAdapterPtr; + +} // namespace canvas +} // namespace simgear + + +#endif /* SG_CANVAS_FWD_HXX_ */ diff --git a/simgear/canvas/elements/CMakeLists.txt b/simgear/canvas/elements/CMakeLists.txt new file mode 100644 index 00000000..6e5c2e93 --- /dev/null +++ b/simgear/canvas/elements/CMakeLists.txt @@ -0,0 +1,21 @@ +include (SimGearComponent) + +set(HEADERS + CanvasElement.hxx + CanvasGroup.hxx + CanvasImage.hxx + CanvasMap.hxx + CanvasPath.hxx + CanvasText.hxx +) + +set(SOURCES + CanvasElement.cxx + CanvasGroup.cxx + CanvasImage.cxx + CanvasMap.cxx + CanvasPath.cxx + CanvasText.cxx +) + +simgear_scene_component(canvas-elements canvas/elements "${SOURCES}" "${HEADERS}") \ No newline at end of file diff --git a/simgear/canvas/elements/CanvasElement.cxx b/simgear/canvas/elements/CanvasElement.cxx new file mode 100644 index 00000000..e81cc206 --- /dev/null +++ b/simgear/canvas/elements/CanvasElement.cxx @@ -0,0 +1,304 @@ +// Interface for 2D Canvas element +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 + +#include +#include + +#include + +#include +#include + +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 Element::getMatrixTransform() + { + return _transform; + } + + //---------------------------------------------------------------------------- + void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child) + { + if( parent == _node + && child->getNameString() == NAME_TRANSFORM ) + { + if( child->getIndex() >= static_cast(_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(_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(_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 geode = new osg::Geode; + geode->addDrawable(_drawable); + _transform->addChild(geode); + } + + //---------------------------------------------------------------------------- + void Element::setupStyle() + { + BOOST_FOREACH( Style::value_type style, _style ) + setStyle(style.second); + } + + //---------------------------------------------------------------------------- + bool Element::setStyle(const SGPropertyNode* child) + { + StyleSetters::const_iterator setter = + _style_setters.find(child->getNameString()); + if( setter == _style_setters.end() ) + return false; + + setter->second(child); + return true; + } + +} // namespace canvas +} // namespace simgear diff --git a/simgear/canvas/elements/CanvasElement.hxx b/simgear/canvas/elements/CanvasElement.hxx new file mode 100644 index 00000000..a079cd6b --- /dev/null +++ b/simgear/canvas/elements/CanvasElement.hxx @@ -0,0 +1,178 @@ +// Interface for 2D Canvas elements +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 +#include +#include // for uint32_t +#include +#include + +#include +#include + +namespace osg +{ + class Drawable; +} + +namespace simgear +{ +namespace canvas +{ + + class MouseEvent; + class Element: + public SGPropertyChangeListener + { + public: + typedef std::map Style; + typedef boost::function StyleSetter; + typedef std::map 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 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 _transform; + std::vector _transform_types; + + SGPropertyNode_ptr _node; + Style _style; + StyleSetters _style_setters; + std::vector _bounding_box; + + Element( const CanvasWeakPtr& canvas, + SGPropertyNode_ptr node, + const Style& parent_style ); + + template + Element::StyleSetter + addStyle(const std::string& name, void (C1::*setter)(T), C2 instance) + { + return _style_setters[ name ] = + bindStyleSetter(name, setter, instance); + } + + template + Element::StyleSetter + addStyle(const std::string& name, void (C1::*setter)(T2), C2 instance) + { + return _style_setters[ name ] = + bindStyleSetter(name, setter, instance); + } + + template + Element::StyleSetter + addStyle( const std::string& name, + void (C1::*setter)(const std::string&), + C2 instance ) + { + return _style_setters[ name ] = + bindStyleSetter(name, setter, instance); + } + + template + Element::StyleSetter + bindStyleSetter( const std::string& name, + void (C1::*setter)(T2), + C2 instance ) + { + return boost::bind(setter, instance, boost::bind(&getValue, _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 _drawable; + + Element(const Element&);// = delete + }; + +} // namespace canvas +} // namespace simgear + +#endif /* CANVAS_ELEMENT_HXX_ */ diff --git a/simgear/canvas/elements/CanvasGroup.cxx b/simgear/canvas/elements/CanvasGroup.cxx new file mode 100644 index 00000000..ff4be60f --- /dev/null +++ b/simgear/canvas/elements/CanvasGroup.cxx @@ -0,0 +1,219 @@ +// A group of 2D Canvas elements +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 + +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; + + // TODO create map of child factories and use also to check for element + // on deletion in ::childRemoved + if( child->getNameString() == "text" ) + element.reset( new Text(_canvas, child, _style) ); + else if( child->getNameString() == "group" ) + element.reset( new Group(_canvas, child, _style) ); + else if( child->getNameString() == "map" ) + element.reset( new Map(_canvas, child, _style) ); + else if( child->getNameString() == "path" ) + element.reset( new Path(_canvas, child, _style) ); + else if( child->getNameString() == "image" ) + element.reset( new Image(_canvas, child, _style) ); + + if( element ) + { + // Add to osg scene graph... + _transform->addChild( element->getMatrixTransform() ); + _children.push_back( ChildList::value_type(child, element) ); + return; + } + + _style[ child->getNameString() ] = child; + } + + //---------------------------------------------------------------------------- + struct ChildFinder + { + public: + ChildFinder(SGPropertyNode *node): + _node(node) + {} + + bool operator()(const Group::ChildList::value_type& el) const + { + return el.first == _node; + } + + private: + SGPropertyNode *_node; + }; + + //---------------------------------------------------------------------------- + void Group::childRemoved(SGPropertyNode* node) + { + if( node->getParent() != _node ) + return; + + if( node->getNameString() == "text" + || node->getNameString() == "group" + || node->getNameString() == "map" + || node->getNameString() == "path" + || node->getNameString() == "image" ) + { + ChildFinder pred(node); + ChildList::iterator child = + std::find_if(_children.begin(), _children.end(), pred); + + if( child == _children.end() ) + SG_LOG + ( + SG_GL, + SG_WARN, + "can't removed unknown child " << node->getDisplayName() + ); + else + { + _transform->removeChild( child->second->getMatrixTransform() ); + _children.erase(child); + } + } + else + { + Style::iterator style = _style.find(node->getNameString()); + if( style != _style.end() ) + _style.erase(style); + } + } + + //---------------------------------------------------------------------------- + void Group::childChanged(SGPropertyNode* node) + { + if( node->getParent()->getParent() == _node + && node->getNameString() == "z-index" ) + return handleZIndexChanged(node->getParent(), node->getIntValue()); + } + + //---------------------------------------------------------------------------- + void Group::handleZIndexChanged(SGPropertyNode* node, int z_index) + { + ChildFinder pred(node); + ChildList::iterator child = + std::find_if(_children.begin(), _children.end(), pred); + + if( child == _children.end() ) + return; + + osg::Node* tf = child->second->getMatrixTransform(); + int index = _transform->getChildIndex(tf), + index_new = index; + + ChildList::iterator next = child; + ++next; + + while( next != _children.end() + && next->first->getIntValue("z-index", 0) <= z_index ) + { + ++index_new; + ++next; + } + + if( index_new != index ) + { + _children.insert(next, *child); + } + else + { + ChildList::iterator prev = child; + while( prev != _children.begin() + && (--prev)->first->getIntValue("z-index", 0) > z_index) + { + --index_new; + } + + if( index == index_new ) + return; + + _children.insert(prev, *child); + } + + _transform->removeChild(index); + _transform->insertChild(index_new, tf); + + _children.erase(child); + + SG_LOG + ( + SG_GENERAL, + SG_INFO, + "canvas::Group: Moved element " << index << " to position " << index_new + ); + } + +} // namespace canvas +} // namespace simgear diff --git a/simgear/canvas/elements/CanvasGroup.hxx b/simgear/canvas/elements/CanvasGroup.hxx new file mode 100644 index 00000000..d57cea71 --- /dev/null +++ b/simgear/canvas/elements/CanvasGroup.hxx @@ -0,0 +1,67 @@ +// A group of 2D Canvas elements +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 +#include +#include + +namespace simgear +{ +namespace canvas +{ + + typedef boost::shared_ptr ElementPtr; + + class Group: + public Element + { + public: + typedef std::list< std::pair< const SGPropertyNode*, + ElementPtr + > + > ChildList; + + Group( const CanvasWeakPtr& canvas, + SGPropertyNode_ptr node, + const Style& parent_style = Style() ); + virtual ~Group(); + + virtual void update(double dt); + + protected: + + ChildList _children; + + virtual bool handleLocalMouseEvent(const canvas::MouseEvent& event); + + virtual void childAdded(SGPropertyNode * child); + virtual void childRemoved(SGPropertyNode * child); + virtual void childChanged(SGPropertyNode * child); + + void handleZIndexChanged(SGPropertyNode* node, int z_index); + }; + +} // namespace canvas +} // namespace simgear + +#endif /* CANVAS_GROUP_HXX_ */ diff --git a/simgear/canvas/elements/CanvasImage.cxx b/simgear/canvas/elements/CanvasImage.cxx new file mode 100644 index 00000000..811c6c17 --- /dev/null +++ b/simgear/canvas/elements/CanvasImage.cxx @@ -0,0 +1,352 @@ +// An image on the Canvas +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 +#include +#include +#include +#include + +#include +#include +#include + +#include + +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& 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& 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& 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 Image::getTextureDimensions() const + { + osg::Texture2D *texture = !_src_canvas.expired() + ? _src_canvas.lock()->getTexture() + : _texture.get(); + + if( !texture ) + return Rect(); + + return Rect + ( + 0,0, + texture->getTextureWidth(), + texture->getTextureHeight() + ); + } + +} // namespace canvas +} // namespace simgear diff --git a/simgear/canvas/elements/CanvasImage.hxx b/simgear/canvas/elements/CanvasImage.hxx new file mode 100644 index 00000000..700c475a --- /dev/null +++ b/simgear/canvas/elements/CanvasImage.hxx @@ -0,0 +1,89 @@ +// An image on the Canvas +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 +#include +#include + +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& 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 getTextureDimensions() const; + + osg::ref_ptr _texture; + // TODO optionally forward events to canvas + CanvasWeakPtr _src_canvas; + + osg::ref_ptr _geom; + osg::ref_ptr _vertices; + osg::ref_ptr _texCoords; + osg::ref_ptr _colors; + + SGPropertyNode *_node_src_rect; + Rect _src_rect, + _region; + }; + +} // namespace canvas +} // namespace canvas + +#endif /* CANVAS_IMAGE_HXX_ */ diff --git a/simgear/canvas/elements/CanvasMap.cxx b/simgear/canvas/elements/CanvasMap.cxx new file mode 100644 index 00000000..1145e1d0 --- /dev/null +++ b/simgear/canvas/elements/CanvasMap.cxx @@ -0,0 +1,230 @@ +// A group of 2D Canvas elements which get automatically transformed according +// to the map parameters. +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 + +#include + +#define LOG_GEO_RET(msg) \ + {\ + SG_LOG\ + (\ + SG_GENERAL,\ + SG_WARN,\ + msg << " (" << child->getStringValue()\ + << ", " << child->getPath() << ")"\ + );\ + return;\ + } + +namespace simgear +{ +namespace canvas +{ + + const std::string GEO = "-geo"; + + //---------------------------------------------------------------------------- + Map::Map( const CanvasWeakPtr& canvas, + SGPropertyNode_ptr node, + const Style& parent_style ): + Group(canvas, node, parent_style), + // TODO make projection configurable + _projection(new SansonFlamsteedProjection), + _projection_dirty(true) + { + + } + + //---------------------------------------------------------------------------- + Map::~Map() + { + + } + + //---------------------------------------------------------------------------- + void Map::update(double dt) + { + for( GeoNodes::iterator it = _geo_nodes.begin(); + it != _geo_nodes.end(); + ++it ) + { + GeoNodePair* geo_node = it->second.get(); + if( !geo_node->isComplete() + || (!geo_node->isDirty() && !_projection_dirty) ) + continue; + + GeoCoord lat = parseGeoCoord(geo_node->getLat()); + if( lat.type != GeoCoord::LATITUDE ) + continue; + + GeoCoord lon = parseGeoCoord(geo_node->getLon()); + if( lon.type != GeoCoord::LONGITUDE ) + continue; + + Projection::ScreenPosition pos = + _projection->worldToScreen(lat.value, lon.value); + + geo_node->setScreenPos(pos.x, pos.y); + +// geo_node->print(); + geo_node->setDirty(false); + } + _projection_dirty = false; + + Group::update(dt); + } + + //---------------------------------------------------------------------------- + void Map::childAdded(SGPropertyNode* parent, SGPropertyNode* child) + { + if( !boost::ends_with(child->getNameString(), GEO) ) + return Element::childAdded(parent, child); + + _geo_nodes[child].reset(new GeoNodePair()); + } + + //---------------------------------------------------------------------------- + void Map::childRemoved(SGPropertyNode* parent, SGPropertyNode* child) + { + if( !boost::ends_with(child->getNameString(), GEO) ) + return Element::childRemoved(parent, child); + + // TODO remove from other node + _geo_nodes.erase(child); + } + + //---------------------------------------------------------------------------- + void Map::valueChanged(SGPropertyNode * child) + { + const std::string& name = child->getNameString(); + + if( !boost::ends_with(name, GEO) ) + return Group::valueChanged(child); + + GeoNodes::iterator it_geo_node = _geo_nodes.find(child); + if( it_geo_node == _geo_nodes.end() ) + LOG_GEO_RET("geo node not found!") + GeoNodePair* geo_node = it_geo_node->second.get(); + + geo_node->setDirty(); + + if( geo_node->getStatus() & GeoNodePair::INCOMPLETE ) + { + // Detect lat, lon tuples... + GeoCoord coord = parseGeoCoord(child->getStringValue()); + int index_other = -1; + + switch( coord.type ) + { + case GeoCoord::LATITUDE: + index_other = child->getIndex() + 1; + geo_node->setNodeLat(child); + break; + case GeoCoord::LONGITUDE: + index_other = child->getIndex() - 1; + geo_node->setNodeLon(child); + break; + default: + LOG_GEO_RET("Invalid geo coord") + } + + SGPropertyNode *other = child->getParent()->getChild(name, index_other); + if( !other ) + return; + + GeoCoord coord_other = parseGeoCoord(other->getStringValue()); + if( coord_other.type == GeoCoord::INVALID + || coord_other.type == coord.type ) + return; + + GeoNodes::iterator it_geo_node_other = _geo_nodes.find(other); + if( it_geo_node_other == _geo_nodes.end() ) + LOG_GEO_RET("other geo node not found!") + GeoNodePair* geo_node_other = it_geo_node_other->second.get(); + + // Let use both nodes use the same GeoNodePair instance + if( geo_node_other != geo_node ) + it_geo_node_other->second = it_geo_node->second; + + if( coord_other.type == GeoCoord::LATITUDE ) + geo_node->setNodeLat(other); + else + geo_node->setNodeLon(other); + + // Set name for resulting screen coordinate nodes + geo_node->setTargetName( name.substr(0, name.length() - GEO.length()) ); + } + } + + //---------------------------------------------------------------------------- + void Map::childChanged(SGPropertyNode * child) + { + if( child->getParent() != _node ) + return Group::childChanged(child); + + if( child->getNameString() == "ref-lat" + || child->getNameString() == "ref-lon" ) + _projection->setWorldPosition( _node->getDoubleValue("ref-lat"), + _node->getDoubleValue("ref-lon") ); + else if( child->getNameString() == "hdg" ) + _projection->setOrientation(child->getFloatValue()); + else if( child->getNameString() == "range" ) + _projection->setRange(child->getDoubleValue()); + else + return Group::childChanged(child); + + _projection_dirty = true; + } + + //---------------------------------------------------------------------------- + Map::GeoCoord Map::parseGeoCoord(const std::string& val) const + { + GeoCoord coord; + if( val.length() < 2 ) + return coord; + + if( val[0] == 'N' || val[0] == 'S' ) + coord.type = GeoCoord::LATITUDE; + else if( val[0] == 'E' || val[0] == 'W' ) + coord.type = GeoCoord::LONGITUDE; + else + return coord; + + char* end; + coord.value = strtod(&val[1], &end); + + if( end != &val[val.length()] ) + { + coord.type = GeoCoord::INVALID; + return coord; + } + + if( val[0] == 'S' || val[0] == 'W' ) + coord.value *= -1; + + return coord; + } + +} // namespace canvas +} // namespace simgear diff --git a/simgear/canvas/elements/CanvasMap.hxx b/simgear/canvas/elements/CanvasMap.hxx new file mode 100644 index 00000000..518c0b2a --- /dev/null +++ b/simgear/canvas/elements/CanvasMap.hxx @@ -0,0 +1,82 @@ +// A group of 2D Canvas elements which get automatically transformed according +// to the map parameters. +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 +#include + +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 + > GeoNodes; + GeoNodes _geo_nodes; + boost::shared_ptr _projection; + bool _projection_dirty; + + struct GeoCoord + { + GeoCoord(): + type(INVALID) + {} + enum + { + INVALID, + LATITUDE, + LONGITUDE + } type; + double value; + }; + + GeoCoord parseGeoCoord(const std::string& val) const; + }; + +} // namespace canvas +} // namespace simgear + +#endif /* CANVAS_MAP_HXX_ */ diff --git a/simgear/canvas/elements/CanvasPath.cxx b/simgear/canvas/elements/CanvasPath.cxx new file mode 100644 index 00000000..ac9d6a9c --- /dev/null +++ b/simgear/canvas/elements/CanvasPath.cxx @@ -0,0 +1,439 @@ +// An OpenVG path on the Canvas +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 + +#include +#include + +#include +#include + +namespace simgear +{ +namespace canvas +{ + typedef std::vector CmdList; + typedef std::vector CoordList; + + /** + * Helper to split and convert comma/whitespace separated floating point + * values + */ + std::vector 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 _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(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("cmd"), + _node->getChildValues("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 splitAndConvert(const char del[], const std::string& str) + { + std::vector values; + size_t pos = 0; + for(;;) + { + pos = str.find_first_not_of(del, pos); + if( pos == std::string::npos ) + break; + + char *end = 0; + float val = strtod(&str[pos], &end); + if( end == &str[pos] || !end ) + break; + + values.push_back(val); + pos = end - &str[0]; + } + return values; + } + +} // namespace canvas +} // namespace simgear diff --git a/simgear/canvas/elements/CanvasPath.hxx b/simgear/canvas/elements/CanvasPath.hxx new file mode 100644 index 00000000..e61897f7 --- /dev/null +++ b/simgear/canvas/elements/CanvasPath.hxx @@ -0,0 +1,57 @@ +// An OpenVG path on the Canvas +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 _path; + + virtual void childRemoved(SGPropertyNode * child); + virtual void childChanged(SGPropertyNode * child); + }; + +} // namespace canvas +} // namespace simgear + +#endif /* CANVAS_PATH_HXX_ */ diff --git a/simgear/canvas/elements/CanvasText.cxx b/simgear/canvas/elements/CanvasText.cxx new file mode 100644 index 00000000..fdaa7895 --- /dev/null +++ b/simgear/canvas/elements/CanvasText.cxx @@ -0,0 +1,291 @@ +// A text on the Canvas +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 +#include +#include +#include + +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(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(&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("draw-mode", &TextOSG::setDrawMode, _text); + addStyle("max-width", &TextOSG::setMaximumWidth, _text); + addStyle("font", &Text::setFont, this); + addStyle("alignment", &Text::setAlignment, this); + addStyle("text", &Text::setText, this); + + setupStyle(); + } + + //---------------------------------------------------------------------------- + Text::~Text() + { + + } + + //---------------------------------------------------------------------------- + void Text::setText(const char* text) + { + _text->setText(text, osgText::String::ENCODING_UTF8); + } + + //---------------------------------------------------------------------------- + void Text::setFont(const char* name) + { + _text->setFont( _canvas.lock()->getSystemAdapter()->getFont(name) ); + } + + //---------------------------------------------------------------------------- + void Text::setAlignment(const char* align) + { + const std::string align_string(align); + if( 0 ) return; +#define ENUM_MAPPING(enum_val, string_val) \ + else if( align_string == string_val )\ + _text->setAlignment( osgText::Text::enum_val ); +#include "text-alignment.hxx" +#undef ENUM_MAPPING + else + { + if( !align_string.empty() ) + SG_LOG + ( + SG_GENERAL, + SG_WARN, + "canvas::Text: unknown alignment '" << align_string << "'" + ); + _text->setAlignment(osgText::Text::LEFT_BASE_LINE); + } + } + + //---------------------------------------------------------------------------- +#if 0 + const char* Text::getAlignment() const + { + switch( _text->getAlignment() ) + { +#define ENUM_MAPPING(enum_val, string_val) \ + case osgText::Text::enum_val:\ + return string_val; +#include "text-alignment.hxx" +#undef ENUM_MAPPING + default: + return "unknown"; + } + } +#endif + + //---------------------------------------------------------------------------- + void Text::childChanged(SGPropertyNode* child) + { + if( child->getParent() != _node ) + return; + + const std::string& name = child->getNameString(); + if( name == "hit-y" ) + handleHit + ( + _node->getFloatValue("hit-x"), + _node->getFloatValue("hit-y") + ); + } + + //---------------------------------------------------------------------------- + void Text::handleHit(float x, float y) + { + const osg::Vec2& pos = _text->handleHit(x, y); + _node->setFloatValue("cursor-x", pos.x()); + _node->setFloatValue("cursor-y", pos.y()); + } + +} // namespace canvas +} // namespace simgear diff --git a/simgear/canvas/elements/CanvasText.hxx b/simgear/canvas/elements/CanvasText.hxx new file mode 100644 index 00000000..22596c57 --- /dev/null +++ b/simgear/canvas/elements/CanvasText.hxx @@ -0,0 +1,59 @@ +// A text on the Canvas +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 +#include +#include + +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 _text; + + virtual void childChanged(SGPropertyNode * child); + + void handleHit(float x, float y); + + }; + +} // namespace canvas +} // namespace simgear + +#endif /* CANVAS_TEXT_HXX_ */ diff --git a/simgear/canvas/elements/map/geo_node_pair.hxx b/simgear/canvas/elements/map/geo_node_pair.hxx new file mode 100644 index 00000000..deb4c373 --- /dev/null +++ b/simgear/canvas/elements/map/geo_node_pair.hxx @@ -0,0 +1,134 @@ +// Groups together two nodes representing a geographic position (lat + lon) +// +// Copyright (C) 2012 Thomas Geymayer +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +#ifndef CANVAS_GEO_NODE_PAIR_HXX_ +#define CANVAS_GEO_NODE_PAIR_HXX_ + +namespace simgear +{ +namespace canvas +{ + class GeoNodePair + { + public: + enum StatusFlags + { + LAT_MISSING = 1, + LON_MISSING = LAT_MISSING << 1, + INCOMPLETE = LAT_MISSING | LON_MISSING, + DIRTY = LON_MISSING << 1 + }; + + GeoNodePair(): + _status(INCOMPLETE), + _node_lat(0), + _node_lon(0) + {} + + uint8_t getStatus() const + { + return _status; + } + + void setDirty(bool flag = true) + { + if( flag ) + _status |= DIRTY; + else + _status &= ~DIRTY; + } + + bool isDirty() const + { + return (_status & DIRTY)!=0; + } + + bool isComplete() const + { + return !(_status & INCOMPLETE); + } + + void setNodeLat(SGPropertyNode* node) + { + _node_lat = node; + _status &= ~LAT_MISSING; + + if( node == _node_lon ) + { + _node_lon = 0; + _status |= LON_MISSING; + } + } + + void setNodeLon(SGPropertyNode* node) + { + _node_lon = node; + _status &= ~LON_MISSING; + + if( node == _node_lat ) + { + _node_lat = 0; + _status |= LAT_MISSING; + } + } + + const char* getLat() const + { + return _node_lat ? _node_lat->getStringValue() : ""; + } + + const char* getLon() const + { + return _node_lon ? _node_lon->getStringValue() : ""; + } + + void setTargetName(const std::string& name) + { + _target_name = name; + } + + void setScreenPos(float x, float y) + { + assert( isComplete() ); + SGPropertyNode *parent = _node_lat->getParent(); + parent->getChild(_target_name, _node_lat->getIndex(), true) + ->setDoubleValue(x); + parent->getChild(_target_name, _node_lon->getIndex(), true) + ->setDoubleValue(y); + } + + void print() + { + std::cout << "lat=" << (_node_lat ? _node_lat->getPath() : "") + << ", lon=" << (_node_lon ? _node_lon->getPath() : "") + << std::endl; + } + + private: + + uint8_t _status; + SGPropertyNode *_node_lat, + *_node_lon; + std::string _target_name; + + }; + +} // namespace canvas +} // namespace simgear + +#endif /* CANVAS_GEO_NODE_PAIR_HXX_ */ diff --git a/simgear/canvas/elements/map/projection.hxx b/simgear/canvas/elements/map/projection.hxx new file mode 100644 index 00000000..9a3f65d7 --- /dev/null +++ b/simgear/canvas/elements/map/projection.hxx @@ -0,0 +1,179 @@ +// Geographic projections for Canvas map element +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 + +namespace simgear +{ +namespace canvas +{ + + /** + * Base class for all projections + */ + class Projection + { + public: + struct ScreenPosition + { + ScreenPosition() {} + + ScreenPosition(double x, double y): + x(x), + y(y) + {} + + double x, y; + }; + + virtual ~Projection() {} + + void setScreenRange(double range) + { + _screen_range = range; + } + + virtual ScreenPosition worldToScreen(double x, double y) = 0; + + protected: + + double _screen_range; + }; + + /** + * Base class for horizontal projections + */ + class HorizontalProjection: + public Projection + { + public: + + HorizontalProjection(): + _cos_rot(1), + _sin_rot(0), + _range(5) + { + setScreenRange(200); + } + + /** + * Set world position of center point used for the projection + */ + void setWorldPosition(double lat, double lon) + { + _ref_lat = SGMiscd::deg2rad(lat); + _ref_lon = SGMiscd::deg2rad(lon); + } + + /** + * Set up heading + */ + void setOrientation(float hdg) + { + hdg = SGMiscf::deg2rad(hdg); + _sin_rot = sin(hdg); + _cos_rot = cos(hdg); + } + + void setRange(double range) + { + _range = range; + } + + /** + * Transform given world position to screen position + * + * @param lat Latitude in degrees + * @param lon Longitude in degrees + */ + ScreenPosition worldToScreen(double lat, double lon) + { + lat = SGMiscd::deg2rad(lat); + lon = SGMiscd::deg2rad(lon); + ScreenPosition pos = project(lat, lon); + double scale = _screen_range / _range; + pos.x *= scale; + pos.y *= scale; + return ScreenPosition + ( + _cos_rot * pos.x - _sin_rot * pos.y, + -_sin_rot * pos.x - _cos_rot * pos.y + ); + } + + protected: + + /** + * Project given geographic world position to screen space + * + * @param lat Latitude in radians + * @param lon Longitude in radians + */ + virtual ScreenPosition project(double lat, double lon) const = 0; + + double _ref_lat, + _ref_lon, + _cos_rot, + _sin_rot, + _range; + }; + + /** + * Sanson-Flamsteed projection, relative to the projection center + */ + class SansonFlamsteedProjection: + public HorizontalProjection + { + protected: + + virtual ScreenPosition project(double lat, double lon) const + { + double d_lat = lat - _ref_lat, + d_lon = lon - _ref_lon; + double r = getEarthRadius(lat); + + ScreenPosition pos; + + pos.x = r * cos(lat) * d_lon; + pos.y = r * d_lat; + + return pos; + } + + /** + * Returns Earth radius at a given latitude (Ellipsoide equation with two + * equal axis) + */ + float getEarthRadius(float lat) const + { + const float rec = 6378137.f / 1852; // earth radius, equator (?) + const float rpol = 6356752.314f / 1852; // earth radius, polar (?) + + double a = cos(lat) / rec; + double b = sin(lat) / rpol; + return 1.0f / sqrt( a * a + b * b ); + } + }; + +} // namespace canvas +} // namespace simgear + +#endif /* CANVAS_MAP_PROJECTION_HXX_ */ diff --git a/simgear/canvas/elements/text-alignment.hxx b/simgear/canvas/elements/text-alignment.hxx new file mode 100644 index 00000000..491ab25b --- /dev/null +++ b/simgear/canvas/elements/text-alignment.hxx @@ -0,0 +1,41 @@ +// Mapping between strings and osg text alignment flags +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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") diff --git a/simgear/math/CMakeLists.txt b/simgear/math/CMakeLists.txt index e0d3e627..a334680d 100644 --- a/simgear/math/CMakeLists.txt +++ b/simgear/math/CMakeLists.txt @@ -1,7 +1,8 @@ include (SimGearComponent) -set(HEADERS +set(HEADERS + Rect.hxx SGBox.hxx SGCMath.hxx SGGeoc.hxx diff --git a/simgear/math/Rect.hxx b/simgear/math/Rect.hxx new file mode 100644 index 00000000..577cd8c6 --- /dev/null +++ b/simgear/math/Rect.hxx @@ -0,0 +1,85 @@ +// Class representing a rectangular region +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 + +namespace simgear +{ + + template + 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_ */ diff --git a/simgear/misc/CMakeLists.txt b/simgear/misc/CMakeLists.txt index d07b6718..8ef0f2e3 100644 --- a/simgear/misc/CMakeLists.txt +++ b/simgear/misc/CMakeLists.txt @@ -4,6 +4,7 @@ include (SimGearComponent) set(HEADERS ResourceManager.hxx interpolator.hxx + parse_color.hxx sg_dir.hxx sg_path.hxx sgstream.hxx @@ -19,6 +20,7 @@ set(HEADERS set(SOURCES ResourceManager.cxx interpolator.cxx + parse_color.cxx sg_dir.cxx sg_path.cxx sgstream.cxx @@ -39,6 +41,10 @@ add_executable(test_strings strutils_test.cxx ) add_test(test_strings ${EXECUTABLE_OUTPUT_PATH}/test_strings) target_link_libraries(test_strings SimGearCore) +add_executable(test_parse_color parse_color_test.cxx ) +add_test(test_parse_color ${EXECUTABLE_OUTPUT_PATH}/test_parse_color) +target_link_libraries(test_parse_color SimGearCore) + add_executable(test_path path_test.cxx ) add_test(test_path ${EXECUTABLE_OUTPUT_PATH}/test_path) target_link_libraries(test_path SimGearCore) diff --git a/simgear/misc/parse_color.cxx b/simgear/misc/parse_color.cxx new file mode 100644 index 00000000..722ccfa2 --- /dev/null +++ b/simgear/misc/parse_color.cxx @@ -0,0 +1,90 @@ +// Parse CSS colors +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 +#include +#include +#include + +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 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 > tokenizer; + const boost::char_separator 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(*tok) + // rgb = [0,255], a = [0,1] + / (comp < 3 ? 255 : 1); + } + } + else + return false; + + result = color; + return true; + } + +} // namespace simgear diff --git a/simgear/misc/parse_color.hxx b/simgear/misc/parse_color.hxx new file mode 100644 index 00000000..d4ecad3c --- /dev/null +++ b/simgear/misc/parse_color.hxx @@ -0,0 +1,40 @@ +// Parse CSS colors +// +// Copyright (C) 2012 Thomas Geymayer +// +// 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 +#include + +namespace simgear +{ + + /** + * Parse a (CSS) color + * + * @param str Text to parse + * @param result Output for parse color + * + * @return Whether str contained a valid color (and result has been modified) + */ + bool parseColor(std::string str, osg::Vec4& result); + +} // namespace simgear + +#endif /* PARSE_COLOR_HXX_ */ diff --git a/simgear/misc/parse_color_test.cxx b/simgear/misc/parse_color_test.cxx new file mode 100644 index 00000000..c41b2645 --- /dev/null +++ b/simgear/misc/parse_color_test.cxx @@ -0,0 +1,35 @@ +#include + +#include "parse_color.hxx" + +#include + +#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; +} diff --git a/simgear/props/PropertyBasedElement.cxx b/simgear/props/PropertyBasedElement.cxx index 88a0ad2c..b1667e8c 100644 --- a/simgear/props/PropertyBasedElement.cxx +++ b/simgear/props/PropertyBasedElement.cxx @@ -12,9 +12,9 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA #include "PropertyBasedElement.hxx" @@ -46,4 +46,10 @@ namespace simgear return _node; } + //---------------------------------------------------------------------------- + void PropertyBasedElement::setSelf(const PropertyBasedElementPtr& self) + { + _self = self; + } + } // namespace simgear diff --git a/simgear/props/PropertyBasedElement.hxx b/simgear/props/PropertyBasedElement.hxx index 23558e70..05dbcd1b 100644 --- a/simgear/props/PropertyBasedElement.hxx +++ b/simgear/props/PropertyBasedElement.hxx @@ -12,9 +12,9 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA #ifndef SG_PROPERTY_BASED_ELEMENT_HXX_ #define SG_PROPERTY_BASED_ELEMENT_HXX_ @@ -47,9 +47,9 @@ namespace simgear SGConstPropertyNode_ptr getProps() const; SGPropertyNode_ptr getProps(); - protected: + virtual void setSelf(const PropertyBasedElementPtr& self); - friend class PropertyBasedMgr; + protected: SGPropertyNode_ptr _node; PropertyBasedElementWeakPtr _self; diff --git a/simgear/props/PropertyBasedMgr.cxx b/simgear/props/PropertyBasedMgr.cxx index 893eb34b..397aa2b6 100644 --- a/simgear/props/PropertyBasedMgr.cxx +++ b/simgear/props/PropertyBasedMgr.cxx @@ -12,9 +12,9 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA #include "PropertyBasedMgr.hxx" @@ -75,7 +75,7 @@ namespace simgear ); PropertyBasedElementPtr el = _element_factory(child); - el->_self = el; + el->setSelf( el ); _elements[index] = el; elementCreated( el ); } diff --git a/simgear/props/PropertyBasedMgr.hxx b/simgear/props/PropertyBasedMgr.hxx index bb189bbc..6246822a 100644 --- a/simgear/props/PropertyBasedMgr.hxx +++ b/simgear/props/PropertyBasedMgr.hxx @@ -12,9 +12,9 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Library General Public License for more details. // -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA #ifndef SG_PROPERTY_BASED_MGR_HXX_ #define SG_PROPERTY_BASED_MGR_HXX_ diff --git a/simgear/props/props.hxx b/simgear/props/props.hxx index 6a0f1f93..f2692710 100644 --- a/simgear/props/props.hxx +++ b/simgear/props/props.hxx @@ -1162,6 +1162,18 @@ public: T getValue(typename boost::disable_if_c::Internal> ::type* dummy = 0) const; + /** + * Get a list of values from all children with the given name + */ + template // TODO use C++11 or traits + std::vector getChildValues(const std::string& name) const; + + /** + * Get a list of values from all children with the given name + */ + template + std::vector getChildValues(const std::string& name) const; + /** * Set a bool value for this node. */ @@ -1846,6 +1858,25 @@ inline T SGPropertyNode::getValue(typename boost::enable_if_c(this); } +template // TODO use C++11 or traits +std::vector SGPropertyNode::getChildValues(const std::string& name) const +{ + const simgear::PropertyList& props = getChildren(name); + std::vector values( props.size() ); + + for( size_t i = 0; i < props.size(); ++i ) + values[i] = props[i]->getValue(); + + return values; +} + +template +inline +std::vector SGPropertyNode::getChildValues(const std::string& name) const +{ + return getChildValues(name); +} + template bool SGPropertyNode::setValue(const T& val, typename boost::disable_if_c