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.
This commit is contained in:
Thomas Geymayer
2012-11-04 14:12:05 +01:00
parent a7f64cf7aa
commit b99f53fda3
41 changed files with 4381 additions and 271 deletions

View File

@@ -1,14 +1,25 @@
include (SimGearComponent)
set(HEADERS
ODGauge.hxx
VGInitOperation.hxx
)
canvas_fwd.hxx
Canvas.hxx
CanvasMgr.hxx
CanvasPlacement.hxx
CanvasSystemAdapter.hxx
MouseEvent.hxx
ODGauge.hxx
VGInitOperation.hxx
)
set(SOURCES
ODGauge.cxx
)
Canvas.cxx
CanvasMgr.cxx
CanvasPlacement.cxx
ODGauge.cxx
)
add_subdirectory(ShivaVG/src)
include_directories(ShivaVG/include)
add_subdirectory(elements)
simgear_scene_component(canvas canvas "${SOURCES}" "${HEADERS}")

502
simgear/canvas/Canvas.cxx Normal file
View File

@@ -0,0 +1,502 @@
// The canvas for rendering with the 2d API
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "Canvas.hxx"
#include <simgear/canvas/MouseEvent.hxx>
#include <simgear/misc/parse_color.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
#include <osg/Camera>
#include <osg/Geode>
#include <osgText/Text>
#include <osgViewer/Viewer>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/foreach.hpp>
#include <iostream>
namespace simgear
{
namespace canvas
{
//----------------------------------------------------------------------------
Canvas::CullCallback::CullCallback(const CanvasWeakPtr& canvas):
_canvas( canvas )
{
}
//----------------------------------------------------------------------------
void Canvas::CullCallback::operator()( osg::Node* node,
osg::NodeVisitor* nv )
{
if( (nv->getTraversalMask() & simgear::MODEL_BIT) && !_canvas.expired() )
_canvas.lock()->enableRendering();
traverse(node, nv);
}
//----------------------------------------------------------------------------
Canvas::Canvas(SGPropertyNode* node):
PropertyBasedElement(node),
_canvas_mgr(0),
_size_x(-1),
_size_y(-1),
_view_width(-1),
_view_height(-1),
_status(node, "status"),
_status_msg(node, "status-msg"),
_mouse_x(node, "mouse/x"),
_mouse_y(node, "mouse/y"),
_mouse_dx(node, "mouse/dx"),
_mouse_dy(node, "mouse/dy"),
_mouse_button(node, "mouse/button"),
_mouse_state(node, "mouse/state"),
_mouse_mod(node, "mouse/mod"),
_mouse_scroll(node, "mouse/scroll"),
_mouse_event(node, "mouse/event"),
_sampling_dirty(false),
_render_dirty(true),
_visible(true),
_render_always(false)
{
_status = 0;
setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
}
//----------------------------------------------------------------------------
Canvas::~Canvas()
{
}
//----------------------------------------------------------------------------
void Canvas::setSystemAdapter(const SystemAdapterPtr& system_adapter)
{
_system_adapter = system_adapter;
_texture.setSystemAdapter(system_adapter);
}
//----------------------------------------------------------------------------
SystemAdapterPtr Canvas::getSystemAdapter() const
{
return _system_adapter;
}
//----------------------------------------------------------------------------
void Canvas::setCanvasMgr(CanvasMgr* canvas_mgr)
{
_canvas_mgr = canvas_mgr;
}
//----------------------------------------------------------------------------
CanvasMgr* Canvas::getCanvasMgr() const
{
return _canvas_mgr;
}
//----------------------------------------------------------------------------
void Canvas::addDependentCanvas(const CanvasWeakPtr& canvas)
{
if( canvas.expired() )
{
SG_LOG
(
SG_GENERAL,
SG_WARN,
"Canvas::addDependentCanvas: got an expired Canvas dependent on "
<< _node->getPath()
);
return;
}
_dependent_canvases.insert(canvas);
}
//----------------------------------------------------------------------------
void Canvas::removeDependentCanvas(const CanvasWeakPtr& canvas)
{
_dependent_canvases.erase(canvas);
}
//----------------------------------------------------------------------------
void Canvas::enableRendering(bool force)
{
_visible = true;
if( force )
_render_dirty = true;
}
//----------------------------------------------------------------------------
void Canvas::update(double delta_time_sec)
{
if( !_texture.serviceable() )
{
if( _status != STATUS_OK )
return;
_texture.setSize(_size_x, _size_y);
_texture.useImageCoords(true);
_texture.useStencil(true);
_texture.allocRT(/*_camera_callback*/);
osg::Camera* camera = _texture.getCamera();
osg::Vec4 clear_color(0.0f, 0.0f, 0.0f , 1.0f);
parseColor(_node->getStringValue("background"), clear_color);
camera->setClearColor(clear_color);
camera->addChild(_root_group->getMatrixTransform());
// Ensure objects are drawn in order of traversal
camera->getOrCreateStateSet()->setBinName("TraversalOrderBin");
if( _texture.serviceable() )
{
setStatusFlags(STATUS_OK);
}
else
{
setStatusFlags(CREATE_FAILED);
return;
}
}
if( _visible || _render_always )
{
if( _render_dirty )
{
// Also mark all dependent (eg. recursively used) canvases as dirty
BOOST_FOREACH(CanvasWeakPtr canvas, _dependent_canvases)
{
if( !canvas.expired() )
canvas.lock()->_render_dirty = true;
}
}
_texture.setRender(_render_dirty);
_render_dirty = false;
_visible = false;
}
else
_texture.setRender(false);
_root_group->update(delta_time_sec);
if( _sampling_dirty )
{
_texture.setSampling(
_node->getBoolValue("mipmapping"),
_node->getIntValue("coverage-samples"),
_node->getIntValue("color-samples")
);
_sampling_dirty = false;
_render_dirty = true;
}
while( !_dirty_placements.empty() )
{
SGPropertyNode *node = _dirty_placements.back();
_dirty_placements.pop_back();
if( node->getIndex() >= static_cast<int>(_placements.size()) )
// New placement
_placements.resize(node->getIndex() + 1);
else
// Remove possibly existing placements
_placements[ node->getIndex() ].clear();
// Get new placements
PlacementFactoryMap::const_iterator placement_factory =
_placement_factories.find( node->getStringValue("type", "object") );
if( placement_factory != _placement_factories.end() )
{
Placements& placements = _placements[ node->getIndex() ] =
placement_factory->second
(
node,
boost::static_pointer_cast<Canvas>(_self.lock())
);
node->setStringValue
(
"status-msg",
placements.empty() ? "No match" : "Ok"
);
}
else
node->setStringValue("status-msg", "Unknown placement type");
}
}
//----------------------------------------------------------------------------
void Canvas::setSizeX(int sx)
{
if( _size_x == sx )
return;
_size_x = sx;
// TODO resize if texture already allocated
if( _size_x <= 0 )
setStatusFlags(MISSING_SIZE_X);
else
setStatusFlags(MISSING_SIZE_X, false);
// reset flag to allow creation with new size
setStatusFlags(CREATE_FAILED, false);
}
//----------------------------------------------------------------------------
void Canvas::setSizeY(int sy)
{
if( _size_y == sy )
return;
_size_y = sy;
// TODO resize if texture already allocated
if( _size_y <= 0 )
setStatusFlags(MISSING_SIZE_Y);
else
setStatusFlags(MISSING_SIZE_Y, false);
// reset flag to allow creation with new size
setStatusFlags(CREATE_FAILED, false);
}
//----------------------------------------------------------------------------
int Canvas::getSizeX() const
{
return _size_x;
}
//----------------------------------------------------------------------------
int Canvas::getSizeY() const
{
return _size_y;
}
//----------------------------------------------------------------------------
void Canvas::setViewWidth(int w)
{
if( _view_width == w )
return;
_view_width = w;
_texture.setViewSize(_view_width, _view_height);
}
//----------------------------------------------------------------------------
void Canvas::setViewHeight(int h)
{
if( _view_height == h )
return;
_view_height = h;
_texture.setViewSize(_view_width, _view_height);
}
//----------------------------------------------------------------------------
bool Canvas::handleMouseEvent(const MouseEvent& event)
{
_mouse_x = event.x;
_mouse_y = event.y;
_mouse_dx = event.dx;
_mouse_dy = event.dy;
_mouse_button = event.button;
_mouse_state = event.state;
_mouse_mod = event.mod;
_mouse_scroll = event.scroll;
// Always set event type last because all listeners are attached to it
_mouse_event = event.type;
if( _root_group.get() )
return _root_group->handleMouseEvent(event);
else
return false;
}
//----------------------------------------------------------------------------
void Canvas::childAdded( SGPropertyNode * parent,
SGPropertyNode * child )
{
if( parent != _node )
return;
if( child->getNameString() == "placement" )
_dirty_placements.push_back(child);
else if( _root_group.get() )
static_cast<Element*>(_root_group.get())->childAdded(parent, child);
}
//----------------------------------------------------------------------------
void Canvas::childRemoved( SGPropertyNode * parent,
SGPropertyNode * child )
{
_render_dirty = true;
if( parent != _node )
return;
if( child->getNameString() == "placement" )
_placements[ child->getIndex() ].clear();
else if( _root_group.get() )
static_cast<Element*>(_root_group.get())->childRemoved(parent, child);
}
//----------------------------------------------------------------------------
void Canvas::valueChanged(SGPropertyNode* node)
{
if( boost::starts_with(node->getNameString(), "status")
|| node->getParent()->getNameString() == "bounding-box" )
return;
_render_dirty = true;
bool handled = true;
if( node->getParent()->getParent() == _node
&& node->getParent()->getNameString() == "placement" )
{
// prevent double updates...
for( size_t i = 0; i < _dirty_placements.size(); ++i )
{
if( node->getParent() == _dirty_placements[i] )
return;
}
_dirty_placements.push_back(node->getParent());
}
else if( node->getParent() == _node )
{
if( node->getNameString() == "background" )
{
osg::Vec4 color;
if( _texture.getCamera() && parseColor(node->getStringValue(), color) )
{
_texture.getCamera()->setClearColor(color);
_render_dirty = true;
}
}
else if( node->getNameString() == "mipmapping"
|| node->getNameString() == "coverage-samples"
|| node->getNameString() == "color-samples" )
{
_sampling_dirty = true;
}
else if( node->getNameString() == "render-always" )
{
_render_always = node->getBoolValue();
}
else if( node->getNameString() == "size" )
{
if( node->getIndex() == 0 )
setSizeX( node->getIntValue() );
else if( node->getIndex() == 1 )
setSizeY( node->getIntValue() );
}
else if( node->getNameString() == "view" )
{
if( node->getIndex() == 0 )
setViewWidth( node->getIntValue() );
else if( node->getIndex() == 1 )
setViewHeight( node->getIntValue() );
}
else if( node->getNameString() == "freeze" )
_texture.setRender( node->getBoolValue() );
else
handled = false;
}
else
handled = false;
if( !handled && _root_group.get() )
_root_group->valueChanged(node);
}
//----------------------------------------------------------------------------
osg::Texture2D* Canvas::getTexture() const
{
return _texture.getTexture();
}
//----------------------------------------------------------------------------
Canvas::CullCallbackPtr Canvas::getCullCallback() const
{
return _cull_callback;
}
//----------------------------------------------------------------------------
void Canvas::addPlacementFactory( const std::string& type,
PlacementFactory factory )
{
if( _placement_factories.find(type) != _placement_factories.end() )
SG_LOG
(
SG_GENERAL,
SG_WARN,
"Canvas::addPlacementFactory: replace existing factor for type " << type
);
_placement_factories[type] = factory;
}
//----------------------------------------------------------------------------
void Canvas::setSelf(const PropertyBasedElementPtr& self)
{
PropertyBasedElement::setSelf(self);
CanvasPtr canvas = boost::static_pointer_cast<Canvas>(self);
_root_group.reset( new Group(canvas, _node) );
// Remove automatically created property listener as we forward them on our
// own
_root_group->removeListener();
_cull_callback = new CullCallback(canvas);
}
//----------------------------------------------------------------------------
void Canvas::setStatusFlags(unsigned int flags, bool set)
{
if( set )
_status = _status | flags;
else
_status = _status & ~flags;
// TODO maybe extend simgear::PropertyObject to allow |=, &= etc.
if( (_status & MISSING_SIZE_X) && (_status & MISSING_SIZE_Y) )
_status_msg = "Missing size";
else if( _status & MISSING_SIZE_X )
_status_msg = "Missing size-x";
else if( _status & MISSING_SIZE_Y )
_status_msg = "Missing size-y";
else if( _status & CREATE_FAILED )
_status_msg = "Creating render target failed";
else if( _status == STATUS_OK && !_texture.serviceable() )
_status_msg = "Creation pending...";
else
_status_msg = "Ok";
}
//----------------------------------------------------------------------------
Canvas::PlacementFactoryMap Canvas::_placement_factories;
} // namespace canvas
} // namespace simgear

