From 1f431323635de0dd86d60a0fc58efa9557983ef8 Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Sat, 20 Apr 2013 00:22:37 +0200 Subject: [PATCH] Canvas: rework style setter system. Style setters are now only setup once for each element type and not for each element instance as before. A static map holds the setters for each element type. Also an animation type is stored which will later allow to animate properties of Canvas elements without specifying and animation type. --- simgear/canvas/elements/CanvasElement.cxx | 18 +- simgear/canvas/elements/CanvasElement.hxx | 303 ++++++++++++++++++++-- simgear/canvas/elements/CanvasGroup.cxx | 18 +- simgear/canvas/elements/CanvasGroup.hxx | 8 +- simgear/canvas/elements/CanvasImage.cxx | 11 +- simgear/canvas/elements/CanvasPath.cxx | 20 +- simgear/canvas/elements/CanvasPath.hxx | 3 +- simgear/canvas/elements/CanvasText.cxx | 42 +-- 8 files changed, 357 insertions(+), 66 deletions(-) diff --git a/simgear/canvas/elements/CanvasElement.cxx b/simgear/canvas/elements/CanvasElement.cxx index 2abba3a4..0d3a0589 100644 --- a/simgear/canvas/elements/CanvasElement.cxx +++ b/simgear/canvas/elements/CanvasElement.cxx @@ -324,8 +324,14 @@ namespace canvas if( setter == _style_setters.end() ) return false; - setter->second(child); - return true; + const StyleSetter* style_setter = &setter->second.setter; + while( style_setter ) + { + if( style_setter->func(*this, child) ) + return true; + style_setter = style_setter->next; + } + return false; } //---------------------------------------------------------------------------- @@ -426,6 +432,9 @@ namespace canvas return transformed; } + //---------------------------------------------------------------------------- + Element::StyleSetters Element::_style_setters; + //---------------------------------------------------------------------------- Element::Element( const CanvasWeakPtr& canvas, const SGPropertyNode_ptr& node, @@ -447,7 +456,10 @@ namespace canvas "New canvas element " << node->getPath() ); - addStyle("clip", &Element::setClip, this); + if( !isInit() ) + { + addStyle("clip", "", &Element::setClip); + } } //---------------------------------------------------------------------------- diff --git a/simgear/canvas/elements/CanvasElement.hxx b/simgear/canvas/elements/CanvasElement.hxx index e6cd3012..31961825 100644 --- a/simgear/canvas/elements/CanvasElement.hxx +++ b/simgear/canvas/elements/CanvasElement.hxx @@ -45,8 +45,21 @@ namespace canvas public PropertyBasedElement { public: - typedef boost::function StyleSetter; - typedef std::map StyleSetters; + typedef boost::function + StyleSetterFunc; + typedef boost::function + StyleSetterFuncUnchecked; + struct StyleSetter: + public SGReferenced + { + StyleSetterFunc func; + SGSharedPtr next; + }; + struct StyleInfo + { + StyleSetter setter; ///!< Function(s) for setting this style + std::string type; ///!< Interpolation type + }; /** * Remove the property listener of the element. @@ -142,7 +155,6 @@ namespace canvas std::vector _transform_types; Style _style; - StyleSetters _style_setters; std::vector _bounding_box; typedef std::vector Listener; @@ -150,44 +162,254 @@ namespace canvas ListenerMap _listener; + typedef std::map StyleSetters; + static StyleSetters _style_setters; + Element( const CanvasWeakPtr& canvas, const SGPropertyNode_ptr& node, const Style& parent_style, Element* parent ); - template - Element::StyleSetter - addStyle(const std::string& name, void (C1::*setter)(T), C2 instance) + /** + * Returns false on first call and true on any successive call. Use to + * perform initialization tasks which are only required once per element + * type. + * + * @tparam Derived (Derived) class type + */ + template + bool isInit() const { - return _style_setters[ name ] = - bindStyleSetter(name, setter, instance); + static bool is_init = false; + if( is_init ) + return true; + + is_init = true; + return false; } - template - Element::StyleSetter - addStyle(const std::string& name, void (C1::*setter)(T2), C2 instance) + /** + * Register a function for setting a style specified by the given property + * + * @param name Property name + * @param type Interpolation type + * @param setter Setter function + * + * @tparam T1 Type of value used to retrieve value from property + * node + * @tparam T2 Type of value the setter function expects + * @tparam Derived Type of class the setter can be applied to + * + * @note T1 needs to be convertible to T2 + */ + template< + typename T1, + typename T2, + class Derived + > + StyleSetter + addStyle( const std::string& name, + const std::string& type, + const boost::function& setter ) + { + StyleInfo& style_info = _style_setters[ name ]; + if( !type.empty() ) + { + if( !style_info.type.empty() && type != style_info.type ) + SG_LOG + ( + SG_GENERAL, + SG_WARN, + "changing animation type for '" << name << "': " + << style_info.type << " -> " << type + ); + + style_info.type = type; + } + + StyleSetter* style = &style_info.setter; + while( style->next ) + style = style->next; + if( style->func ) + style = style->next = new StyleSetter; + + style->func = boost::bind + ( + &type_match::call, + _1, + _2, + bindStyleSetter(name, setter) + ); + return *style; + } + + template< + typename T, + class Derived + > + StyleSetter + addStyle( const std::string& name, + const std::string& type, + const boost::function& setter ) + { + return addStyle(name, type, setter); + } + + template< + typename T, + class Derived + > + StyleSetter + addStyle( const std::string& name, + const std::string& type, + void (Derived::*setter)(T) ) + { + return addStyle + ( + name, + type, + boost::function(setter) + ); + } + + template< + typename T1, + typename T2, + class Derived + > + StyleSetterFunc + addStyle( const std::string& name, + const std::string& type, + void (Derived::*setter)(T2) ) + { + return addStyle + ( + name, + type, + boost::function(setter) + ); + } + + template< + class Derived + > + StyleSetter + addStyle( const std::string& name, + const std::string& type, + void (Derived::*setter)(const std::string&) ) { - return _style_setters[ name ] = - bindStyleSetter(name, setter, instance); + return addStyle + ( + name, + type, + boost::function(setter) + ); } - template - Element::StyleSetter + template< + typename T, + class Derived, + class Other, + class OtherRef + > + StyleSetter addStyle( const std::string& name, - void (C1::*setter)(const std::string&), - C2 instance ) + const std::string& type, + void (Other::*setter)(T), + OtherRef Derived::*instance_ref ) { - return _style_setters[ name ] = - bindStyleSetter(name, setter, instance); + return addStyle(name, type, bindOther(setter, instance_ref)); } - template - Element::StyleSetter + template< + typename T1, + typename T2, + class Derived, + class Other, + class OtherRef + > + StyleSetter + addStyle( const std::string& name, + const std::string& type, + void (Other::*setter)(T2), + OtherRef Derived::*instance_ref ) + { + return addStyle(name, type, bindOther(setter, instance_ref)); + } + + template< + typename T1, + typename T2, + class Derived, + class Other, + class OtherRef + > + StyleSetter + addStyle( const std::string& name, + const std::string& type, + const boost::function& setter, + OtherRef Derived::*instance_ref ) + { + return addStyle(name, type, bindOther(setter, instance_ref)); + } + + template< + class Derived, + class Other, + class OtherRef + > + StyleSetter + addStyle( const std::string& name, + const std::string& type, + void (Other::*setter)(const std::string&), + OtherRef Derived::*instance_ref ) + { + return addStyle + ( + name, + type, + boost::function(setter), + instance_ref + ); + } + + template + boost::function + bindOther( void (Other::*setter)(T), OtherRef Derived::*instance_ref ) + { + return boost::bind(setter, boost::bind(instance_ref, _1), _2); + } + + template + boost::function + bindOther( const boost::function& setter, + OtherRef Derived::*instance_ref ) + { + return boost::bind + ( + setter, + boost::bind + ( + &reference_from_pointer, + boost::bind(instance_ref, _1) + ), + _2 + ); + } + + template + StyleSetterFuncUnchecked bindStyleSetter( const std::string& name, - void (C1::*setter)(T2), - C2 instance ) + const boost::function& setter ) { - return boost::bind(setter, instance, boost::bind(&getValue, _1)); + return boost::bind + ( + setter, + // We will only call setters with Derived instances, so we can safely + // cast here. + boost::bind(&derived_cast, _1), + boost::bind(&getValue, _2) + ); } virtual void childAdded(SGPropertyNode * child) {} @@ -208,6 +430,39 @@ namespace canvas osg::ref_ptr _drawable; Element(const Element&);// = delete + + template + static Derived& derived_cast(Element& el) + { + return static_cast(el); + } + + template + static T& reference_from_pointer(const SharedPtr& p) + { + return *get_pointer(p); + } + + /** + * Helper to call a function only of the element type can be converted to + * the required type. + * + * @return Whether the function has been called + */ + template + struct type_match + { + static bool call( Element& el, + const SGPropertyNode* prop, + const StyleSetterFuncUnchecked& func ) + { + Derived* d = dynamic_cast(&el); + if( !d ) + return false; + func(*d, prop); + return true; + } + }; }; } // namespace canvas diff --git a/simgear/canvas/elements/CanvasGroup.cxx b/simgear/canvas/elements/CanvasGroup.cxx index 8263b079..9bfb8c2f 100644 --- a/simgear/canvas/elements/CanvasGroup.cxx +++ b/simgear/canvas/elements/CanvasGroup.cxx @@ -46,6 +46,9 @@ namespace canvas return el; } + //---------------------------------------------------------------------------- + ElementFactories Group::_child_factories; + //---------------------------------------------------------------------------- Group::Group( const CanvasWeakPtr& canvas, const SGPropertyNode_ptr& node, @@ -53,11 +56,14 @@ namespace canvas Element* parent ): Element(canvas, node, parent_style, parent) { - _child_factories["group"] = &createElement; - _child_factories["image"] = &createElement; - _child_factories["map" ] = &createElement; - _child_factories["path" ] = &createElement; - _child_factories["text" ] = &createElement; + if( !isInit() ) + { + _child_factories["group"] = &createElement; + _child_factories["image"] = &createElement; + _child_factories["map" ] = &createElement; + _child_factories["path" ] = &createElement; + _child_factories["text" ] = &createElement; + } } //---------------------------------------------------------------------------- @@ -183,7 +189,7 @@ namespace canvas if( child->getParent() != _node ) return; - ChildFactories::iterator child_factory = + ElementFactories::iterator child_factory = _child_factories.find( child->getNameString() ); if( child_factory != _child_factories.end() ) { diff --git a/simgear/canvas/elements/CanvasGroup.hxx b/simgear/canvas/elements/CanvasGroup.hxx index 7eec7664..524b5dfb 100644 --- a/simgear/canvas/elements/CanvasGroup.hxx +++ b/simgear/canvas/elements/CanvasGroup.hxx @@ -28,6 +28,8 @@ namespace simgear namespace canvas { + typedef std::map ElementFactories; + class Group: public Element { @@ -64,10 +66,8 @@ namespace canvas protected: - typedef std::map ChildFactories; - - ChildFactories _child_factories; - ChildList _children; + static ElementFactories _child_factories; + ChildList _children; virtual void childAdded(SGPropertyNode * child); virtual void childRemoved(SGPropertyNode * child); diff --git a/simgear/canvas/elements/CanvasImage.cxx b/simgear/canvas/elements/CanvasImage.cxx index e9a683cb..7400833f 100644 --- a/simgear/canvas/elements/CanvasImage.cxx +++ b/simgear/canvas/elements/CanvasImage.cxx @@ -127,10 +127,13 @@ namespace canvas setDrawable(_geom); - addStyle("fill", &Image::setFill, this); - addStyle("slice", &Image::setSlice, this); - addStyle("slice-width", &Image::setSliceWidth, this); - addStyle("outset", &Image::setOutset, this); + if( !isInit() ) + { + addStyle("fill", "color", &Image::setFill); + addStyle("slice", "", &Image::setSlice); + addStyle("slice-width", "numeric", &Image::setSliceWidth); + addStyle("outset", "", &Image::setOutset); + } setFill("#ffffff"); // TODO how should we handle default values? diff --git a/simgear/canvas/elements/CanvasPath.cxx b/simgear/canvas/elements/CanvasPath.cxx index 9fde6782..045aabf5 100644 --- a/simgear/canvas/elements/CanvasPath.cxx +++ b/simgear/canvas/elements/CanvasPath.cxx @@ -476,14 +476,18 @@ namespace canvas _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); + + if( !isInit() ) + { + PathDrawableRef Path::*path = &Path::_path; + + addStyle("fill", "color", &PathDrawable::setFill, path); + addStyle("fill-rule", "", &PathDrawable::setFillRule, path); + addStyle("stroke", "color", &PathDrawable::setStroke, path); + addStyle("stroke-width", "numeric", &PathDrawable::setStrokeWidth, path); + addStyle("stroke-dasharray", "", &PathDrawable::setStrokeDashArray, path); + addStyle("stroke-linecap", "", &PathDrawable::setStrokeLinecap, path); + } setupStyle(); } diff --git a/simgear/canvas/elements/CanvasPath.hxx b/simgear/canvas/elements/CanvasPath.hxx index 2cde2dc9..706f8cd7 100644 --- a/simgear/canvas/elements/CanvasPath.hxx +++ b/simgear/canvas/elements/CanvasPath.hxx @@ -48,7 +48,8 @@ namespace canvas }; class PathDrawable; - osg::ref_ptr _path; + typedef osg::ref_ptr PathDrawableRef; + PathDrawableRef _path; virtual void childRemoved(SGPropertyNode * child); virtual void childChanged(SGPropertyNode * child); diff --git a/simgear/canvas/elements/CanvasText.cxx b/simgear/canvas/elements/CanvasText.cxx index 8c4028bf..a00cc1fa 100644 --- a/simgear/canvas/elements/CanvasText.cxx +++ b/simgear/canvas/elements/CanvasText.cxx @@ -254,22 +254,32 @@ namespace canvas _text->setAxisAlignment(osgText::Text::USER_DEFINED_ROTATION); _text->setRotation(osg::Quat(osg::PI, osg::X_AXIS)); - addStyle("fill", &TextOSG::setFill, _text); - addStyle("background", &TextOSG::setBackgroundColor, _text); - addStyle("character-size", - static_cast(&TextOSG::setCharacterSize), - _text); - addStyle("character-aspect-ratio", &TextOSG::setCharacterAspect, _text); - addStyle("padding", &TextOSG::setBoundingBoxMargin, _text); - // TEXT = 1 default - // BOUNDINGBOX = 2 - // FILLEDBOUNDINGBOX = 4 - // ALIGNMENT = 8 - addStyle("draw-mode", &TextOSG::setDrawMode, _text); - addStyle("max-width", &TextOSG::setMaximumWidth, _text); - addStyle("font", &Text::setFont, this); - addStyle("alignment", &Text::setAlignment, this); - addStyle("text", &Text::setText, this); + if( !isInit() ) + { + osg::ref_ptr Text::*text = &Text::_text; + + addStyle("fill", "color", &TextOSG::setFill, text); + addStyle("background", "color", &TextOSG::setBackgroundColor, text); + addStyle("character-size", + "numeric", + static_cast< + void (TextOSG::*)(float) + > (&TextOSG::setCharacterSize), + text); + addStyle("character-aspect-ratio", + "numeric", + &TextOSG::setCharacterAspect, text); + addStyle("padding", "numeric", &TextOSG::setBoundingBoxMargin, text); + // TEXT = 1 default + // BOUNDINGBOX = 2 + // FILLEDBOUNDINGBOX = 4 + // ALIGNMENT = 8 + addStyle("draw-mode", "", &TextOSG::setDrawMode, text); + addStyle("max-width", "numeric", &TextOSG::setMaximumWidth, text); + addStyle("font", "", &Text::setFont); + addStyle("alignment", "", &Text::setAlignment); + addStyle("text", "", &Text::setText); + } setupStyle(); } -- 2.39.5