From 544784ca8520d91f3383eee45b4450c6873bba72 Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Fri, 10 Aug 2012 13:11:06 +0200 Subject: [PATCH] Canvas: Forward mouse events to elements. - Use bounding box intersections to determine which element is hit. - Transform mouse coordinates to local coordinates. - Try to get osgText bounding box correct (or at least better) --- src/Canvas/MouseEvent.hxx | 12 +++++--- src/Canvas/canvas.cxx | 3 +- src/Canvas/elements/element.cxx | 36 ++++++++++++++++++++++++ src/Canvas/elements/element.hxx | 5 ++++ src/Canvas/elements/group.cxx | 50 +++++++++++++++++++++++++++------ src/Canvas/elements/group.hxx | 13 +++++++-- src/Canvas/elements/text.cxx | 17 +++++++++++ 7 files changed, 120 insertions(+), 16 deletions(-) diff --git a/src/Canvas/MouseEvent.hxx b/src/Canvas/MouseEvent.hxx index 0bd5d44a0..c9b8c4d1c 100644 --- a/src/Canvas/MouseEvent.hxx +++ b/src/Canvas/MouseEvent.hxx @@ -36,13 +36,17 @@ namespace canvas dx(0), dy(0), button(-1), state(-1), - mod(-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; - int x, y, - dx, dy, - button, //handleMouseEvent(event); } //------------------------------------------------------------------------------ diff --git a/src/Canvas/elements/element.cxx b/src/Canvas/elements/element.cxx index 01fbf1a5d..040670a43 100644 --- a/src/Canvas/elements/element.cxx +++ b/src/Canvas/elements/element.cxx @@ -17,6 +17,7 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "element.hxx" +#include #include #include @@ -124,6 +125,31 @@ namespace canvas } } + //---------------------------------------------------------------------------- + bool Element::handleMouseEvent(const canvas::MouseEvent& event) + { + // Transform event to local coordinates + const osg::Matrixd& m = _transform->getInverseMatrix(); + canvas::MouseEvent local_event = event; + local_event.x = m(0, 0) * event.x + m(1, 0) * event.y + m(3, 0); + local_event.y = m(0, 1) * event.x + m(1, 1) * event.y + m(3, 1); + + // Drawables have a bounding box... + if( _drawable ) + { + if( !_drawable->getBound().contains(local_event.getPos3()) ) + return false; + } + // ... for other elements, i.e. groups only a bounding sphere is available + else if( !_transform->getBound().contains(local_event.getPos3()) ) + return false; + + local_event.dx = m(0, 0) * event.dx + m(1, 0) * event.dy; + local_event.dy = m(0, 1) * event.dx + m(1, 1) * event.dy; + + return handleLocalMouseEvent(local_event); + } + //---------------------------------------------------------------------------- osg::ref_ptr Element::getMatrixTransform() { @@ -239,6 +265,16 @@ namespace canvas ); } + //---------------------------------------------------------------------------- + bool Element::handleLocalMouseEvent(const canvas::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 ) { diff --git a/src/Canvas/elements/element.hxx b/src/Canvas/elements/element.hxx index 642060434..782b5d1fb 100644 --- a/src/Canvas/elements/element.hxx +++ b/src/Canvas/elements/element.hxx @@ -31,6 +31,7 @@ namespace osg namespace canvas { + class MouseEvent; class Element: public SGPropertyChangeListener { @@ -44,6 +45,8 @@ namespace canvas */ virtual void update(double dt); + virtual bool handleMouseEvent(const canvas::MouseEvent& event); + osg::ref_ptr getMatrixTransform(); virtual void childAdded( SGPropertyNode * parent, @@ -85,6 +88,8 @@ namespace canvas Element(SGPropertyNode_ptr node, uint32_t attributes_used = 0); + virtual bool handleLocalMouseEvent(const canvas::MouseEvent& event); + virtual void childAdded(SGPropertyNode * child) {} virtual void childRemoved(SGPropertyNode * child){} virtual void childChanged(SGPropertyNode * child){} diff --git a/src/Canvas/elements/group.cxx b/src/Canvas/elements/group.cxx index 473ecc2f5..eef81587c 100644 --- a/src/Canvas/elements/group.cxx +++ b/src/Canvas/elements/group.cxx @@ -22,6 +22,8 @@ #include "text.hxx" #include "CanvasImage.hxx" +#include + namespace canvas { @@ -41,19 +43,31 @@ namespace canvas //---------------------------------------------------------------------------- void Group::update(double dt) { - for( ChildMap::iterator child = _children.begin(); - child != _children.end(); - ++child ) - child->second->update(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) { boost::shared_ptr element; + // TODO create map of child factories and use also to check for element + // on deletion in ::childRemoved if( child->getNameString() == "text" ) element.reset( new Text(child) ); else if( child->getNameString() == "group" ) @@ -64,24 +78,44 @@ namespace canvas element.reset( new Path(child) ); else if( child->getNameString() == "image" ) element.reset( new Image(child) ); - + if( !element ) return; // Add to osg scene graph... _transform->addChild( element->getMatrixTransform() ); - _children[ child ] = element; + _children.push_back( ChildList::value_type(child, element) ); } + //---------------------------------------------------------------------------- + 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->getNameString() == "text" || node->getNameString() == "group" || node->getNameString() == "map" - || node->getNameString() == "path" ) + || node->getNameString() == "path" + || node->getNameString() == "image" ) { - ChildMap::iterator child = _children.find(node); + ChildFinder pred(node); + ChildList::iterator child = + std::find_if(_children.begin(), _children.end(), pred); if( child == _children.end() ) SG_LOG diff --git a/src/Canvas/elements/group.hxx b/src/Canvas/elements/group.hxx index 1c07847f9..af64f70dc 100644 --- a/src/Canvas/elements/group.hxx +++ b/src/Canvas/elements/group.hxx @@ -21,7 +21,7 @@ #include "element.hxx" #include -#include +#include namespace canvas { @@ -32,14 +32,21 @@ namespace canvas public Element { public: + typedef std::list< std::pair< const SGPropertyNode*, + ElementPtr + > + > ChildList; + Group(SGPropertyNode_ptr node); virtual ~Group(); virtual void update(double dt); protected: - typedef std::map ChildMap; - ChildMap _children; + + ChildList _children; + + virtual bool handleLocalMouseEvent(const canvas::MouseEvent& event); virtual void childAdded(SGPropertyNode * child); virtual void childRemoved(SGPropertyNode * child); diff --git a/src/Canvas/elements/text.cxx b/src/Canvas/elements/text.cxx index 518e78378..3171acc7f 100644 --- a/src/Canvas/elements/text.cxx +++ b/src/Canvas/elements/text.cxx @@ -31,6 +31,8 @@ namespace canvas { public: osg::Vec2 handleHit(float x, float y); + + virtual osg::BoundingBox computeBound() const; }; //---------------------------------------------------------------------------- @@ -112,6 +114,21 @@ namespace canvas ); } + //---------------------------------------------------------------------------- + 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(); + + return bb; + } + //---------------------------------------------------------------------------- Text::Text(SGPropertyNode_ptr node): Element(node, COLOR | COLOR_FILL | BOUNDING_BOX),