180
simgear/canvas/Canvas.hxx Normal file
View File

@@ -0,0 +1,180 @@
// The canvas for rendering with the 2d API
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_HXX_
#define CANVAS_HXX_
#include "canvas_fwd.hxx"
#include "ODGauge.hxx"
#include <simgear/canvas/elements/CanvasGroup.hxx>
#include <simgear/props/PropertyBasedElement.hxx>
#include <simgear/props/propertyObject.hxx>
#include <osg/NodeCallback>
#include <osg/observer_ptr>
#include <memory>
#include <string>
namespace simgear
{
namespace canvas
{
class CanvasMgr;
class MouseEvent;
class Canvas:
public PropertyBasedElement
{
public:
enum StatusFlags
{
STATUS_OK,
MISSING_SIZE_X = 0x0001,
MISSING_SIZE_Y = 0x0002,
CREATE_FAILED = 0x0004
};
/**
* This callback is installed on every placement of the canvas in the
* scene to only render the canvas if at least one placement is visible
*/
class CullCallback:
public osg::NodeCallback
{
public:
CullCallback(const CanvasWeakPtr& canvas);
private:
CanvasWeakPtr _canvas;
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
};
typedef osg::ref_ptr<CullCallback> CullCallbackPtr;
Canvas(SGPropertyNode* node);
virtual ~Canvas();
void setSystemAdapter(const SystemAdapterPtr& system_adapter);
SystemAdapterPtr getSystemAdapter() const;
void setCanvasMgr(CanvasMgr* canvas_mgr);
CanvasMgr* getCanvasMgr() const;
/**
* Add a canvas which should be mared as dirty upon any change to this
* canvas.
*
* This mechanism is used to eg. redraw a canvas if it's displaying
* another canvas (recursive canvases)
*/
void addDependentCanvas(const CanvasWeakPtr& canvas);
/**
* Stop notifying the given canvas upon changes
*/
void removeDependentCanvas(const CanvasWeakPtr& canvas);
/**
* Enable rendering for the next frame
*
* @param force Force redraw even if nothing has changed (if dirty flag
* is not set)
*/
void enableRendering(bool force = false);
void update(double delta_time_sec);
void setSizeX(int sx);
void setSizeY(int sy);
int getSizeX() const;
int getSizeY() const;
void setViewWidth(int w);
void setViewHeight(int h);
bool handleMouseEvent(const MouseEvent& event);
virtual void childAdded( SGPropertyNode * parent,
SGPropertyNode * child );
virtual void childRemoved( SGPropertyNode * parent,
SGPropertyNode * child );
virtual void valueChanged (SGPropertyNode * node);
osg::Texture2D* getTexture() const;
CullCallbackPtr getCullCallback() const;
static void addPlacementFactory( const std::string& type,
PlacementFactory factory );
protected:
SystemAdapterPtr _system_adapter;
CanvasMgr *_canvas_mgr;
int _size_x,
_size_y,
_view_width,
_view_height;
PropertyObject<int> _status;
PropertyObject<std::string> _status_msg;
PropertyObject<int> _mouse_x, _mouse_y,
_mouse_dx, _mouse_dy,
_mouse_button,
_mouse_state,
_mouse_mod,
_mouse_scroll,
_mouse_event;
bool _sampling_dirty,
_render_dirty,
_visible;
ODGauge _texture;
std::auto_ptr<Group> _root_group;
CullCallbackPtr _cull_callback;
bool _render_always; //<! Used to disable automatic lazy rendering (culling)
std::vector<SGPropertyNode*> _dirty_placements;
std::vector<canvas::Placements> _placements;
std::set<CanvasWeakPtr> _dependent_canvases; //<! Canvases which use this
// canvas and should be
// notified about changes
typedef std::map<std::string, canvas::PlacementFactory> PlacementFactoryMap;
static PlacementFactoryMap _placement_factories;
virtual void setSelf(const PropertyBasedElementPtr& self);
void setStatusFlags(unsigned int flags, bool set = true);
private:
Canvas(const Canvas&); // = delete;
Canvas& operator=(const Canvas&); // = delete;
};
} // namespace canvas
} // namespace simgear
#endif /* CANVAS_HXX_ */

View File

@@ -0,0 +1,65 @@
// Canvas with 2D rendering API
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CanvasMgr.hxx"
#include "Canvas.hxx"
#include <boost/bind.hpp>
namespace simgear
{
namespace canvas
{
/**
* Canvas factory
*/
CanvasPtr canvasFactory(SGPropertyNode* node)
{
return CanvasPtr(new Canvas(node));
}
//----------------------------------------------------------------------------
CanvasMgr::CanvasMgr( SGPropertyNode_ptr node,
SystemAdapterPtr system_adapter ):
PropertyBasedMgr(node, "texture", &canvasFactory),
_system_adapter(system_adapter)
{
}
//----------------------------------------------------------------------------
CanvasPtr CanvasMgr::getCanvas(size_t index) const
{
if( index >= _elements.size()
|| !_elements[index] )
return CanvasPtr();
return boost::static_pointer_cast<Canvas>(_elements[index]);
}
//----------------------------------------------------------------------------
void CanvasMgr::elementCreated(PropertyBasedElementPtr element)
{
CanvasPtr canvas = boost::static_pointer_cast<Canvas>(element);
canvas->setSystemAdapter(_system_adapter);
canvas->setCanvasMgr(this);
}
} // namespace canvas
} // namespace simgear

View File

@@ -0,0 +1,61 @@
// Canvas with 2D rendering API
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_CANVAS_MGR_H_
#define SG_CANVAS_MGR_H_
#include "canvas_fwd.hxx"
#include <simgear/props/PropertyBasedMgr.hxx>
namespace simgear
{
namespace canvas
{
class CanvasMgr:
public PropertyBasedMgr
{
public:
/**
* @param node Root node of branch used to control canvasses
* @param system_adapter Adapter for connecting between canvas and
* application framework
*
*/
CanvasMgr( SGPropertyNode_ptr node,
SystemAdapterPtr system_adapter );
/**
* Get ::Canvas by index
*
* @param index Index of texture node in /canvas/by-index/
*/
CanvasPtr getCanvas(size_t index) const;
protected:
SystemAdapterPtr _system_adapter;
virtual void elementCreated(PropertyBasedElementPtr element);
};
} // namespace canvas
} // namespace simgear
#endif /* SG_CANVAS_MGR_H_ */

View File

@@ -0,0 +1,39 @@
// Base class for canvas placements
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CanvasPlacement.hxx"
namespace simgear
{
namespace canvas
{
//----------------------------------------------------------------------------
Placement::Placement()
{
}
//----------------------------------------------------------------------------
Placement::~Placement()
{
}
} // namespace canvas
} // namespace simgear

View File

@@ -0,0 +1,41 @@
// Base class for canvas placements
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_PLACEMENT_HXX_
#define CANVAS_PLACEMENT_HXX_
namespace simgear
{
namespace canvas
{
class Placement
{
public:
Placement();
virtual ~Placement() = 0;
private:
Placement(const Placement&) /* = delete */;
Placement& operator=(const Placement&) /* = delete */;
};
} // namespace canvas
} // namespace simgear
#endif /* CANVAS_PLACEMENT_HXX_ */

View File

@@ -0,0 +1,43 @@
// Adapter for using the canvas with different applications
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_CANVAS_SYSTEM_ADAPTER_HXX_
#define SG_CANVAS_SYSTEM_ADAPTER_HXX_
#include "canvas_fwd.hxx"
namespace simgear
{
namespace canvas
{
class SystemAdapter
{
public:
virtual ~SystemAdapter() {}
virtual FontPtr getFont(const std::string& name) const = 0;
virtual void addCamera(osg::Camera* camera) const = 0;
virtual void removeCamera(osg::Camera* camera) const = 0;
virtual osg::Image* getImage(const std::string& path) const = 0;
};
} // namespace canvas
} // namespace simgear
#endif /* SG_CANVAS_SYSTEM_ADAPTER_HXX_ */

View File

@@ -0,0 +1,60 @@
// Mouse event
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_MOUSE_EVENT_HXX_
#define CANVAS_MOUSE_EVENT_HXX_
#include <osgGA/GUIEventAdapter>
namespace simgear
{
namespace canvas
{
class MouseEvent
{
public:
typedef osgGA::GUIEventAdapter::EventType EventType;
typedef osgGA::GUIEventAdapter::ScrollingMotion Scroll;
MouseEvent(EventType type):
type(type),
x(-1), y(-1),
dx(0), dy(0),
button(-1),
state(-1),
mod(-1),
scroll(osgGA::GUIEventAdapter::SCROLL_NONE)
{}
osg::Vec2f getPos() const { return osg::Vec2f(x, y); }
osg::Vec3f getPos3() const { return osg::Vec3f(x, y, 0); }
EventType type;
float x, y,
dx, dy;
int button, //<! Button for this event
state, //<! Current button state
mod; //<! Keyboard modifier state
Scroll scroll;
};
} // namespace canvas
} // namespace simgear
#endif /* CANVAS_MOUSE_EVENT_HXX_ */

View File

@@ -10,26 +10,30 @@
// Supports now multisampling/mipmapping, usage of the stencil buffer and placing
// the texture in the scene by certain filter criteria
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifdef HAVE_CONFIG_H
# include <simgear_config.h>
#endif
#include "ODGauge.hxx"
#include "CanvasSystemAdapter.hxx"
#include <simgear/debug/logstream.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
#include <osg/Texture2D>
#include <osg/AlphaFunc>
#include <osg/BlendFunc>
@@ -40,237 +44,243 @@
#include <osg/StateSet>
#include <osg/FrameBufferObject> // for GL_DEPTH_STENCIL_EXT on Windows
#include <simgear/debug/logstream.hxx>
#include <simgear/scene/util/RenderConstants.hxx>
#include "ODGauge.hxx"
#include <cassert>
namespace simgear
{
//------------------------------------------------------------------------------
ODGauge::ODGauge( const CameraRegistrationCallback& cb_camera_add,
const CameraRegistrationCallback& cb_camera_remove ):
_size_x( -1 ),
_size_y( -1 ),
_view_width( -1 ),
_view_height( -1 ),
_use_image_coords( false ),
_use_stencil( false ),
_use_mipmapping( false ),
_coverage_samples( 0 ),
_color_samples( 0 ),
rtAvailable( false ),
_cb_cam_add( cb_camera_add ),
_cb_cam_remove( cb_camera_remove )
namespace canvas
{
}
//----------------------------------------------------------------------------
ODGauge::ODGauge():
_size_x( -1 ),
_size_y( -1 ),
_view_width( -1 ),
_view_height( -1 ),
_use_image_coords( false ),
_use_stencil( false ),
_use_mipmapping( false ),
_coverage_samples( 0 ),
_color_samples( 0 ),
rtAvailable( false )
{
//------------------------------------------------------------------------------
ODGauge::~ODGauge()
{
if( camera.valid() )
_cb_cam_remove(camera.get());
}
}
//------------------------------------------------------------------------------
void ODGauge::setSize(int size_x, int size_y)
{
_size_x = size_x;
_size_y = size_y < 0 ? size_x : size_y;
//----------------------------------------------------------------------------
ODGauge::~ODGauge()
{
if( camera.valid() && _system_adapter )
_system_adapter->removeCamera(camera.get());
}
if( texture.valid() )
texture->setTextureSize(_size_x, _size_x);
}
//----------------------------------------------------------------------------
void ODGauge::setSystemAdapter(const SystemAdapterPtr& system_adapter)
{
_system_adapter = system_adapter;
}
//----------------------------------------------------------------------------
void ODGauge::setViewSize(int width, int height)
{
_view_width = width;
_view_height = height < 0 ? width : height;
//----------------------------------------------------------------------------
void ODGauge::setSize(int size_x, int size_y)
{
_size_x = size_x;
_size_y = size_y < 0 ? size_x : size_y;
if( camera )
if( texture.valid() )
texture->setTextureSize(_size_x, _size_x);
}
//----------------------------------------------------------------------------
void ODGauge::setViewSize(int width, int height)
{
_view_width = width;
_view_height = height < 0 ? width : height;
if( camera )
updateCoordinateFrame();
}
//----------------------------------------------------------------------------
void ODGauge::useImageCoords(bool use)
{
if( use == _use_image_coords )
return;
_use_image_coords = use;
if( texture )
updateCoordinateFrame();
}
//----------------------------------------------------------------------------
void ODGauge::useStencil(bool use)
{
if( use == _use_stencil )
return;
_use_stencil = use;
if( texture )
updateStencil();
}
//----------------------------------------------------------------------------
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_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();
}
//----------------------------------------------------------------------------
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();
}
//------------------------------------------------------------------------------
void ODGauge::useImageCoords(bool use)
{
if( use == _use_image_coords )
return;
_use_image_coords = use;
if( texture )
updateCoordinateFrame();
}
//------------------------------------------------------------------------------
void ODGauge::useStencil(bool use)
{
if( use == _use_stencil )
return;
_use_stencil = use;
if( texture )
updateStencil();
}
//------------------------------------------------------------------------------
void ODGauge::setSampling( bool mipmapping,
int coverage_samples,
int color_samples )
{
if( _use_mipmapping == mipmapping
&& _coverage_samples == coverage_samples
&& _color_samples == color_samples )
return;
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);
}
_use_mipmapping = mipmapping;
updateSampling();
if( color_samples > coverage_samples )
if( _system_adapter )
_system_adapter->addCamera(camera.get());
rtAvailable = true;
}
//----------------------------------------------------------------------------
void ODGauge::updateCoordinateFrame()
{
SG_LOG
(
SG_GL,
SG_WARN,
"ODGauge::setSampling: color_samples > coverage_samples not allowed!"
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. )
);
}
//----------------------------------------------------------------------------
void ODGauge::updateStencil()
{
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);
}
//----------------------------------------------------------------------------
void ODGauge::updateSampling()
{
assert( camera );
assert( texture );
texture->setFilter(
osg::Texture2D::MIN_FILTER,
_use_mipmapping ? osg::Texture2D::LINEAR_MIPMAP_LINEAR
: osg::Texture2D::LINEAR
);
color_samples = coverage_samples;
}
_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 )
{
texture = new osg::Texture2D;
texture->setTextureSize(_size_x, _size_y);
texture->setInternalFormat(GL_RGBA);
}
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)
camera->attach(
osg::Camera::COLOR_BUFFER,
texture.get(),
0, 0,
_use_mipmapping,
_coverage_samples,
_color_samples
);
else
camera->setProjectionMatrix(
osg::Matrix::ortho2D( -_view_width/2., _view_width/2.,
-_view_height/2., _view_height/2. )
);
}
//------------------------------------------------------------------------------
void ODGauge::updateStencil()
{
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);
}
//------------------------------------------------------------------------------
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
);
}
} // namespace canvas
} // namespace simgear

