Canvas: CSS like property value inheritance.
- Groups store property nodes for different styles and pass them to child elements on creation (No dynamic updates yet) - Use StyleSetter map instead of loads of if/else if statements for setting element styles. - Unify element style properties (Only use one property, instead of multiple like it has been with colors) - Fix: Create only one element per property node (Was two)
This commit is contained in:
parent
29e267f18a
commit
ced478cf95
16 changed files with 437 additions and 290 deletions
|
@ -102,6 +102,10 @@ Canvas::Canvas(SGPropertyNode* node):
|
|||
_root_group( new canvas::Group(node) ),
|
||||
_render_always(false)
|
||||
{
|
||||
// Remove automatically created property listener as we forward them on our
|
||||
// own
|
||||
_root_group->removeListener();
|
||||
|
||||
_status = 0;
|
||||
setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
|
||||
|
||||
|
@ -317,6 +321,7 @@ void Canvas::valueChanged(SGPropertyNode* node)
|
|||
if( boost::starts_with(node->getNameString(), "status") )
|
||||
return;
|
||||
|
||||
bool handled = true;
|
||||
if( node->getParent()->getParent() == _node )
|
||||
{
|
||||
if( !_color_background.empty()
|
||||
|
@ -335,6 +340,8 @@ void Canvas::valueChanged(SGPropertyNode* node)
|
|||
|
||||
_dirty_placements.push_back(node->getParent());
|
||||
}
|
||||
else
|
||||
handled = false;
|
||||
}
|
||||
else if( node->getParent() == _node )
|
||||
{
|
||||
|
@ -358,8 +365,13 @@ void Canvas::valueChanged(SGPropertyNode* node)
|
|||
else if( node->getIndex() == 1 )
|
||||
setViewHeight( node->getIntValue() );
|
||||
}
|
||||
else
|
||||
handled = false;
|
||||
}
|
||||
else
|
||||
handled = false;
|
||||
|
||||
if( !handled )
|
||||
_root_group->valueChanged(node);
|
||||
}
|
||||
|
||||
|
|
|
@ -71,8 +71,8 @@ bool CullCallback::cull( osg::NodeVisitor* nv,
|
|||
namespace canvas
|
||||
{
|
||||
//----------------------------------------------------------------------------
|
||||
Image::Image(SGPropertyNode_ptr node):
|
||||
Element(node, COLOR_FILL | BOUNDING_BOX),
|
||||
Image::Image(SGPropertyNode_ptr node, const Style& parent_style):
|
||||
Element(node, parent_style, BOUNDING_BOX),
|
||||
_texture(new osg::Texture2D),
|
||||
_node_src_rect( node->getNode("source", 0, true) )
|
||||
{
|
||||
|
@ -103,6 +103,11 @@ namespace canvas
|
|||
_geom->addPrimitiveSet(prim);
|
||||
|
||||
setDrawable(_geom);
|
||||
|
||||
addStyle("fill", &Image::setFill, this);
|
||||
setFill("#ffffff"); // TODO how should we handle default values?
|
||||
|
||||
setupStyle();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -189,37 +194,44 @@ namespace canvas
|
|||
setupDefaultDimensions();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setFill(const std::string& fill)
|
||||
{
|
||||
osg::Vec4 color = parseColor(fill);
|
||||
for( int i = 0; i < 4; ++i )
|
||||
(*_colors)[i] = color;
|
||||
_colors->dirty();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
const Rect<float>& Image::getRegion() const
|
||||
{
|
||||
return _region;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::valueChanged(SGPropertyNode *node)
|
||||
{
|
||||
if( node->getParent() == _node_src_rect )
|
||||
{
|
||||
_attributes_dirty |= SRC_RECT;
|
||||
|
||||
if( node->getNameString() == "left" )
|
||||
_src_rect.setLeft( node->getFloatValue() );
|
||||
else if( node->getNameString() == "right" )
|
||||
_src_rect.setRight( node->getFloatValue() );
|
||||
else if( node->getNameString() == "top" )
|
||||
_src_rect.setTop( node->getFloatValue() );
|
||||
else if( node->getNameString() == "bottom" )
|
||||
_src_rect.setBottom( node->getFloatValue() );
|
||||
}
|
||||
else
|
||||
Element::valueChanged(node);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
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() );
|
||||
|
@ -289,14 +301,6 @@ namespace canvas
|
|||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::colorFillChanged(const osg::Vec4& color)
|
||||
{
|
||||
for( int i = 0; i < 4; ++i )
|
||||
(*_colors)[i] = color;
|
||||
_colors->dirty();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Image::setupDefaultDimensions()
|
||||
{
|
||||
|
|
|
@ -39,8 +39,8 @@ namespace canvas
|
|||
* size[0-1] Dimensions of rectangle
|
||||
* [x,y] Position of rectangle
|
||||
*/
|
||||
Image(SGPropertyNode_ptr node);
|
||||
~Image();
|
||||
Image(SGPropertyNode_ptr node, const Style& parent_style);
|
||||
virtual ~Image();
|
||||
|
||||
virtual void update(double dt);
|
||||
|
||||
|
@ -48,14 +48,10 @@ namespace canvas
|
|||
CanvasWeakPtr getCanvas() const;
|
||||
|
||||
void setImage(osg::Image *img);
|
||||
void setFill(const std::string& fill);
|
||||
|
||||
const Rect<float>& getRegion() const;
|
||||
|
||||
/**
|
||||
* Callback for every changed child node
|
||||
*/
|
||||
virtual void valueChanged(SGPropertyNode *node);
|
||||
|
||||
protected:
|
||||
|
||||
enum ImageAttributes
|
||||
|
@ -64,13 +60,7 @@ namespace canvas
|
|||
DEST_SIZE = SRC_RECT << 1 // Element size
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback for changed direct child nodes
|
||||
*/
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
virtual void colorFillChanged(const osg::Vec4& color);
|
||||
|
||||
void handleHit(float x, float y);
|
||||
|
||||
void setupDefaultDimensions();
|
||||
Rect<int> getTextureDimensions() const;
|
||||
|
@ -79,10 +69,10 @@ namespace canvas
|
|||
// TODO optionally forward events to canvas
|
||||
CanvasWeakPtr _canvas;
|
||||
|
||||
osg::Geometry *_geom;
|
||||
osg::Vec3Array *_vertices;
|
||||
osg::Vec2Array *_texCoords;
|
||||
osg::Vec4Array* _colors;
|
||||
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,
|
||||
|
|
|
@ -23,19 +23,30 @@
|
|||
#include <osg/Drawable>
|
||||
#include <osg/Geode>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
namespace canvas
|
||||
{
|
||||
const std::string NAME_TRANSFORM = "tf";
|
||||
const std::string NAME_COLOR = "color";
|
||||
const std::string NAME_COLOR_FILL = "color-fill";
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::removeListener()
|
||||
{
|
||||
_node->removeChangeListener(this);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Element::~Element()
|
||||
{
|
||||
removeListener();
|
||||
|
||||
BOOST_FOREACH(osg::Group* parent, _transform->getParents())
|
||||
{
|
||||
parent->removeChild(_transform);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -95,24 +106,6 @@ namespace canvas
|
|||
_transform_dirty = false;
|
||||
}
|
||||
|
||||
if( _attributes_dirty & COLOR )
|
||||
{
|
||||
colorChanged( osg::Vec4( _color[0]->getFloatValue(),
|
||||
_color[1]->getFloatValue(),
|
||||
_color[2]->getFloatValue(),
|
||||
_color[3]->getFloatValue() ) );
|
||||
_attributes_dirty &= ~COLOR;
|
||||
}
|
||||
|
||||
if( _attributes_dirty & COLOR_FILL )
|
||||
{
|
||||
colorFillChanged( osg::Vec4( _color_fill[0]->getFloatValue(),
|
||||
_color_fill[1]->getFloatValue(),
|
||||
_color_fill[2]->getFloatValue(),
|
||||
_color_fill[3]->getFloatValue() ) );
|
||||
_attributes_dirty &= ~COLOR_FILL;
|
||||
}
|
||||
|
||||
if( !_bounding_box.empty() )
|
||||
{
|
||||
assert( _drawable );
|
||||
|
@ -159,18 +152,15 @@ namespace canvas
|
|||
//----------------------------------------------------------------------------
|
||||
void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
|
||||
{
|
||||
if( parent == _node )
|
||||
{
|
||||
if( child->getNameString() == NAME_TRANSFORM )
|
||||
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;
|
||||
}
|
||||
else
|
||||
childAdded(child);
|
||||
return;
|
||||
}
|
||||
else if( parent->getParent() == _node
|
||||
&& parent->getNameString() == NAME_TRANSFORM )
|
||||
|
@ -191,16 +181,16 @@ namespace canvas
|
|||
type = TT_SCALE;
|
||||
|
||||
_transform_dirty = true;
|
||||
return;
|
||||
}
|
||||
|
||||
childAdded(child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Element::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
|
||||
{
|
||||
if( parent != _node )
|
||||
return;
|
||||
|
||||
if( child->getNameString() == NAME_TRANSFORM )
|
||||
if( parent == _node && child->getNameString() == NAME_TRANSFORM )
|
||||
{
|
||||
assert(child->getIndex() < static_cast<int>(_transform_types.size()));
|
||||
_transform_types[ child->getIndex() ] = TT_NONE;
|
||||
|
@ -209,8 +199,9 @@ namespace canvas
|
|||
_transform_types.pop_back();
|
||||
|
||||
_transform_dirty = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
childRemoved(child);
|
||||
}
|
||||
|
||||
|
@ -218,45 +209,41 @@ namespace canvas
|
|||
void Element::valueChanged(SGPropertyNode* child)
|
||||
{
|
||||
SGPropertyNode *parent = child->getParent();
|
||||
if( parent->getParent() == _node )
|
||||
if( parent == _node )
|
||||
{
|
||||
if( parent->getNameString() == NAME_TRANSFORM )
|
||||
_transform_dirty = true;
|
||||
else if( !_color.empty() && _color[0]->getParent() == parent )
|
||||
_attributes_dirty |= COLOR;
|
||||
else if( !_color_fill.empty() && _color_fill[0]->getParent() == parent )
|
||||
_attributes_dirty |= COLOR_FILL;
|
||||
}
|
||||
else if( parent == _node )
|
||||
{
|
||||
if( child->getNameString() == "update" )
|
||||
update(0);
|
||||
if( setStyle(child) )
|
||||
return;
|
||||
else if( child->getNameString() == "update" )
|
||||
return update(0);
|
||||
else if( child->getNameString() == "visible" )
|
||||
// TODO check if we need another nodemask
|
||||
_transform->setNodeMask( child->getBoolValue() ? 0xffffffff : 0 );
|
||||
else
|
||||
childChanged(child);
|
||||
return _transform->setNodeMask( child->getBoolValue() ? 0xffffffff : 0 );
|
||||
}
|
||||
else if( parent->getParent() == _node
|
||||
&& parent->getNameString() == NAME_TRANSFORM )
|
||||
{
|
||||
_transform_dirty = true;
|
||||
return;
|
||||
}
|
||||
|
||||
childChanged(child);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Element::Element(SGPropertyNode_ptr node, uint32_t attributes_used):
|
||||
Element::Element( SGPropertyNode_ptr node,
|
||||
const Style& parent_style,
|
||||
uint32_t attributes_used ):
|
||||
_attributes_used( attributes_used ),
|
||||
_attributes_dirty( attributes_used ),
|
||||
_transform_dirty( false ),
|
||||
_transform( new osg::MatrixTransform ),
|
||||
_node( node ),
|
||||
_style( parent_style ),
|
||||
_drawable( 0 )
|
||||
{
|
||||
assert( _node );
|
||||
_node->addChangeListener(this);
|
||||
|
||||
if( _attributes_used & COLOR )
|
||||
linkColorNodes("color", _node, _color, osg::Vec4f(0,0,0,1));
|
||||
|
||||
if( _attributes_used & COLOR_FILL )
|
||||
linkColorNodes("color-fill", _node, _color_fill, osg::Vec4f(1,1,1,1));
|
||||
|
||||
SG_LOG
|
||||
(
|
||||
SG_GL,
|
||||
|
@ -296,4 +283,23 @@ namespace canvas
|
|||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
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
|
||||
|
|
|
@ -20,8 +20,11 @@
|
|||
#define CANVAS_ELEMENT_HXX_
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <osg/MatrixTransform>
|
||||
#include <simgear/misc/stdint.hxx> // for uint32_t
|
||||
#include <osg/MatrixTransform>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
namespace osg
|
||||
{
|
||||
|
@ -36,6 +39,11 @@ namespace canvas
|
|||
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;
|
||||
|
||||
void removeListener();
|
||||
virtual ~Element() = 0;
|
||||
|
||||
/**
|
||||
|
@ -59,9 +67,7 @@ namespace canvas
|
|||
|
||||
enum Attributes
|
||||
{
|
||||
COLOR = 0x0001,
|
||||
COLOR_FILL = 0x0002,
|
||||
BOUNDING_BOX = 0x0004,
|
||||
BOUNDING_BOX = 0x0001,
|
||||
LAST_ATTRIBUTE = BOUNDING_BOX
|
||||
};
|
||||
|
||||
|
@ -82,11 +88,48 @@ namespace canvas
|
|||
std::vector<TransformType> _transform_types;
|
||||
|
||||
SGPropertyNode_ptr _node;
|
||||
std::vector<SGPropertyNode_ptr> _color,
|
||||
_color_fill;
|
||||
Style _style;
|
||||
StyleSetters _style_setters;
|
||||
std::vector<SGPropertyNode_ptr> _bounding_box;
|
||||
|
||||
Element(SGPropertyNode_ptr node, uint32_t attributes_used = 0);
|
||||
Element( SGPropertyNode_ptr node,
|
||||
const Style& parent_style,
|
||||
uint32_t attributes_used = 0 );
|
||||
|
||||
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);
|
||||
|
||||
|
@ -94,14 +137,14 @@ namespace canvas
|
|||
virtual void childRemoved(SGPropertyNode * child){}
|
||||
virtual void childChanged(SGPropertyNode * child){}
|
||||
|
||||
virtual void colorChanged(const osg::Vec4& color) {}
|
||||
virtual void colorFillChanged(const osg::Vec4& color){}
|
||||
|
||||
void setDrawable( osg::Drawable* drawable );
|
||||
void setupStyle();
|
||||
|
||||
bool setStyle(const SGPropertyNode* child);
|
||||
|
||||
private:
|
||||
|
||||
osg::Drawable *_drawable;
|
||||
osg::ref_ptr<osg::Drawable> _drawable;
|
||||
|
||||
Element(const Element&);// = delete
|
||||
};
|
||||
|
|
|
@ -28,8 +28,8 @@ namespace canvas
|
|||
{
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Group::Group(SGPropertyNode_ptr node):
|
||||
Element(node)
|
||||
Group::Group(SGPropertyNode_ptr node, const Style& parent_style):
|
||||
Element(node, parent_style)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -64,27 +64,33 @@ namespace canvas
|
|||
//----------------------------------------------------------------------------
|
||||
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(child) );
|
||||
element.reset( new Text(child, _style) );
|
||||
else if( child->getNameString() == "group" )
|
||||
element.reset( new Group(child) );
|
||||
element.reset( new Group(child, _style) );
|
||||
else if( child->getNameString() == "map" )
|
||||
element.reset( new Map(child) );
|
||||
element.reset( new Map(child, _style) );
|
||||
else if( child->getNameString() == "path" )
|
||||
element.reset( new Path(child) );
|
||||
element.reset( new Path(child, _style) );
|
||||
else if( child->getNameString() == "image" )
|
||||
element.reset( new Image(child) );
|
||||
|
||||
if( !element )
|
||||
return;
|
||||
element.reset( new Image(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;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -107,6 +113,9 @@ namespace canvas
|
|||
//----------------------------------------------------------------------------
|
||||
void Group::childRemoved(SGPropertyNode* node)
|
||||
{
|
||||
if( node->getParent() != _node )
|
||||
return;
|
||||
|
||||
if( node->getNameString() == "text"
|
||||
|| node->getNameString() == "group"
|
||||
|| node->getNameString() == "map"
|
||||
|
@ -130,6 +139,12 @@ namespace canvas
|
|||
_children.erase(child);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Style::iterator style = _style.find(node->getNameString());
|
||||
if( style != _style.end() )
|
||||
_style.erase(style);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "element.hxx"
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
namespace canvas
|
||||
{
|
||||
|
@ -37,7 +38,7 @@ namespace canvas
|
|||
>
|
||||
> ChildList;
|
||||
|
||||
Group(SGPropertyNode_ptr node);
|
||||
Group(SGPropertyNode_ptr node, const Style& parent_style = Style());
|
||||
virtual ~Group();
|
||||
|
||||
virtual void update(double dt);
|
||||
|
|
|
@ -46,8 +46,8 @@ namespace canvas
|
|||
const std::string GEO = "-geo";
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Map::Map(SGPropertyNode_ptr node):
|
||||
Group(node),
|
||||
Map::Map(SGPropertyNode_ptr node, const Style& parent_style):
|
||||
Group(node, parent_style),
|
||||
_projection_dirty(true)
|
||||
{
|
||||
|
||||
|
@ -177,6 +177,9 @@ namespace canvas
|
|||
//----------------------------------------------------------------------------
|
||||
void Map::childChanged(SGPropertyNode * child)
|
||||
{
|
||||
if( child->getParent() != _node )
|
||||
return;
|
||||
|
||||
if( child->getNameString() == "ref-lat"
|
||||
|| child->getNameString() == "ref-lon" )
|
||||
projection.setWorldPosition( _node->getDoubleValue("ref-lat"),
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace canvas
|
|||
public Group
|
||||
{
|
||||
public:
|
||||
Map(SGPropertyNode_ptr node);
|
||||
Map(SGPropertyNode_ptr node, const Style& parent_style);
|
||||
virtual ~Map();
|
||||
|
||||
virtual void update(double dt);
|
||||
|
|
|
@ -40,9 +40,9 @@ namespace canvas
|
|||
_paint(VG_INVALID_HANDLE),
|
||||
_paint_fill(VG_INVALID_HANDLE),
|
||||
_attributes_dirty(~0),
|
||||
_mode(0),
|
||||
_stroke_width(1),
|
||||
_stroke_linecap(VG_CAP_BUTT),
|
||||
_fill(false)
|
||||
_stroke_linecap(VG_CAP_BUTT)
|
||||
{
|
||||
setSupportsDisplayList(false);
|
||||
setDataVariance(Object::DYNAMIC);
|
||||
|
@ -76,43 +76,54 @@ namespace canvas
|
|||
}
|
||||
|
||||
/**
|
||||
* Set stroke width and dash (line stipple)
|
||||
* Set path fill paint ("none" if not filled)
|
||||
*/
|
||||
void setStroke( float width,
|
||||
const std::vector<float> dash = std::vector<float>() )
|
||||
void setFill(const std::string& fill)
|
||||
{
|
||||
if( fill == "none" )
|
||||
{
|
||||
_mode &= ~VG_FILL_PATH;
|
||||
}
|
||||
else
|
||||
{
|
||||
_fill_color = parseColor(fill);
|
||||
_mode |= VG_FILL_PATH;
|
||||
_attributes_dirty |= FILL_COLOR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set path stroke paint ("none" if no stroke)
|
||||
*/
|
||||
void setStroke(const std::string& stroke)
|
||||
{
|
||||
if( stroke == "none" )
|
||||
{
|
||||
_mode &= ~VG_STROKE_PATH;
|
||||
}
|
||||
else
|
||||
{
|
||||
_stroke_color = parseColor(stroke);
|
||||
_mode |= VG_STROKE_PATH;
|
||||
_attributes_dirty |= STROKE_COLOR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set stroke width
|
||||
*/
|
||||
void setStrokeWidth(float width)
|
||||
{
|
||||
_stroke_width = width;
|
||||
_stroke_dash = dash;
|
||||
|
||||
_attributes_dirty |= BOUNDING_BOX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the line color
|
||||
* Set stroke dash (line stipple)
|
||||
*/
|
||||
void setColor(const osg::Vec4& color)
|
||||
void setStrokeDashArray(const std::string& dash)
|
||||
{
|
||||
for( size_t i = 0; i < 4; ++i )
|
||||
_stroke_color[i] = color[i];
|
||||
_attributes_dirty |= STROKE_COLOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable filling of the path
|
||||
*/
|
||||
void enableFill(bool enable)
|
||||
{
|
||||
_fill = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the line color
|
||||
*/
|
||||
void setColorFill(const osg::Vec4& color)
|
||||
{
|
||||
for( size_t i = 0; i < 4; ++i )
|
||||
_fill_color[i] = color[i];
|
||||
_attributes_dirty |= FILL_COLOR;
|
||||
_stroke_dash = splitAndConvert(",\t\n ", dash);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -166,28 +177,25 @@ namespace canvas
|
|||
if( _paint == VG_INVALID_HANDLE )
|
||||
_paint = vgCreatePaint();
|
||||
|
||||
vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, _stroke_color);
|
||||
vgSetParameterfv(_paint, VG_PAINT_COLOR, 4, _stroke_color._v);
|
||||
|
||||
_attributes_dirty &= ~STROKE_COLOR;
|
||||
}
|
||||
|
||||
// Initialize/update fill paint
|
||||
if( _attributes_dirty & (FILL_COLOR | FILL) )
|
||||
if( _attributes_dirty & FILL_COLOR )
|
||||
{
|
||||
if( _paint_fill == VG_INVALID_HANDLE )
|
||||
_paint_fill = vgCreatePaint();
|
||||
|
||||
if( _attributes_dirty & FILL_COLOR )
|
||||
vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, _fill_color);
|
||||
vgSetParameterfv(_paint_fill, VG_PAINT_COLOR, 4, _fill_color._v);
|
||||
|
||||
_attributes_dirty &= ~(FILL_COLOR | FILL);
|
||||
_attributes_dirty &= ~FILL_COLOR;
|
||||
}
|
||||
|
||||
// Detect draw mode
|
||||
VGbitfield mode = 0;
|
||||
if( _stroke_width > 0 )
|
||||
// Setup paint
|
||||
if( _mode & VG_STROKE_PATH )
|
||||
{
|
||||
mode |= VG_STROKE_PATH;
|
||||
vgSetPaint(_paint, VG_STROKE_PATH);
|
||||
|
||||
vgSetf(VG_STROKE_LINE_WIDTH, _stroke_width);
|
||||
|
@ -196,15 +204,14 @@ namespace canvas
|
|||
_stroke_dash.size(),
|
||||
_stroke_dash.empty() ? 0 : &_stroke_dash[0] );
|
||||
}
|
||||
if( _fill )
|
||||
if( _mode & VG_FILL_PATH )
|
||||
{
|
||||
mode |= VG_FILL_PATH;
|
||||
vgSetPaint(_paint_fill, VG_FILL_PATH);
|
||||
}
|
||||
|
||||
// And finally draw the path
|
||||
if( mode )
|
||||
vgDrawPath(_path, mode);
|
||||
if( _mode )
|
||||
vgDrawPath(_path, _mode);
|
||||
|
||||
VGErrorCode err = vgGetError();
|
||||
if( err != VG_NO_ERROR )
|
||||
|
@ -246,8 +253,7 @@ namespace canvas
|
|||
PATH = 0x0001,
|
||||
STROKE_COLOR = PATH << 1,
|
||||
FILL_COLOR = STROKE_COLOR << 1,
|
||||
FILL = FILL_COLOR << 1,
|
||||
BOUNDING_BOX = FILL << 1
|
||||
BOUNDING_BOX = FILL_COLOR << 1
|
||||
};
|
||||
|
||||
mutable VGPath _path;
|
||||
|
@ -258,14 +264,13 @@ namespace canvas
|
|||
CmdList _cmds;
|
||||
CoordList _coords;
|
||||
|
||||
VGfloat _stroke_color[4];
|
||||
VGbitfield _mode;
|
||||
osg::Vec4f _fill_color;
|
||||
osg::Vec4f _stroke_color;
|
||||
VGfloat _stroke_width;
|
||||
std::vector<VGfloat> _stroke_dash;
|
||||
VGCapStyle _stroke_linecap;
|
||||
|
||||
bool _fill;
|
||||
VGfloat _fill_color[4];
|
||||
|
||||
/**
|
||||
* Initialize/Update the OpenVG path
|
||||
*/
|
||||
|
@ -339,11 +344,20 @@ namespace canvas
|
|||
bool PathDrawable::_vg_initialized = false;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Path::Path(SGPropertyNode_ptr node):
|
||||
Element(node, COLOR | COLOR_FILL | BOUNDING_BOX),
|
||||
Path::Path(SGPropertyNode_ptr node, const Style& parent_style):
|
||||
Element(node, parent_style, BOUNDING_BOX),
|
||||
_path( new PathDrawable() )
|
||||
{
|
||||
setDrawable(_path);
|
||||
PathDrawable *path = _path.get();
|
||||
|
||||
addStyle("fill", &PathDrawable::setFill, 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();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -365,16 +379,6 @@ namespace canvas
|
|||
|
||||
_attributes_dirty &= ~(CMDS | COORDS);
|
||||
}
|
||||
if( _attributes_dirty & STROKE )
|
||||
{
|
||||
_path->setStroke
|
||||
(
|
||||
_node->getFloatValue("stroke-width", 1),
|
||||
getVectorFromChildren<VGfloat, float>(_node, "stroke-dasharray")
|
||||
);
|
||||
|
||||
_attributes_dirty &= ~STROKE;
|
||||
}
|
||||
|
||||
Element::update(dt);
|
||||
}
|
||||
|
@ -382,29 +386,13 @@ namespace canvas
|
|||
//----------------------------------------------------------------------------
|
||||
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;
|
||||
else if( child->getNameString() == "stroke-width"
|
||||
|| child->getNameString() == "stroke-dasharray" )
|
||||
_attributes_dirty |= STROKE;
|
||||
else if( child->getNameString() == "stroke-linecap" )
|
||||
_path->setStrokeLinecap( child->getStringValue() );
|
||||
else if( child->getNameString() == "fill" )
|
||||
_path->enableFill( child->getBoolValue() );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::colorChanged(const osg::Vec4& color)
|
||||
{
|
||||
_path->setColor(color);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Path::colorFillChanged(const osg::Vec4& color)
|
||||
{
|
||||
_path->setColorFill(color);
|
||||
}
|
||||
|
||||
} // namespace canvas
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace canvas
|
|||
public Element
|
||||
{
|
||||
public:
|
||||
Path(SGPropertyNode_ptr node);
|
||||
Path(SGPropertyNode_ptr node, const Style& parent_style);
|
||||
virtual ~Path();
|
||||
|
||||
virtual void update(double dt);
|
||||
|
@ -38,15 +38,12 @@ namespace canvas
|
|||
enum PathAttributes
|
||||
{
|
||||
CMDS = LAST_ATTRIBUTE << 1,
|
||||
COORDS = CMDS << 1,
|
||||
STROKE = COORDS << 1
|
||||
COORDS = CMDS << 1
|
||||
};
|
||||
|
||||
osg::ref_ptr<PathDrawable> _path;
|
||||
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
virtual void colorChanged(const osg::Vec4& color);
|
||||
virtual void colorFillChanged(const osg::Vec4& color);
|
||||
};
|
||||
|
||||
} // namespace canvas
|
||||
|
|
|
@ -30,11 +30,37 @@ namespace canvas
|
|||
public osgText::Text
|
||||
{
|
||||
public:
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Text::TextOSG::setCharacterAspect(float aspect)
|
||||
{
|
||||
setCharacterSize(getCharacterHeight(), aspect);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Text::TextOSG::setFill(const std::string& fill)
|
||||
{
|
||||
// if( fill == "none" )
|
||||
// TODO No text
|
||||
// else
|
||||
setColor( parseColor(fill) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Text::TextOSG::setBackgroundColor(const std::string& fill)
|
||||
{
|
||||
setBoundingBoxColor( parseColor(fill) );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Vec2 Text::TextOSG::handleHit(float x, float y)
|
||||
{
|
||||
|
@ -130,19 +156,33 @@ namespace canvas
|
|||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
Text::Text(SGPropertyNode_ptr node):
|
||||
Element(node, COLOR | COLOR_FILL | BOUNDING_BOX),
|
||||
_text( new Text::TextOSG() ),
|
||||
_font_size( 0 ),
|
||||
_font_aspect( 0 )
|
||||
Text::Text(SGPropertyNode_ptr node, const Style& parent_style):
|
||||
Element(node, parent_style, BOUNDING_BOX),
|
||||
_text( new Text::TextOSG() )
|
||||
{
|
||||
setDrawable(_text);
|
||||
_text->setCharacterSizeMode(osgText::Text::OBJECT_COORDS);
|
||||
_text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION);
|
||||
_text->setRotation(osg::Quat(osg::PI, osg::X_AXIS));
|
||||
|
||||
_font_size = getChildDefault<float>(_node, "character-size", 32);
|
||||
_font_aspect = getChildDefault<float>(_node, "character-aspect-ratio", 1);
|
||||
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();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -152,20 +192,9 @@ namespace canvas
|
|||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Text::update(double dt)
|
||||
void Text::setText(const char* text)
|
||||
{
|
||||
Element::update(dt);
|
||||
|
||||
if( _attributes_dirty & FONT_SIZE )
|
||||
{
|
||||
_text->setCharacterSize
|
||||
(
|
||||
_font_size->getFloatValue(),
|
||||
_font_aspect->getFloatValue()
|
||||
);
|
||||
|
||||
_attributes_dirty &= ~FONT_SIZE;
|
||||
}
|
||||
_text->setText(text, osgText::String::ENCODING_UTF8);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
@ -213,51 +242,20 @@ namespace canvas
|
|||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Text::childChanged(SGPropertyNode* child)
|
||||
{
|
||||
const std::string& name = child->getNameString();
|
||||
if( child->getParent() != _node )
|
||||
return;
|
||||
|
||||
const std::string& name = child->getNameString();
|
||||
if( name == "hit-y" )
|
||||
handleHit
|
||||
(
|
||||
_node->getFloatValue("hit-x"),
|
||||
_node->getFloatValue("hit-y")
|
||||
);
|
||||
else if( _font_size == child || _font_aspect == child )
|
||||
_attributes_dirty |= FONT_SIZE;
|
||||
else if( name == "text" )
|
||||
_text->setText
|
||||
(
|
||||
osgText::String( child->getStringValue(),
|
||||
osgText::String::ENCODING_UTF8 )
|
||||
);
|
||||
else if( name == "padding" )
|
||||
_text->setBoundingBoxMargin( child->getFloatValue() );
|
||||
else if( name == "draw-mode" )
|
||||
// TEXT = 1 default
|
||||
// BOUNDINGBOX = 2
|
||||
// FILLEDBOUNDINGBOX = 4
|
||||
// ALIGNMENT = 8
|
||||
_text->setDrawMode( child->getIntValue() );
|
||||
else if( name == "max-width" )
|
||||
_text->setMaximumWidth( child->getFloatValue() );
|
||||
else if( name == "font" )
|
||||
setFont( child->getStringValue() );
|
||||
else if( name == "alignment" )
|
||||
setAlignment( child->getStringValue() );
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Text::colorChanged(const osg::Vec4& color)
|
||||
{
|
||||
_text->setColor(color);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void Text::colorFillChanged(const osg::Vec4& color)
|
||||
{
|
||||
_text->setBoundingBoxColor(color);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
|
|
@ -32,30 +32,19 @@ namespace canvas
|
|||
public Element
|
||||
{
|
||||
public:
|
||||
Text(SGPropertyNode_ptr node);
|
||||
Text(SGPropertyNode_ptr node, const Style& parent_style);
|
||||
~Text();
|
||||
|
||||
virtual void update(double dt);
|
||||
|
||||
void setText(const char* text);
|
||||
void setFont(const char* name);
|
||||
void setAlignment(const char* align);
|
||||
|
||||
protected:
|
||||
|
||||
enum TextAttributes
|
||||
{
|
||||
FONT_SIZE = LAST_ATTRIBUTE << 1, // Font size and aspect ration
|
||||
};
|
||||
|
||||
class TextOSG;
|
||||
osg::ref_ptr<TextOSG> _text;
|
||||
|
||||
SGPropertyNode_ptr _font_size,
|
||||
_font_aspect;
|
||||
|
||||
virtual void childChanged(SGPropertyNode * child);
|
||||
virtual void colorChanged(const osg::Vec4& color);
|
||||
virtual void colorFillChanged(const osg::Vec4& color);
|
||||
|
||||
void handleHit(float x, float y);
|
||||
|
||||
|
|
|
@ -17,10 +17,98 @@
|
|||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "property_helper.hxx"
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/tokenizer.hpp>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace canvas
|
||||
{
|
||||
//----------------------------------------------------------------------------
|
||||
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;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
osg::Vec4 parseColor(std::string str)
|
||||
{
|
||||
boost::trim(str);
|
||||
osg::Vec4 color(0,0,0,1);
|
||||
|
||||
if( str.empty() )
|
||||
return color;
|
||||
|
||||
// #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 color;
|
||||
|
||||
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
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "Unknown color: " << str);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void linkColorNodes( const char* name,
|
||||
SGPropertyNode* parent,
|
||||
|
|
|
@ -60,12 +60,26 @@ namespace canvas
|
|||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split a string by a delimter and convert the values to float
|
||||
*
|
||||
* TODO do we need other types than float?
|
||||
*/
|
||||
std::vector<float> splitAndConvert(const char del[], const std::string& str);
|
||||
|
||||
/**
|
||||
* Parse a (CSS) color
|
||||
*/
|
||||
osg::Vec4 parseColor(std::string str);
|
||||
|
||||
/**
|
||||
* @param name Name of color node
|
||||
* @param parent Parent for color channel nodes
|
||||
* @param nodes Vector to push color nodes into
|
||||
* @param def Default color
|
||||
*
|
||||
* @deprecated Use only a single property instead and parse with #parseColor
|
||||
*
|
||||
* <name>
|
||||
* <red type="float">def[0] or existing value</red>
|
||||
* <green type="float">def[1] or existing value</green>
|
||||
|
|
|
@ -28,8 +28,10 @@ namespace canvas
|
|||
//----------------------------------------------------------------------------
|
||||
Window::Window(SGPropertyNode* node):
|
||||
PropertyBasedElement(node),
|
||||
_image(node)
|
||||
_image(node, Element::Style())
|
||||
{
|
||||
_image.removeListener();
|
||||
|
||||
// TODO probably better remove default position and size
|
||||
node->setFloatValue("x", 50);
|
||||
node->setFloatValue("y", 100);
|
||||
|
@ -44,10 +46,7 @@ namespace canvas
|
|||
//----------------------------------------------------------------------------
|
||||
Window::~Window()
|
||||
{
|
||||
BOOST_FOREACH(osg::Group* parent, getGroup()->getParents())
|
||||
{
|
||||
parent->removeChild(getGroup());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in a new issue