View File

@@ -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 <osg/NodeCallback>
#include <osg/Group>
#include <boost/function.hpp>
namespace osg
{
class Camera;
@@ -42,6 +40,9 @@ namespace osg
namespace simgear
{
namespace canvas
{
/**
* Owner Drawn Gauge (aka render-to-texture) helper class
*/
@@ -49,12 +50,11 @@ namespace simgear
{
public:
typedef boost::function<void(osg::Camera*)> CameraRegistrationCallback;
ODGauge( const CameraRegistrationCallback& cb_camera_add,
const CameraRegistrationCallback& cb_camera_remove );
ODGauge();
virtual ~ODGauge();
void setSystemAdapter(const SystemAdapterPtr& system_adapter);
/**
* Set the size of the render target.
*
@@ -124,7 +124,9 @@ namespace simgear
// Real initialization function. Bad name.
void allocRT(osg::NodeCallback* camera_cull_callback = 0);
private:
protected:
SystemAdapterPtr _system_adapter;
int _size_x,
_size_y,
@@ -142,15 +144,13 @@ namespace simgear
osg::ref_ptr<osg::Camera> camera;
osg::ref_ptr<osg::Texture2D> texture;
CameraRegistrationCallback _cb_cam_add,
_cb_cam_remove;
void updateCoordinateFrame();
void updateStencil();
void updateSampling();
};
} // namespace canvas
} // namespace simgear
#endif // _SG_OD_GAUGE_HXX

View File

@@ -45,4 +45,8 @@ TARGET_LINK_LIBRARIES(
ShivaVG
${OPENGL_gl_LIBRARY}
${OPENGL_glu_LIBRARY}
)
)
if(NOT SIMGEAR_HEADLESS)
install(TARGETS ShivaVG ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()

View File

@@ -12,9 +12,9 @@
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_VG_INITOPERATION_HXX_
#define CANVAS_VG_INITOPERATION_HXX_

View File

@@ -0,0 +1,58 @@
// Canvas forward declarations
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_CANVAS_FWD_HXX_
#define SG_CANVAS_FWD_HXX_
#include <simgear/props/propsfwd.hxx>
#include <simgear/structure/SGWeakPtr.hxx>
#include <osg/ref_ptr>
#include <osgText/Font>
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <vector>
namespace simgear
{
namespace canvas
{
class Canvas;
typedef boost::shared_ptr<Canvas> CanvasPtr;
typedef boost::weak_ptr<Canvas> CanvasWeakPtr;
typedef osg::ref_ptr<osgText::Font> FontPtr;
class Placement;
typedef boost::shared_ptr<Placement> PlacementPtr;
typedef std::vector<PlacementPtr> Placements;
typedef boost::function<Placements( const SGPropertyNode*,
CanvasPtr )> PlacementFactory;
class SystemAdapter;
typedef boost::shared_ptr<SystemAdapter> SystemAdapterPtr;
} // namespace canvas
} // namespace simgear
#endif /* SG_CANVAS_FWD_HXX_ */

View File

@@ -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}")

View File

@@ -0,0 +1,304 @@
// Interface for 2D Canvas element
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CanvasElement.hxx"
#include <simgear/canvas/MouseEvent.hxx>
#include <osg/Drawable>
#include <osg/Geode>
#include <boost/foreach.hpp>
#include <cassert>
#include <cstring>
namespace simgear
{
namespace canvas
{
const std::string NAME_TRANSFORM = "tf";
//----------------------------------------------------------------------------
void Element::removeListener()
{
_node->removeChangeListener(this);
}
//----------------------------------------------------------------------------
Element::~Element()
{
removeListener();
BOOST_FOREACH(osg::Group* parent, _transform->getParents())
{
parent->removeChild(_transform);
}
}
//----------------------------------------------------------------------------
void Element::update(double dt)
{
if( !_transform->getNodeMask() )
// Don't do anything if element is hidden
return;
if( _transform_dirty )
{
osg::Matrix m;
for( size_t i = 0; i < _transform_types.size(); ++i )
{
// Skip unused indizes...
if( _transform_types[i] == TT_NONE )
continue;
SGPropertyNode* tf_node = _node->getChild("tf", i, true);
// Build up the matrix representation of the current transform node
osg::Matrix tf;
switch( _transform_types[i] )
{
case TT_MATRIX:
tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1),
tf_node->getDoubleValue("m[1]", 0), 0, 0,
tf_node->getDoubleValue("m[2]", 0),
tf_node->getDoubleValue("m[3]", 1), 0, 0,
0, 0, 1, 0,
tf_node->getDoubleValue("m[4]", 0),
tf_node->getDoubleValue("m[5]", 0), 0, 1 );
break;
case TT_TRANSLATE:
tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0),
tf_node->getDoubleValue("t[1]", 0),
0 ) );
break;
case TT_ROTATE:
tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 );
break;
case TT_SCALE:
{
float sx = tf_node->getDoubleValue("s[0]", 1);
// sy defaults to sx...
tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 );
break;
}
default:
break;
}
m.postMult( tf );
}
_transform->setMatrix(m);
_transform_dirty = false;
}
}
//----------------------------------------------------------------------------
bool Element::handleMouseEvent(const MouseEvent& event)
{
// Transform event to local coordinates
const osg::Matrixd& m = _transform->getInverseMatrix();
MouseEvent local_event = event;
local_event.x = m(0, 0) * event.x + m(1, 0) * event.y + m(3, 0);
local_event.y = m(0, 1) * event.x + m(1, 1) * event.y + m(3, 1);
// Drawables have a bounding box...
if( _drawable )
{
if( !_drawable->getBound().contains(local_event.getPos3()) )
return false;
}
// ... for other elements, i.e. groups only a bounding sphere is available
else if( !_transform->getBound().contains(local_event.getPos3()) )
return false;
local_event.dx = m(0, 0) * event.dx + m(1, 0) * event.dy;
local_event.dy = m(0, 1) * event.dx + m(1, 1) * event.dy;
return handleLocalMouseEvent(local_event);
}
//----------------------------------------------------------------------------
osg::ref_ptr<osg::MatrixTransform> Element::getMatrixTransform()
{
return _transform;
}
//----------------------------------------------------------------------------
void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
{
if( parent == _node
&& child->getNameString() == NAME_TRANSFORM )
{
if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
_transform_types.resize( child->getIndex() + 1 );
_transform_types[ child->getIndex() ] = TT_NONE;
_transform_dirty = true;
return;
}
else if( parent->getParent() == _node
&& parent->getNameString() == NAME_TRANSFORM )
{
assert(parent->getIndex() < static_cast<int>(_transform_types.size()));
const std::string& name = child->getNameString();
TransformType& type = _transform_types[parent->getIndex()];
if( name == "m" )
type = TT_MATRIX;
else if( name == "t" )
type = TT_TRANSLATE;
else if( name == "rot" )
type = TT_ROTATE;
else if( name == "s" )
type = TT_SCALE;
_transform_dirty = true;
return;
}
childAdded(child);
}
//----------------------------------------------------------------------------
void Element::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
{
if( parent == _node && child->getNameString() == NAME_TRANSFORM )
{
assert(child->getIndex() < static_cast<int>(_transform_types.size()));
_transform_types[ child->getIndex() ] = TT_NONE;
while( !_transform_types.empty() && _transform_types.back() == TT_NONE )
_transform_types.pop_back();
_transform_dirty = true;
return;
}
childRemoved(child);
}
//----------------------------------------------------------------------------
void Element::valueChanged(SGPropertyNode* child)
{
SGPropertyNode *parent = child->getParent();
if( parent == _node )
{
if( setStyle(child) )
return;
else if( child->getNameString() == "update" )
return update(0);
else if( child->getNameString() == "visible" )
// TODO check if we need another nodemask
return _transform->setNodeMask( child->getBoolValue() ? 0xffffffff : 0 );
}
else if( parent->getParent() == _node
&& parent->getNameString() == NAME_TRANSFORM )
{
_transform_dirty = true;
return;
}
childChanged(child);
}
//----------------------------------------------------------------------------
void Element::setBoundingBox(const osg::BoundingBox& bb)
{
if( _bounding_box.empty() )
{
SGPropertyNode* bb_node = _node->getChild("bounding-box", 0, true);
_bounding_box.resize(4);
_bounding_box[0] = bb_node->getChild("min-x", 0, true);
_bounding_box[1] = bb_node->getChild("min-y", 0, true);
_bounding_box[2] = bb_node->getChild("max-x", 0, true);
_bounding_box[3] = bb_node->getChild("max-y", 0, true);
}
_bounding_box[0]->setFloatValue(bb._min.x());
_bounding_box[1]->setFloatValue(bb._min.y());
_bounding_box[2]->setFloatValue(bb._max.x());
_bounding_box[3]->setFloatValue(bb._max.y());
}
//----------------------------------------------------------------------------
Element::Element( const CanvasWeakPtr& canvas,
SGPropertyNode_ptr node,
const Style& parent_style ):
_canvas( canvas ),
_transform_dirty( false ),
_transform( new osg::MatrixTransform ),
_node( node ),
_style( parent_style ),
_drawable( 0 )
{
assert( _node );
_node->addChangeListener(this);
SG_LOG
(
SG_GL,
SG_DEBUG,
"New canvas element " << node->getPath()
);
}
//----------------------------------------------------------------------------
bool Element::handleLocalMouseEvent(const MouseEvent& event)
{
// std::cout << _node->getPath()
// << " local: pos=(" << event.x << "|" << event.y << ") "
// << "d=(" << event.dx << "|" << event.dx << ")"
// << std::endl;
return true;
}
//----------------------------------------------------------------------------
void Element::setDrawable( osg::Drawable* drawable )
{
_drawable = drawable;
assert( _drawable );
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->addDrawable(_drawable);
_transform->addChild(geode);
}
//----------------------------------------------------------------------------
void Element::setupStyle()
{
BOOST_FOREACH( Style::value_type style, _style )
setStyle(style.second);
}
//----------------------------------------------------------------------------
bool Element::setStyle(const SGPropertyNode* child)
{
StyleSetters::const_iterator setter =
_style_setters.find(child->getNameString());
if( setter == _style_setters.end() )
return false;
setter->second(child);
return true;
}
} // namespace canvas
} // namespace simgear

View File

@@ -0,0 +1,178 @@
// Interface for 2D Canvas elements
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_ELEMENT_HXX_
#define CANVAS_ELEMENT_HXX_
#include <simgear/canvas/canvas_fwd.hxx>
#include <simgear/props/props.hxx>
#include <simgear/misc/stdint.hxx> // for uint32_t
#include <osg/BoundingBox>
#include <osg/MatrixTransform>
#include <boost/bind.hpp>
#include <boost/function.hpp>
namespace osg
{
class Drawable;
}
namespace simgear
{
namespace canvas
{
class MouseEvent;
class Element:
public SGPropertyChangeListener
{
public:
typedef std::map<std::string, const SGPropertyNode*> Style;
typedef boost::function<void(const SGPropertyNode*)> StyleSetter;
typedef std::map<std::string, StyleSetter> StyleSetters;
/**
* Remove the property listener of the element.
*
* You will need to call the appropriate methods (#childAdded,
* #childRemoved, #valueChanged) yourself to ensure the element still
* receives the needed events.
*/
void removeListener();
/**
*
*/
virtual ~Element() = 0;
/**
* Called every frame to update internal state
*
* @param dt Frame time in seconds
*/
virtual void update(double dt);
/**
* Handle mouse event (transforms coordinates to local coordinate frame
* and forwards event to #handleLocalMouseEvent)
*/
virtual bool handleMouseEvent(const canvas::MouseEvent& event);
osg::ref_ptr<osg::MatrixTransform> getMatrixTransform();
virtual void childAdded( SGPropertyNode * parent,
SGPropertyNode * child );
virtual void childRemoved( SGPropertyNode * parent,
SGPropertyNode * child );
virtual void valueChanged(SGPropertyNode * child);
/**
* Write the given bounding box to the property tree
*/
void setBoundingBox(const osg::BoundingBox& bb);
protected:
enum Attributes
{
LAST_ATTRIBUTE = 0x0001
};
enum TransformType
{
TT_NONE,
TT_MATRIX,
TT_TRANSLATE,
TT_ROTATE,
TT_SCALE
};
CanvasWeakPtr _canvas;
uint32_t _attributes_dirty;
bool _transform_dirty;
osg::ref_ptr<osg::MatrixTransform> _transform;
std::vector<TransformType> _transform_types;
SGPropertyNode_ptr _node;
Style _style;
StyleSetters _style_setters;
std::vector<SGPropertyNode_ptr> _bounding_box;
Element( const CanvasWeakPtr& canvas,
SGPropertyNode_ptr node,
const Style& parent_style );
template<typename T, class C1, class C2>
Element::StyleSetter
addStyle(const std::string& name, void (C1::*setter)(T), C2 instance)
{
return _style_setters[ name ] =
bindStyleSetter<T>(name, setter, instance);
}
template<typename T1, typename T2, class C1, class C2>
Element::StyleSetter
addStyle(const std::string& name, void (C1::*setter)(T2), C2 instance)
{
return _style_setters[ name ] =
bindStyleSetter<T1>(name, setter, instance);
}
template<class C1, class C2>
Element::StyleSetter
addStyle( const std::string& name,
void (C1::*setter)(const std::string&),
C2 instance )
{
return _style_setters[ name ] =
bindStyleSetter<const char*>(name, setter, instance);
}
template<typename T1, typename T2, class C1, class C2>
Element::StyleSetter
bindStyleSetter( const std::string& name,
void (C1::*setter)(T2),
C2 instance )
{
return boost::bind(setter, instance, boost::bind(&getValue<T1>, _1));
}
virtual bool handleLocalMouseEvent(const canvas::MouseEvent& event);
virtual void childAdded(SGPropertyNode * child) {}
virtual void childRemoved(SGPropertyNode * child){}
virtual void childChanged(SGPropertyNode * child){}
void setDrawable(osg::Drawable* drawable);
void setupStyle();
bool setStyle(const SGPropertyNode* child);
private:
osg::ref_ptr<osg::Drawable> _drawable;
Element(const Element&);// = delete
};
} // namespace canvas
} // namespace simgear
#endif /* CANVAS_ELEMENT_HXX_ */

View File

@@ -0,0 +1,219 @@
// A group of 2D Canvas elements
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CanvasGroup.hxx"
#include "CanvasImage.hxx"
#include "CanvasMap.hxx"
#include "CanvasPath.hxx"
#include "CanvasText.hxx"
#include <boost/foreach.hpp>
namespace simgear
{
namespace canvas
{
//----------------------------------------------------------------------------
Group::Group( const CanvasWeakPtr& canvas,
SGPropertyNode_ptr node,
const Style& parent_style ):
Element(canvas, node, parent_style)
{
}
//----------------------------------------------------------------------------
Group::~Group()
{
}
//----------------------------------------------------------------------------
void Group::update(double dt)
{
BOOST_FOREACH( ChildList::value_type child, _children )
child.second->update(dt);
Element::update(dt);
}
//----------------------------------------------------------------------------
bool Group::handleLocalMouseEvent(const canvas::MouseEvent& event)
{
// Iterate in reverse order as last child is displayed on top
BOOST_REVERSE_FOREACH( ChildList::value_type child, _children )
{
if( child.second->handleMouseEvent(event) )
return true;
}
return false;
}
//----------------------------------------------------------------------------
void Group::childAdded(SGPropertyNode* child)
{
if( child->getParent() != _node )
return;
boost::shared_ptr<Element> element;
// TODO create map of child factories and use also to check for element
// on deletion in ::childRemoved
if( child->getNameString() == "text" )
element.reset( new Text(_canvas, child, _style) );
else if( child->getNameString() == "group" )
element.reset( new Group(_canvas, child, _style) );
else if( child->getNameString() == "map" )
element.reset( new Map(_canvas, child, _style) );
else if( child->getNameString() == "path" )
element.reset( new Path(_canvas, child, _style) );
else if( child->getNameString() == "image" )
element.reset( new Image(_canvas, child, _style) );
if( element )
{
// Add to osg scene graph...
_transform->addChild( element->getMatrixTransform() );
_children.push_back( ChildList::value_type(child, element) );
return;
}
_style[ child->getNameString() ] = child;
}
//----------------------------------------------------------------------------
struct ChildFinder
{
public:
ChildFinder(SGPropertyNode *node):
_node(node)
{}
bool operator()(const Group::ChildList::value_type& el) const
{
return el.first == _node;
}
private:
SGPropertyNode *_node;
};
//----------------------------------------------------------------------------
void Group::childRemoved(SGPropertyNode* node)
{
if( node->getParent() != _node )
return;
if( node->getNameString() == "text"
|| node->getNameString() == "group"
|| node->getNameString() == "map"
|| node->getNameString() == "path"
|| node->getNameString() == "image" )
{
ChildFinder pred(node);
ChildList::iterator child =
std::find_if(_children.begin(), _children.end(), pred);
if( child == _children.end() )
SG_LOG
(
SG_GL,
SG_WARN,
"can't removed unknown child " << node->getDisplayName()
);
else
{
_transform->removeChild( child->second->getMatrixTransform() );
_children.erase(child);
}
}
else
{
Style::iterator style = _style.find(node->getNameString());
if( style != _style.end() )
_style.erase(style);
}
}
//----------------------------------------------------------------------------
void Group::childChanged(SGPropertyNode* node)
{
if( node->getParent()->getParent() == _node
&& node->getNameString() == "z-index" )
return handleZIndexChanged(node->getParent(), node->getIntValue());
}
//----------------------------------------------------------------------------
void Group::handleZIndexChanged(SGPropertyNode* node, int z_index)
{
ChildFinder pred(node);
ChildList::iterator child =
std::find_if(_children.begin(), _children.end(), pred);
if( child == _children.end() )
return;
osg::Node* tf = child->second->getMatrixTransform();
int index = _transform->getChildIndex(tf),
index_new = index;
ChildList::iterator next = child;
++next;
while( next != _children.end()
&& next->first->getIntValue("z-index", 0) <= z_index )
{
++index_new;
++next;
}
if( index_new != index )
{
_children.insert(next, *child);
}
else
{
ChildList::iterator prev = child;
while( prev != _children.begin()
&& (--prev)->first->getIntValue("z-index", 0) > z_index)
{
--index_new;
}
if( index == index_new )
return;
_children.insert(prev, *child);
}
_transform->removeChild(index);
_transform->insertChild(index_new, tf);
_children.erase(child);
SG_LOG
(
SG_GENERAL,
SG_INFO,
"canvas::Group: Moved element " << index << " to position " << index_new
);
}
} // namespace canvas
} // namespace simgear

View File

@@ -0,0 +1,67 @@
// A group of 2D Canvas elements
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_GROUP_HXX_
#define CANVAS_GROUP_HXX_
#include "CanvasElement.hxx"
#include <boost/shared_ptr.hpp>
#include <list>
#include <map>
namespace simgear
{
namespace canvas
{
typedef boost::shared_ptr<Element> ElementPtr;
class Group:
public Element
{
public:
typedef std::list< std::pair< const SGPropertyNode*,
ElementPtr
>
> ChildList;
Group( const CanvasWeakPtr& canvas,
SGPropertyNode_ptr node,
const Style& parent_style = Style() );
virtual ~Group();
virtual void update(double dt);
protected:
ChildList _children;
virtual bool handleLocalMouseEvent(const canvas::MouseEvent& event);
virtual void childAdded(SGPropertyNode * child);
virtual void childRemoved(SGPropertyNode * child);
virtual void childChanged(SGPropertyNode * child);
void handleZIndexChanged(SGPropertyNode* node, int z_index);
};
} // namespace canvas
} // namespace simgear
#endif /* CANVAS_GROUP_HXX_ */

View File

@@ -0,0 +1,352 @@
// An image on the Canvas
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CanvasImage.hxx"
#include <simgear/canvas/Canvas.hxx>
#include <simgear/canvas/CanvasMgr.hxx>
#include <simgear/canvas/CanvasSystemAdapter.hxx>
#include <simgear/misc/parse_color.hxx>
#include <simgear/misc/sg_path.hxx>
#include <osg/Array>
#include <osg/Geometry>
#include <osg/PrimitiveSet>
#include <boost/algorithm/string/predicate.hpp>
namespace simgear
{
namespace canvas
{
/**
* Callback to enable/disable rendering of canvas displayed inside windows or
* other canvases.
*/
class CullCallback:
public osg::Drawable::CullCallback
{
public:
CullCallback(const CanvasWeakPtr& canvas);
private:
CanvasWeakPtr _canvas;
virtual bool cull( osg::NodeVisitor* nv,
osg::Drawable* drawable,
osg::RenderInfo* renderInfo ) const;
};
//----------------------------------------------------------------------------
CullCallback::CullCallback(const CanvasWeakPtr& canvas):
_canvas( canvas )
{
}
//----------------------------------------------------------------------------
bool CullCallback::cull( osg::NodeVisitor* nv,
osg::Drawable* drawable,
osg::RenderInfo* renderInfo ) const
{
if( !_canvas.expired() )
_canvas.lock()->enableRendering();
// TODO check if window/image should be culled
return false;
}
//----------------------------------------------------------------------------
Image::Image( const CanvasWeakPtr& canvas,
SGPropertyNode_ptr node,
const Style& parent_style ):
Element(canvas, node, parent_style),
_texture(new osg::Texture2D),
_node_src_rect( node->getNode("source", 0, true) )
{
_geom = new osg::Geometry;
_geom->setUseDisplayList(false);
osg::StateSet *stateSet = _geom->getOrCreateStateSet();
stateSet->setTextureAttributeAndModes(0, _texture.get());
stateSet->setDataVariance(osg::Object::STATIC);
// allocate arrays for the image
_vertices = new osg::Vec3Array(4);
_vertices->setDataVariance(osg::Object::STATIC);
_geom->setVertexArray(_vertices);
_texCoords = new osg::Vec2Array(4);
_texCoords->setDataVariance(osg::Object::STATIC);
_geom->setTexCoordArray(0, _texCoords);
_colors = new osg::Vec4Array(4);
_colors->setDataVariance(osg::Object::STATIC);
_geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
_geom->setColorArray(_colors);
osg::DrawArrays* prim = new osg::DrawArrays(osg::PrimitiveSet::QUADS);
prim->set(osg::PrimitiveSet::QUADS, 0, 4);
prim->setDataVariance(osg::Object::STATIC);
_geom->addPrimitiveSet(prim);
setDrawable(_geom);
addStyle("fill", &Image::setFill, this);
setFill("#ffffff"); // TODO how should we handle default values?
setupStyle();
}
//----------------------------------------------------------------------------
Image::~Image()
{
}
//----------------------------------------------------------------------------
void Image::update(double dt)
{
Element::update(dt);
if( _attributes_dirty & DEST_SIZE )
{
(*_vertices)[0].set(_region.l(), _region.t(), 0);
(*_vertices)[1].set(_region.r(), _region.t(), 0);
(*_vertices)[2].set(_region.r(), _region.b(), 0);
(*_vertices)[3].set(_region.l(), _region.b(), 0);
_vertices->dirty();
_attributes_dirty &= ~DEST_SIZE;
_geom->dirtyBound();
setBoundingBox(_geom->getBound());
}
if( _attributes_dirty & SRC_RECT )
{
double u0 = _src_rect.l(),
u1 = _src_rect.r(),
v0 = _src_rect.b(),
v1 = _src_rect.t();
if( !_node_src_rect->getBoolValue("normalized", true) )
{
const Rect<int>& tex_dim = getTextureDimensions();
u0 /= tex_dim.width();
u1 /= tex_dim.width();
v0 /= tex_dim.height();
v1 /= tex_dim.height();
}
(*_texCoords)[0].set(u0, v0);
(*_texCoords)[1].set(u1, v0);
(*_texCoords)[2].set(u1, v1);
(*_texCoords)[3].set(u0, v1);
_texCoords->dirty();
_attributes_dirty &= ~SRC_RECT;
}
}
//----------------------------------------------------------------------------
void Image::setSrcCanvas(CanvasPtr canvas)
{
if( !_src_canvas.expired() )
_src_canvas.lock()->removeDependentCanvas(_canvas);
_src_canvas = canvas;
_geom->getOrCreateStateSet()
->setTextureAttribute(0, canvas ? canvas->getTexture() : 0);
_geom->setCullCallback(canvas ? new CullCallback(canvas) : 0);
if( !_src_canvas.expired() )
{
setupDefaultDimensions();
_src_canvas.lock()->addDependentCanvas(_canvas);
}
}
//----------------------------------------------------------------------------
CanvasWeakPtr Image::getSrcCanvas() const
{
return _src_canvas;
}
//----------------------------------------------------------------------------
void Image::setImage(osg::Image *img)
{
// remove canvas...
setSrcCanvas( CanvasPtr() );
_texture->setImage(img);
_geom->getOrCreateStateSet()
->setTextureAttributeAndModes(0, _texture);
if( img )
setupDefaultDimensions();
}
//----------------------------------------------------------------------------
void Image::setFill(const std::string& fill)
{
osg::Vec4 color;
if( !parseColor(fill, color) )
return;
for( int i = 0; i < 4; ++i )
(*_colors)[i] = color;
_colors->dirty();
}
//----------------------------------------------------------------------------
const Rect<float>& Image::getRegion() const
{
return _region;
}
//----------------------------------------------------------------------------
void Image::childChanged(SGPropertyNode* child)
{
const std::string& name = child->getNameString();
if( child->getParent() == _node_src_rect )
{
_attributes_dirty |= SRC_RECT;
if( name == "left" )
_src_rect.setLeft( child->getFloatValue() );
else if( name == "right" )
_src_rect.setRight( child->getFloatValue() );
else if( name == "top" )
_src_rect.setTop( child->getFloatValue() );
else if( name == "bottom" )
_src_rect.setBottom( child->getFloatValue() );
return;
}
else if( child->getParent() != _node )
return;
if( name == "x" )
{
_region.setX( child->getFloatValue() );
_attributes_dirty |= DEST_SIZE;
}
else if( name == "y" )
{
_region.setY( child->getFloatValue() );
_attributes_dirty |= DEST_SIZE;
}
else if( name == "size" )
{
if( child->getIndex() == 0 )
_region.setWidth( child->getFloatValue() );
else
_region.setHeight( child->getFloatValue() );
_attributes_dirty |= DEST_SIZE;
}
else if( name == "file" )
{
static const std::string CANVAS_PROTOCOL = "canvas://";
const std::string& path = child->getStringValue();
CanvasPtr canvas = _canvas.lock();
if( !canvas )
{
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No canvas available");
return;
}
if( boost::starts_with(path, CANVAS_PROTOCOL) )
{
CanvasMgr* canvas_mgr = canvas->getCanvasMgr();
if( !canvas_mgr )
{
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: Failed to get CanvasMgr");
return;
}
const SGPropertyNode* canvas_node =
canvas_mgr->getPropertyRoot()
->getParent()
->getNode( path.substr(CANVAS_PROTOCOL.size()) );
if( !canvas_node )
{
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: No such canvas: " << path);
return;
}
// TODO add support for other means of addressing canvases (eg. by
// name)
CanvasPtr src_canvas = canvas_mgr->getCanvas( canvas_node->getIndex() );
if( !src_canvas )
{
SG_LOG(SG_GL, SG_ALERT, "canvas::Image: Invalid canvas: " << path);
return;
}
setSrcCanvas(src_canvas);
}
else
{
setImage( canvas->getSystemAdapter()->getImage(path) );
}
}
}
//----------------------------------------------------------------------------
void Image::setupDefaultDimensions()
{
if( !_src_rect.width() || !_src_rect.height() )
{
const Rect<int>& tex_dim = getTextureDimensions();
_node_src_rect->setBoolValue("normalized", false);
_node_src_rect->setFloatValue("right", tex_dim.width());
_node_src_rect->setFloatValue("bottom", tex_dim.height());
}
if( !_region.width() || !_region.height() )
{
_node->setFloatValue("size[0]", _src_rect.width());
_node->setFloatValue("size[1]", _src_rect.height());
}
}
//----------------------------------------------------------------------------
Rect<int> Image::getTextureDimensions() const
{
osg::Texture2D *texture = !_src_canvas.expired()
? _src_canvas.lock()->getTexture()
: _texture.get();
if( !texture )
return Rect<int>();
return Rect<int>
(
0,0,
texture->getTextureWidth(),
texture->getTextureHeight()
);
}
} // namespace canvas
} // namespace simgear

View File

@@ -0,0 +1,89 @@
// An image on the Canvas
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_IMAGE_HXX_
#define CANVAS_IMAGE_HXX_
#include "CanvasElement.hxx"
#include <simgear/canvas/canvas_fwd.hxx>
#include <simgear/math/Rect.hxx>
#include <osg/Texture2D>
namespace simgear
{
namespace canvas
{
class Image:
public Element
{
public:
/**
* @param node Property node containing settings for this image:
* rect/[left/right/top/bottom] Dimensions of source
* rect
* size[0-1] Dimensions of rectangle
* [x,y] Position of rectangle
*/
Image( const CanvasWeakPtr& canvas,
SGPropertyNode_ptr node,
const Style& parent_style );
virtual ~Image();
virtual void update(double dt);
void setSrcCanvas(CanvasPtr canvas);
CanvasWeakPtr getSrcCanvas() const;
void setImage(osg::Image *img);
void setFill(const std::string& fill);
const Rect<float>& getRegion() const;
protected:
enum ImageAttributes
{
SRC_RECT = LAST_ATTRIBUTE << 1, // Source image rectangle
DEST_SIZE = SRC_RECT << 1 // Element size
};
virtual void childChanged(SGPropertyNode * child);
void setupDefaultDimensions();
Rect<int> getTextureDimensions() const;
osg::ref_ptr<osg::Texture2D> _texture;
// TODO optionally forward events to canvas
CanvasWeakPtr _src_canvas;
osg::ref_ptr<osg::Geometry> _geom;
osg::ref_ptr<osg::Vec3Array> _vertices;
osg::ref_ptr<osg::Vec2Array> _texCoords;
osg::ref_ptr<osg::Vec4Array> _colors;
SGPropertyNode *_node_src_rect;
Rect<float> _src_rect,
_region;
};
} // namespace canvas
} // namespace canvas
#endif /* CANVAS_IMAGE_HXX_ */

View File

@@ -0,0 +1,230 @@
// A group of 2D Canvas elements which get automatically transformed according
// to the map parameters.
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CanvasMap.hxx"
#include "map/geo_node_pair.hxx"
#include "map/projection.hxx"
#include <cmath>
#include <boost/algorithm/string/predicate.hpp>
#define LOG_GEO_RET(msg) \
{\
SG_LOG\
(\
SG_GENERAL,\
SG_WARN,\
msg << " (" << child->getStringValue()\
<< ", " << child->getPath() << ")"\
);\
return;\
}
namespace simgear
{
namespace canvas
{
const std::string GEO = "-geo";
//----------------------------------------------------------------------------
Map::Map( const CanvasWeakPtr& canvas,
SGPropertyNode_ptr node,
const Style& parent_style ):
Group(canvas, node, parent_style),
// TODO make projection configurable
_projection(new SansonFlamsteedProjection),
_projection_dirty(true)
{
}
//----------------------------------------------------------------------------
Map::~Map()
{
}
//----------------------------------------------------------------------------
void Map::update(double dt)
{
for( GeoNodes::iterator it = _geo_nodes.begin();
it != _geo_nodes.end();
++it )
{
GeoNodePair* geo_node = it->second.get();
if( !geo_node->isComplete()
|| (!geo_node->isDirty() && !_projection_dirty) )
continue;
GeoCoord lat = parseGeoCoord(geo_node->getLat());
if( lat.type != GeoCoord::LATITUDE )
continue;
GeoCoord lon = parseGeoCoord(geo_node->getLon());
if( lon.type != GeoCoord::LONGITUDE )
continue;
Projection::ScreenPosition pos =
_projection->worldToScreen(lat.value, lon.value);
geo_node->setScreenPos(pos.x, pos.y);
// geo_node->print();
geo_node->setDirty(false);
}
_projection_dirty = false;
Group::update(dt);
}
//----------------------------------------------------------------------------
void Map::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
{
if( !boost::ends_with(child->getNameString(), GEO) )
return Element::childAdded(parent, child);
_geo_nodes[child].reset(new GeoNodePair());
}
//----------------------------------------------------------------------------
void Map::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
{
if( !boost::ends_with(child->getNameString(), GEO) )
return Element::childRemoved(parent, child);
// TODO remove from other node
_geo_nodes.erase(child);
}
//----------------------------------------------------------------------------
void Map::valueChanged(SGPropertyNode * child)
{
const std::string& name = child->getNameString();
if( !boost::ends_with(name, GEO) )
return Group::valueChanged(child);
GeoNodes::iterator it_geo_node = _geo_nodes.find(child);
if( it_geo_node == _geo_nodes.end() )
LOG_GEO_RET("geo node not found!")
GeoNodePair* geo_node = it_geo_node->second.get();
geo_node->setDirty();
if( geo_node->getStatus() & GeoNodePair::INCOMPLETE )
{
// Detect lat, lon tuples...
GeoCoord coord = parseGeoCoord(child->getStringValue());
int index_other = -1;
switch( coord.type )
{
case GeoCoord::LATITUDE:
index_other = child->getIndex() + 1;
geo_node->setNodeLat(child);
break;
case GeoCoord::LONGITUDE:
index_other = child->getIndex() - 1;
geo_node->setNodeLon(child);
break;
default:
LOG_GEO_RET("Invalid geo coord")
}
SGPropertyNode *other = child->getParent()->getChild(name, index_other);
if( !other )
return;
GeoCoord coord_other = parseGeoCoord(other->getStringValue());
if( coord_other.type == GeoCoord::INVALID
|| coord_other.type == coord.type )
return;
GeoNodes::iterator it_geo_node_other = _geo_nodes.find(other);
if( it_geo_node_other == _geo_nodes.end() )
LOG_GEO_RET("other geo node not found!")
GeoNodePair* geo_node_other = it_geo_node_other->second.get();
// Let use both nodes use the same GeoNodePair instance
if( geo_node_other != geo_node )
it_geo_node_other->second = it_geo_node->second;
if( coord_other.type == GeoCoord::LATITUDE )
geo_node->setNodeLat(other);
else
geo_node->setNodeLon(other);
// Set name for resulting screen coordinate nodes
geo_node->setTargetName( name.substr(0, name.length() - GEO.length()) );
}
}
//----------------------------------------------------------------------------
void Map::childChanged(SGPropertyNode * child)
{
if( child->getParent() != _node )
return Group::childChanged(child);
if( child->getNameString() == "ref-lat"
|| child->getNameString() == "ref-lon" )
_projection->setWorldPosition( _node->getDoubleValue("ref-lat"),
_node->getDoubleValue("ref-lon") );
else if( child->getNameString() == "hdg" )
_projection->setOrientation(child->getFloatValue());
else if( child->getNameString() == "range" )
_projection->setRange(child->getDoubleValue());
else
return Group::childChanged(child);
_projection_dirty = true;
}
//----------------------------------------------------------------------------
Map::GeoCoord Map::parseGeoCoord(const std::string& val) const
{
GeoCoord coord;
if( val.length() < 2 )
return coord;
if( val[0] == 'N' || val[0] == 'S' )
coord.type = GeoCoord::LATITUDE;
else if( val[0] == 'E' || val[0] == 'W' )
coord.type = GeoCoord::LONGITUDE;
else
return coord;
char* end;
coord.value = strtod(&val[1], &end);
if( end != &val[val.length()] )
{
coord.type = GeoCoord::INVALID;
return coord;
}
if( val[0] == 'S' || val[0] == 'W' )
coord.value *= -1;
return coord;
}
} // namespace canvas
} // namespace simgear

View File

@@ -0,0 +1,82 @@
// A group of 2D Canvas elements which get automatically transformed according
// to the map parameters.
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_MAP_HXX_
#define CANVAS_MAP_HXX_
#include "CanvasGroup.hxx"
#include <boost/shared_ptr.hpp>
#include <boost/unordered_map.hpp>
namespace simgear
{
namespace canvas
{
class GeoNodePair;
class HorizontalProjection;
class Map:
public Group
{
public:
Map( const CanvasWeakPtr& canvas,
SGPropertyNode_ptr node,
const Style& parent_style );
virtual ~Map();
virtual void update(double dt);
virtual void childAdded( SGPropertyNode * parent,
SGPropertyNode * child );
virtual void childRemoved( SGPropertyNode * parent,
SGPropertyNode * child );
virtual void valueChanged(SGPropertyNode * child);
protected:
virtual void childChanged(SGPropertyNode * child);
typedef boost::unordered_map< SGPropertyNode*,
boost::shared_ptr<GeoNodePair>
> GeoNodes;
GeoNodes _geo_nodes;
boost::shared_ptr<HorizontalProjection> _projection;
bool _projection_dirty;
struct GeoCoord
{
GeoCoord():
type(INVALID)
{}
enum
{
INVALID,
LATITUDE,
LONGITUDE
} type;
double value;
};
GeoCoord parseGeoCoord(const std::string& val) const;
};
} // namespace canvas
} // namespace simgear
#endif /* CANVAS_MAP_HXX_ */

View File

@@ -0,0 +1,439 @@
// An OpenVG path on the Canvas
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CanvasPath.hxx"
#include <simgear/misc/parse_color.hxx>
#include <osg/Drawable>
#include <osg/BlendFunc>
#include <vg/openvg.h>
#include <cassert>
namespace simgear
{
namespace canvas
{
typedef std::vector<VGubyte> CmdList;
typedef std::vector<VGfloat> CoordList;
/**
* Helper to split and convert comma/whitespace separated floating point
* values
*/
std::vector<float> splitAndConvert(const char del[], const std::string& str);
class Path::PathDrawable:
public osg::Drawable
{
public:
PathDrawable(Path* path):
_path_element(path),
_path(VG_INVALID_HANDLE),
_paint(VG_INVALID_HANDLE),
_paint_fill(VG_INVALID_HANDLE),
_attributes_dirty(~0),
_mode(0),
_fill_rule(VG_EVEN_ODD),
_stroke_width(1),
_stroke_linecap(VG_CAP_BUTT)
{
setSupportsDisplayList(false);
setDataVariance(Object::DYNAMIC);
setUpdateCallback(new PathUpdateCallback());
}
virtual ~PathDrawable()
{
if( _path != VG_INVALID_HANDLE )
vgDestroyPath(_path);
if( _paint != VG_INVALID_HANDLE )
vgDestroyPaint(_paint);
if( _paint_fill != VG_INVALID_HANDLE )
vgDestroyPaint(_paint_fill);
}
virtual const char* className() const { return "PathDrawable"; }
virtual osg::Object* cloneType() const { return new PathDrawable(_path_element); }
virtual osg::Object* clone(const osg::CopyOp&) const { return new PathDrawable(_path_element); }
/**
* Replace the current path segments with the new ones
*
* @param cmds List of OpenVG path commands
* @param coords List of coordinates/parameters used by #cmds
*/
void setSegments(const CmdList& cmds, const CoordList& coords)
{
_cmds = cmds;
_coords = coords;
_attributes_dirty |= (PATH | BOUNDING_BOX);
}
/**
* Set path fill paint ("none" if not filled)
*/
void setFill(const std::string& fill)
{
if( fill == "none" )
{
_mode &= ~VG_FILL_PATH;
}
else if( parseColor(fill, _fill_color) )
{
_mode |= VG_FILL_PATH;
_attributes_dirty |= FILL_COLOR;
}
else
{
SG_LOG
(
SG_GENERAL,
SG_WARN,
"canvas::Path Unknown fill: " << fill
);
}
}
/**
* Set path fill rule ("pseudo-nonzero" or "evenodd")
*
* @warning As the current nonzero implementation causes sever artifacts
* for every concave path we call it pseudo-nonzero, so that
* everyone is warned that it won't work as expected :)
*/
void setFillRule(const std::string& fill_rule)
{
if( fill_rule == "pseudo-nonzero" )
_fill_rule = VG_NON_ZERO;
else // if( fill_rule == "evenodd" )
_fill_rule = VG_EVEN_ODD;
}
/**
* Set path stroke paint ("none" if no stroke)
*/
void setStroke(const std::string& stroke)
{
if( stroke == "none" )
{
_mode &= ~VG_STROKE_PATH;
}
else if( parseColor(stroke, _stroke_color) )
{
_mode |= VG_STROKE_PATH;
_attributes_dirty |= STROKE_COLOR;
}
else
{
SG_LOG
(
SG_GENERAL,
SG_WARN,
"canvas::Path Unknown stroke: " << stroke
);
}
}
/**
* Set stroke width
*/
void setStrokeWidth(float width)
{
_stroke_width = width;
_attributes_dirty |= BOUNDING_BOX;
}
/**
* Set stroke dash (line stipple)
*/
void setStrokeDashArray(const std::string& dash)
{
_stroke_dash = splitAndConvert(",\t\n ", dash);
}
/**
* Set stroke-linecap
*
* @see http://www.w3.org/TR/SVG/painting.html#StrokeLinecapProperty
*/
void setStrokeLinecap(const std::string& linecap)
{
if( linecap == "round" )
_stroke_linecap = VG_CAP_ROUND;
else if( linecap == "square" )
_stroke_linecap = VG_CAP_SQUARE;
else
_stroke_linecap = VG_CAP_BUTT;
}
/**
* Draw callback
*/
virtual void drawImplementation(osg::RenderInfo& renderInfo) const
{
if( _attributes_dirty & PATH )
return;
osg::State* state = renderInfo.getState();
assert(state);
state->setActiveTextureUnit(0);
state->setClientActiveTextureUnit(0);
state->disableAllVertexArrays();
glPushAttrib(~0u); // Don't use GL_ALL_ATTRIB_BITS as on my machine it
// eg. doesn't include GL_MULTISAMPLE_BIT
glPushClientAttrib(~0u);
// Initialize/Update the paint
if( _attributes_dirty & STROKE_COLOR )
{
if( _paint == VG_INVALID_HANDLE )
_paint = vgCreatePaint();
vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, _stroke_color._v);
_attributes_dirty &= ~STROKE_COLOR;
}
// Initialize/update fill paint
if( _attributes_dirty & FILL_COLOR )
{
if( _paint_fill == VG_INVALID_HANDLE )
_paint_fill = vgCreatePaint();
vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, _fill_color._v);
_attributes_dirty &= ~FILL_COLOR;
}
// Setup paint
if( _mode & VG_STROKE_PATH )
{
vgSetPaint(_paint, VG_STROKE_PATH);
vgSetf(VG_STROKE_LINE_WIDTH, _stroke_width);
vgSeti(VG_STROKE_CAP_STYLE, _stroke_linecap);
vgSetfv( VG_STROKE_DASH_PATTERN,
_stroke_dash.size(),
_stroke_dash.empty() ? 0 : &_stroke_dash[0] );
}
if( _mode & VG_FILL_PATH )
{
vgSetPaint(_paint_fill, VG_FILL_PATH);
vgSeti(VG_FILL_RULE, _fill_rule);
}
// And finally draw the path
if( _mode )
vgDrawPath(_path, _mode);
VGErrorCode err = vgGetError();
if( err != VG_NO_ERROR )
SG_LOG(SG_GL, SG_ALERT, "vgError: " << err);
glPopAttrib();
glPopClientAttrib();
}
/**
* Compute the bounding box
*/
virtual osg::BoundingBox computeBound() const
{
if( _path == VG_INVALID_HANDLE || (_attributes_dirty & PATH) )
return osg::BoundingBox();
VGfloat min[2], size[2];
vgPathBounds(_path, &min[0], &min[1], &size[0], &size[1]);
_attributes_dirty &= ~BOUNDING_BOX;
// vgPathBounds doesn't take stroke width into account
float ext = 0.5 * _stroke_width;
osg::BoundingBox bb
(
min[0] - ext, min[1] - ext, -0.1,
min[0] + size[0] + ext, min[1] + size[1] + ext, 0.1
);
_path_element->setBoundingBox(bb);
return bb;
}
private:
enum Attributes
{
PATH = 0x0001,
STROKE_COLOR = PATH << 1,
FILL_COLOR = STROKE_COLOR << 1,
BOUNDING_BOX = FILL_COLOR << 1
};
Path *_path_element;
mutable VGPath _path;
mutable VGPaint _paint;
mutable VGPaint _paint_fill;
mutable uint32_t _attributes_dirty;
CmdList _cmds;
CoordList _coords;
VGbitfield _mode;
osg::Vec4f _fill_color;
VGFillRule _fill_rule;
osg::Vec4f _stroke_color;
VGfloat _stroke_width;
std::vector<VGfloat> _stroke_dash;
VGCapStyle _stroke_linecap;
/**
* Initialize/Update the OpenVG path
*/
void update()
{
if( _attributes_dirty & PATH )
{
const VGbitfield caps = VG_PATH_CAPABILITY_APPEND_TO
| VG_PATH_CAPABILITY_MODIFY
| VG_PATH_CAPABILITY_PATH_BOUNDS;
if( _path == VG_INVALID_HANDLE )
_path = vgCreatePath(
VG_PATH_FORMAT_STANDARD,
VG_PATH_DATATYPE_F,
1.f, 0.f, // scale,bias
_cmds.size(), _coords.size(),
caps
);
else
vgClearPath(_path, caps);
if( !_cmds.empty() && !_coords.empty() )
vgAppendPathData(_path, _cmds.size(), &_cmds[0], &_coords[0]);
_attributes_dirty &= ~PATH;
_attributes_dirty |= BOUNDING_BOX;
}
if( _attributes_dirty & BOUNDING_BOX )
dirtyBound();
}
struct PathUpdateCallback:
public osg::Drawable::UpdateCallback
{
virtual void update(osg::NodeVisitor*, osg::Drawable* drawable)
{
static_cast<PathDrawable*>(drawable)->update();
}
};
};
//----------------------------------------------------------------------------
Path::Path( const CanvasWeakPtr& canvas,
SGPropertyNode_ptr node,
const Style& parent_style ):
Element(canvas, node, parent_style),
_path( new PathDrawable(this) )
{
setDrawable(_path);
PathDrawable *path = _path.get();
addStyle("fill", &PathDrawable::setFill, path);
addStyle("fill-rule", &PathDrawable::setFillRule, path);
addStyle("stroke", &PathDrawable::setStroke, path);
addStyle("stroke-width", &PathDrawable::setStrokeWidth, path);
addStyle("stroke-dasharray", &PathDrawable::setStrokeDashArray, path);
addStyle("stroke-linecap", &PathDrawable::setStrokeLinecap, path);
setupStyle();
}
//----------------------------------------------------------------------------
Path::~Path()
{
}
//----------------------------------------------------------------------------
void Path::update(double dt)
{
if( _attributes_dirty & (CMDS | COORDS) )
{
_path->setSegments
(
_node->getChildValues<VGubyte, int>("cmd"),
_node->getChildValues<VGfloat, float>("coord")
);
_attributes_dirty &= ~(CMDS | COORDS);
}
Element::update(dt);
}
//----------------------------------------------------------------------------
void Path::childRemoved(SGPropertyNode* child)
{
childChanged(child);
}
//----------------------------------------------------------------------------
void Path::childChanged(SGPropertyNode* child)
{
if( child->getParent() != _node )
return;
if( child->getNameString() == "cmd" )
_attributes_dirty |= CMDS;
else if( child->getNameString() == "coord" )
_attributes_dirty |= COORDS;
}
//----------------------------------------------------------------------------
std::vector<float> splitAndConvert(const char del[], const std::string& str)
{
std::vector<float> values;
size_t pos = 0;
for(;;)
{
pos = str.find_first_not_of(del, pos);
if( pos == std::string::npos )
break;
char *end = 0;
float val = strtod(&str[pos], &end);
if( end == &str[pos] || !end )
break;
values.push_back(val);
pos = end - &str[0];
}
return values;
}
} // namespace canvas
} // namespace simgear

View File

@@ -0,0 +1,57 @@
// An OpenVG path on the Canvas
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_PATH_HXX_
#define CANVAS_PATH_HXX_
#include "CanvasElement.hxx"
namespace simgear
{
namespace canvas
{
class Path:
public Element
{
public:
Path( const CanvasWeakPtr& canvas,
SGPropertyNode_ptr node,
const Style& parent_style );
virtual ~Path();
virtual void update(double dt);
protected:
enum PathAttributes
{
CMDS = LAST_ATTRIBUTE << 1,
COORDS = CMDS << 1
};
class PathDrawable;
osg::ref_ptr<PathDrawable> _path;
virtual void childRemoved(SGPropertyNode * child);
virtual void childChanged(SGPropertyNode * child);
};
} // namespace canvas
} // namespace simgear
#endif /* CANVAS_PATH_HXX_ */

View File

@@ -0,0 +1,291 @@
// A text on the Canvas
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "CanvasText.hxx"
#include <simgear/canvas/Canvas.hxx>
#include <simgear/canvas/CanvasSystemAdapter.hxx>
#include <simgear/misc/parse_color.hxx>
#include <osgText/Text>
namespace simgear
{
namespace canvas
{
class Text::TextOSG:
public osgText::Text
{
public:
TextOSG(canvas::Text* text);
void setCharacterAspect(float aspect);
void setFill(const std::string& fill);
void setBackgroundColor(const std::string& fill);
osg::Vec2 handleHit(float x, float y);
virtual osg::BoundingBox computeBound() const;
protected:
canvas::Text *_text_element;
};
//----------------------------------------------------------------------------
Text::TextOSG::TextOSG(canvas::Text* text):
_text_element(text)
{
}
//----------------------------------------------------------------------------
void Text::TextOSG::setCharacterAspect(float aspect)
{
setCharacterSize(getCharacterHeight(), aspect);
}
//----------------------------------------------------------------------------
void Text::TextOSG::setFill(const std::string& fill)
{
// if( fill == "none" )
// TODO No text
// else
osg::Vec4 color;
if( parseColor(fill, color) )
setColor( color );
}
//----------------------------------------------------------------------------
void Text::TextOSG::setBackgroundColor(const std::string& fill)
{
osg::Vec4 color;
if( parseColor(fill, color) )
setBoundingBoxColor( color );
}
//----------------------------------------------------------------------------
osg::Vec2 Text::TextOSG::handleHit(float x, float y)
{
float line_height = _characterHeight + _lineSpacing;
// TODO check with align other than TOP
float first_line_y = -0.5 * _lineSpacing;//_offset.y() - _characterHeight;
size_t line = std::max<int>(0, (y - first_line_y) / line_height);
if( _textureGlyphQuadMap.empty() )
return osg::Vec2(-1, -1);
// TODO check when it can be larger
assert( _textureGlyphQuadMap.size() == 1 );
const GlyphQuads& glyphquad = _textureGlyphQuadMap.begin()->second;
const GlyphQuads::Glyphs& glyphs = glyphquad._glyphs;
const GlyphQuads::Coords2& coords = glyphquad._coords;
const GlyphQuads::LineNumbers& line_numbers = glyphquad._lineNumbers;
const float HIT_FRACTION = 0.6;
const float character_width = getCharacterHeight()
* getCharacterAspectRatio();
y = (line + 0.5) * line_height;
bool line_found = false;
for(size_t i = 0; i < line_numbers.size(); ++i)
{
if( line_numbers[i] != line )
{
if( !line_found )
{
if( line_numbers[i] < line )
// Wait for the correct line...
continue;
// We have already passed the correct line -> It's empty...
return osg::Vec2(0, y);
}
// Next line and not returned -> not before any character
// -> return position after last character of line
return osg::Vec2(coords[(i - 1) * 4 + 2].x(), y);
}
line_found = true;
// Get threshold for mouse x position for setting cursor before or after
// current character
float threshold = coords[i * 4].x()
+ HIT_FRACTION * glyphs[i]->getHorizontalAdvance()
* character_width;
if( x <= threshold )
{
if( i == 0 || line_numbers[i - 1] != line )
// first character of line
x = coords[i * 4].x();
else if( coords[(i - 1) * 4].x() == coords[(i - 1) * 4 + 2].x() )
// If previous character width is zero set to begin of next character
// (Happens eg. with spaces)
x = coords[i * 4].x();
else
// position at center between characters
x = 0.5 * (coords[(i - 1) * 4 + 2].x() + coords[i * 4].x());
return osg::Vec2(x, y);
}
}
// Nothing found -> return position after last character
return osg::Vec2
(
coords.back().x(),
(_lineCount - 0.5) * line_height
);
}
//----------------------------------------------------------------------------
osg::BoundingBox Text::TextOSG::computeBound() const
{
osg::BoundingBox bb = osgText::Text::computeBound();
if( !bb.valid() )
return bb;
// TODO bounding box still doesn't seem always right (eg. with center
// horizontal alignment not completely accurate)
bb._min.y() += _offset.y();
bb._max.y() += _offset.y();
_text_element->setBoundingBox(bb);
return bb;
}
//----------------------------------------------------------------------------
Text::Text( const CanvasWeakPtr& canvas,
SGPropertyNode_ptr node,
const Style& parent_style ):
Element(canvas, node, parent_style),
_text( new Text::TextOSG(this) )
{
setDrawable(_text);
_text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
_text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
_text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
addStyle("fill", &TextOSG::setFill, _text);
addStyle("background", &TextOSG::setBackgroundColor, _text);
addStyle("character-size",
static_cast<void (TextOSG::*)(float)>(&TextOSG::setCharacterSize),
_text);
addStyle("character-aspect-ratio", &TextOSG::setCharacterAspect, _text);
addStyle("padding", &TextOSG::setBoundingBoxMargin, _text);
// TEXT = 1 default
// BOUNDINGBOX = 2
// FILLEDBOUNDINGBOX = 4
// ALIGNMENT = 8
addStyle<int>("draw-mode", &TextOSG::setDrawMode, _text);
addStyle("max-width", &TextOSG::setMaximumWidth, _text);
addStyle("font", &Text::setFont, this);
addStyle("alignment", &Text::setAlignment, this);
addStyle("text", &Text::setText, this);
setupStyle();
}
//----------------------------------------------------------------------------
Text::~Text()
{
}
//----------------------------------------------------------------------------
void Text::setText(const char* text)
{
_text->setText(text, osgText::String::ENCODING_UTF8);
}
//----------------------------------------------------------------------------
void Text::setFont(const char* name)
{
_text->setFont( _canvas.lock()->getSystemAdapter()->getFont(name) );
}
//----------------------------------------------------------------------------
void Text::setAlignment(const char* align)
{
const std::string align_string(align);
if( 0 ) return;
#define ENUM_MAPPING(enum_val, string_val) \
else if( align_string == string_val )\
_text->setAlignment( osgText::Text::enum_val );
#include "text-alignment.hxx"
#undef ENUM_MAPPING
else
{
if( !align_string.empty() )
SG_LOG
(
SG_GENERAL,
SG_WARN,
"canvas::Text: unknown alignment '" << align_string << "'"
);
_text->setAlignment(osgText::Text::LEFT_BASE_LINE);
}
}
//----------------------------------------------------------------------------
#if 0
const char* Text::getAlignment() const
{
switch( _text->getAlignment() )
{
#define ENUM_MAPPING(enum_val, string_val) \
case osgText::Text::enum_val:\
return string_val;
#include "text-alignment.hxx"
#undef ENUM_MAPPING
default:
return "unknown";
}
}
#endif
//----------------------------------------------------------------------------
void Text::childChanged(SGPropertyNode* child)
{
if( child->getParent() != _node )
return;
const std::string& name = child->getNameString();
if( name == "hit-y" )
handleHit
(
_node->getFloatValue("hit-x"),
_node->getFloatValue("hit-y")
);
}
//----------------------------------------------------------------------------
void Text::handleHit(float x, float y)
{
const osg::Vec2& pos = _text->handleHit(x, y);
_node->setFloatValue("cursor-x", pos.x());
_node->setFloatValue("cursor-y", pos.y());
}
} // namespace canvas
} // namespace simgear

View File

@@ -0,0 +1,59 @@
// A text on the Canvas
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_TEXT_HXX_
#define CANVAS_TEXT_HXX_
#include "CanvasElement.hxx"
#include <osgText/Text>
#include <map>
#include <vector>
namespace simgear
{
namespace canvas
{
class Text:
public Element
{
public:
Text( const CanvasWeakPtr& canvas,
SGPropertyNode_ptr node,
const Style& parent_style );
~Text();
void setText(const char* text);
void setFont(const char* name);
void setAlignment(const char* align);
protected:
class TextOSG;
osg::ref_ptr<TextOSG> _text;
virtual void childChanged(SGPropertyNode * child);
void handleHit(float x, float y);
};
} // namespace canvas
} // namespace simgear
#endif /* CANVAS_TEXT_HXX_ */

View File

@@ -0,0 +1,134 @@
// Groups together two nodes representing a geographic position (lat + lon)
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_GEO_NODE_PAIR_HXX_
#define CANVAS_GEO_NODE_PAIR_HXX_
namespace simgear
{
namespace canvas
{
class GeoNodePair
{
public:
enum StatusFlags
{
LAT_MISSING = 1,
LON_MISSING = LAT_MISSING << 1,
INCOMPLETE = LAT_MISSING | LON_MISSING,
DIRTY = LON_MISSING << 1
};
GeoNodePair():
_status(INCOMPLETE),
_node_lat(0),
_node_lon(0)
{}
uint8_t getStatus() const
{
return _status;
}
void setDirty(bool flag = true)
{
if( flag )
_status |= DIRTY;
else
_status &= ~DIRTY;
}
bool isDirty() const
{
return (_status & DIRTY)!=0;
}
bool isComplete() const
{
return !(_status & INCOMPLETE);
}
void setNodeLat(SGPropertyNode* node)
{
_node_lat = node;
_status &= ~LAT_MISSING;
if( node == _node_lon )
{
_node_lon = 0;
_status |= LON_MISSING;
}
}
void setNodeLon(SGPropertyNode* node)
{
_node_lon = node;
_status &= ~LON_MISSING;
if( node == _node_lat )
{
_node_lat = 0;
_status |= LAT_MISSING;
}
}
const char* getLat() const
{
return _node_lat ? _node_lat->getStringValue() : "";
}
const char* getLon() const
{
return _node_lon ? _node_lon->getStringValue() : "";
}
void setTargetName(const std::string& name)
{
_target_name = name;
}
void setScreenPos(float x, float y)
{
assert( isComplete() );
SGPropertyNode *parent = _node_lat->getParent();
parent->getChild(_target_name, _node_lat->getIndex(), true)
->setDoubleValue(x);
parent->getChild(_target_name, _node_lon->getIndex(), true)
->setDoubleValue(y);
}
void print()
{
std::cout << "lat=" << (_node_lat ? _node_lat->getPath() : "")
<< ", lon=" << (_node_lon ? _node_lon->getPath() : "")
<< std::endl;
}
private:
uint8_t _status;
SGPropertyNode *_node_lat,
*_node_lon;
std::string _target_name;
};
} // namespace canvas
} // namespace simgear
#endif /* CANVAS_GEO_NODE_PAIR_HXX_ */

View File

@@ -0,0 +1,179 @@
// Geographic projections for Canvas map element
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef CANVAS_MAP_PROJECTION_HXX_
#define CANVAS_MAP_PROJECTION_HXX_
#include <simgear/math/SGMisc.hxx>
namespace simgear
{
namespace canvas
{
/**
* Base class for all projections
*/
class Projection
{
public:
struct ScreenPosition
{
ScreenPosition() {}
ScreenPosition(double x, double y):
x(x),
y(y)
{}
double x, y;
};
virtual ~Projection() {}
void setScreenRange(double range)
{
_screen_range = range;
}
virtual ScreenPosition worldToScreen(double x, double y) = 0;
protected:
double _screen_range;
};
/**
* Base class for horizontal projections
*/
class HorizontalProjection:
public Projection
{
public:
HorizontalProjection():
_cos_rot(1),
_sin_rot(0),
_range(5)
{
setScreenRange(200);
}
/**
* Set world position of center point used for the projection
*/
void setWorldPosition(double lat, double lon)
{
_ref_lat = SGMiscd::deg2rad(lat);
_ref_lon = SGMiscd::deg2rad(lon);
}
/**
* Set up heading
*/
void setOrientation(float hdg)
{
hdg = SGMiscf::deg2rad(hdg);
_sin_rot = sin(hdg);
_cos_rot = cos(hdg);
}
void setRange(double range)
{
_range = range;
}
/**
* Transform given world position to screen position
*
* @param lat Latitude in degrees
* @param lon Longitude in degrees
*/
ScreenPosition worldToScreen(double lat, double lon)
{
lat = SGMiscd::deg2rad(lat);
lon = SGMiscd::deg2rad(lon);
ScreenPosition pos = project(lat, lon);
double scale = _screen_range / _range;
pos.x *= scale;
pos.y *= scale;
return ScreenPosition
(
_cos_rot * pos.x - _sin_rot * pos.y,
-_sin_rot * pos.x - _cos_rot * pos.y
);
}
protected:
/**
* Project given geographic world position to screen space
*
* @param lat Latitude in radians
* @param lon Longitude in radians
*/
virtual ScreenPosition project(double lat, double lon) const = 0;
double _ref_lat,
_ref_lon,
_cos_rot,
_sin_rot,
_range;
};
/**
* Sanson-Flamsteed projection, relative to the projection center
*/
class SansonFlamsteedProjection:
public HorizontalProjection
{
protected:
virtual ScreenPosition project(double lat, double lon) const
{
double d_lat = lat - _ref_lat,
d_lon = lon - _ref_lon;
double r = getEarthRadius(lat);
ScreenPosition pos;
pos.x = r * cos(lat) * d_lon;
pos.y = r * d_lat;
return pos;
}
/**
* Returns Earth radius at a given latitude (Ellipsoide equation with two
* equal axis)
*/
float getEarthRadius(float lat) const
{
const float rec = 6378137.f / 1852; // earth radius, equator (?)
const float rpol = 6356752.314f / 1852; // earth radius, polar (?)
double a = cos(lat) / rec;
double b = sin(lat) / rpol;
return 1.0f / sqrt( a * a + b * b );
}
};
} // namespace canvas
} // namespace simgear
#endif /* CANVAS_MAP_PROJECTION_HXX_ */

View File

@@ -0,0 +1,41 @@
// Mapping between strings and osg text alignment flags
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef ENUM_MAPPING
# error "Only include with ENUM_MAPPING defined!"
#endif
ENUM_MAPPING(LEFT_TOP, "left-top")
ENUM_MAPPING(LEFT_CENTER, "left-center")
ENUM_MAPPING(LEFT_BOTTOM, "left-bottom")
ENUM_MAPPING(CENTER_TOP, "center-top")
ENUM_MAPPING(CENTER_CENTER, "center-center")
ENUM_MAPPING(CENTER_BOTTOM, "center-bottom")
ENUM_MAPPING(RIGHT_TOP, "right-top")
ENUM_MAPPING(RIGHT_CENTER, "right-center")
ENUM_MAPPING(RIGHT_BOTTOM, "right-bottom")
ENUM_MAPPING(LEFT_BASE_LINE, "left-baseline")
ENUM_MAPPING(CENTER_BASE_LINE, "center-baseline")
ENUM_MAPPING(RIGHT_BASE_LINE, "right-baseline")
ENUM_MAPPING(LEFT_BOTTOM_BASE_LINE, "left-bottom-baseline")
ENUM_MAPPING(CENTER_BOTTOM_BASE_LINE, "center-bottom-baseline")
ENUM_MAPPING(RIGHT_BOTTOM_BASE_LINE, "right-bottom-baseline")

View File

@@ -1,7 +1,8 @@
include (SimGearComponent)
set(HEADERS
set(HEADERS
Rect.hxx
SGBox.hxx
SGCMath.hxx
SGGeoc.hxx

85
simgear/math/Rect.hxx Normal file
View File

@@ -0,0 +1,85 @@
// Class representing a rectangular region
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_RECT_HXX_
#define SG_RECT_HXX_
#include <osg/Vec2>
namespace simgear
{
template<typename T>
class Rect
{
public:
Rect():
_x1(0),
_x2(0),
_y1(0),
_y2(0)
{}
Rect(T x, T y, T w, T h):
_x1(x),
_x2(x + w),
_y1(y),
_y2(y + h)
{}
void set(T x, T y, T w, T h)
{
_x1 = x;
_x2 = x + w;
_y1 = y;
_y2 = y + h;
}
T x() const { return _x1; }
T y() const { return _y1; }
T width() const { return _x2 - _x1; }
T height() const { return _y2 - _y1; }
void setX(T x) { T w = width(); _x1 = x; _x2 = x + w; }
void setY(T y) { T h = height(); _y1 = y; _y2 = y + h; }
void setWidth(T w) { _x2 = _x1 + w; }
void setHeight(T h) { _y2 = _y1 + h; }
T l() const { return _x1; }
T r() const { return _x2; }
T t() const { return _y1; }
T b() const { return _y2; }
void setLeft(T l) { _x1 = l; }
void setRight(T r) { _x2 = r; }
void setTop(T t) { _y1 = t; }
void setBottom(T b) { _y2 = b; }
bool contains(T x, T y) const
{
return _x1 <= x && x <= _x2
&& _y1 <= y && y <= _y2;
}
private:
T _x1, _x2, _y1, _y2;
};
} // namespace simgear
#endif /* SG_RECT_HXX_ */

View File

@@ -4,6 +4,7 @@ include (SimGearComponent)
set(HEADERS
ResourceManager.hxx
interpolator.hxx
parse_color.hxx
sg_dir.hxx
sg_path.hxx
sgstream.hxx
@@ -19,6 +20,7 @@ set(HEADERS
set(SOURCES
ResourceManager.cxx
interpolator.cxx
parse_color.cxx
sg_dir.cxx
sg_path.cxx
sgstream.cxx
@@ -39,6 +41,10 @@ add_executable(test_strings strutils_test.cxx )
add_test(test_strings ${EXECUTABLE_OUTPUT_PATH}/test_strings)
target_link_libraries(test_strings SimGearCore)
add_executable(test_parse_color parse_color_test.cxx )
add_test(test_parse_color ${EXECUTABLE_OUTPUT_PATH}/test_parse_color)
target_link_libraries(test_parse_color SimGearCore)
add_executable(test_path path_test.cxx )
add_test(test_path ${EXECUTABLE_OUTPUT_PATH}/test_path)
target_link_libraries(test_path SimGearCore)

View File

@@ -0,0 +1,90 @@
// Parse CSS colors
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "parse_color.hxx"
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/tokenizer.hpp>
namespace simgear
{
//----------------------------------------------------------------------------
bool parseColor(std::string str, osg::Vec4& result)
{
boost::trim(str);
osg::Vec4 color(0,0,0,1);
if( str.empty() )
return false;
// #rrggbb
if( str[0] == '#' )
{
const int offsets[] = {2,2,2};
const boost::offset_separator hex_separator( boost::begin(offsets),
boost::end(offsets) );
typedef boost::tokenizer<boost::offset_separator> offset_tokenizer;
offset_tokenizer tokens(str.begin() + 1, str.end(), hex_separator);
int comp = 0;
for( offset_tokenizer::const_iterator tok = tokens.begin();
tok != tokens.end() && comp < 4;
++tok, ++comp )
{
color._v[comp] = strtol(std::string(*tok).c_str(), 0, 16) / 255.f;
}
}
// rgb(r,g,b)
// rgba(r,g,b,a)
else if( boost::ends_with(str, ")") )
{
const std::string RGB = "rgb(",
RGBA = "rgba(";
size_t pos;
if( boost::starts_with(str, RGB) )
pos = RGB.length();
else if( boost::starts_with(str, RGBA) )
pos = RGBA.length();
else
return false;
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
const boost::char_separator<char> del(", \t\n");
tokenizer tokens(str.begin() + pos, str.end() - 1, del);
int comp = 0;
for( tokenizer::const_iterator tok = tokens.begin();
tok != tokens.end() && comp < 4;
++tok, ++comp )
{
color._v[comp] = boost::lexical_cast<float>(*tok)
// rgb = [0,255], a = [0,1]
/ (comp < 3 ? 255 : 1);
}
}
else
return false;
result = color;
return true;
}
} // namespace simgear

View File

@@ -0,0 +1,40 @@
// Parse CSS colors
//
// Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef PARSE_COLOR_HXX_
#define PARSE_COLOR_HXX_
#include <osg/Vec4>
#include <string>
namespace simgear
{
/**
* Parse a (CSS) color
*
* @param str Text to parse
* @param result Output for parse color
*
* @return Whether str contained a valid color (and result has been modified)
*/
bool parseColor(std::string str, osg::Vec4& result);
} // namespace simgear
#endif /* PARSE_COLOR_HXX_ */

View File

@@ -0,0 +1,35 @@
#include <simgear/compiler.h>
#include "parse_color.hxx"
#include <iostream>
#define COMPARE(a, b) \
if( (a) != (b) ) \
{ \
std::cerr << "failed:" << #a << " != " << #b << std::endl; \
return 1; \
}
#define VERIFY(a) \
if( !(a) ) \
{ \
std::cerr << "failed:" << #a << std::endl; \
return 1; \
}
#define VERIFY_COLOR(str, r, g, b, a) \
VERIFY(simgear::parseColor(str, color)) \
COMPARE(color, osg::Vec4(r, g, b, a))
int main (int ac, char ** av)
{
osg::Vec4 color;
VERIFY_COLOR("#ff0000", 1,0,0,1);
VERIFY_COLOR("#00ff00", 0,1,0,1);
VERIFY_COLOR("#0000ff", 0,0,1,1);
VERIFY_COLOR("rgb( 255,\t127.5,0)", 1, 0.5, 0, 1);
VERIFY_COLOR("rgba(255, 127.5,0, 0.5)", 1, 0.5, 0, 0.5);
std::cout << "all tests passed successfully!" << std::endl;
return 0;
}

View File

@@ -12,9 +12,9 @@
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "PropertyBasedElement.hxx"
@@ -46,4 +46,10 @@ namespace simgear
return _node;
}
//----------------------------------------------------------------------------
void PropertyBasedElement::setSelf(const PropertyBasedElementPtr& self)
{
_self = self;
}
} // namespace simgear

View File

@@ -12,9 +12,9 @@
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_PROPERTY_BASED_ELEMENT_HXX_
#define SG_PROPERTY_BASED_ELEMENT_HXX_
@@ -47,9 +47,9 @@ namespace simgear
SGConstPropertyNode_ptr getProps() const;
SGPropertyNode_ptr getProps();
protected:
virtual void setSelf(const PropertyBasedElementPtr& self);
friend class PropertyBasedMgr;
protected:
SGPropertyNode_ptr _node;
PropertyBasedElementWeakPtr _self;

View File

@@ -12,9 +12,9 @@
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "PropertyBasedMgr.hxx"
@@ -75,7 +75,7 @@ namespace simgear
);
PropertyBasedElementPtr el = _element_factory(child);
el->_self = el;
el->setSelf( el );
_elements[index] = el;
elementCreated( el );
}

View File

@@ -12,9 +12,9 @@
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_PROPERTY_BASED_MGR_HXX_
#define SG_PROPERTY_BASED_MGR_HXX_

View File

@@ -1162,6 +1162,18 @@ public:
T getValue(typename boost::disable_if_c<simgear::props::PropertyTraits<T>::Internal>
::type* dummy = 0) const;
/**
* Get a list of values from all children with the given name
*/
template<typename T, typename T_get /* = T */> // TODO use C++11 or traits
std::vector<T> getChildValues(const std::string& name) const;
/**
* Get a list of values from all children with the given name
*/
template<typename T>
std::vector<T> getChildValues(const std::string& name) const;
/**
* Set a bool value for this node.
*/
@@ -1846,6 +1858,25 @@ inline T SGPropertyNode::getValue(typename boost::enable_if_c<simgear::props
return ::getValue<T>(this);
}
template<typename T, typename T_get /* = T */> // TODO use C++11 or traits
std::vector<T> SGPropertyNode::getChildValues(const std::string& name) const
{
const simgear::PropertyList& props = getChildren(name);
std::vector<T> values( props.size() );
for( size_t i = 0; i < props.size(); ++i )
values[i] = props[i]->getValue<T_get>();
return values;
}
template<typename T>
inline
std::vector<T> SGPropertyNode::getChildValues(const std::string& name) const
{
return getChildValues<T, T>(name);
}
template<typename T>
bool SGPropertyNode::setValue(const T& val,
typename boost::disable_if_c<simgear::props