先来看看FeatureEditor的用法:

    const osgEarth::SpatialReference* mapSRS = mapNode->getMapSRS();

    osgEarth::Symbology::Style geomStyle;
geomStyle.getOrCreate<osgEarth::LineSymbol>()->stroke()->color() = osgEarth::Symbology::Color::Cyan;
geomStyle.getOrCreate<osgEarth::LineSymbol>()->stroke()->width() = 5.0f;
geomStyle.getOrCreate<osgEarth::LineSymbol>()->tessellationSize() = 75000;
geomStyle.getOrCreate<osgEarth::AltitudeSymbol>()->clamping() = osgEarth::AltitudeSymbol::CLAMP_TO_TERRAIN;
geomStyle.getOrCreate<osgEarth::AltitudeSymbol>()->technique() = osgEarth::AltitudeSymbol::TECHNIQUE_DRAPE; osg::ref_ptr<osgEarth::Symbology::Polygon> polygon = new osgEarth::Symbology::Polygon();
polygon->push_back(osg::Vec3d(0, 40, 0));
polygon->push_back(osg::Vec3d(-60, 40, 0));
polygon->push_back(osg::Vec3d(-60, 60, 0));
polygon->push_back(osg::Vec3d(0, 60, 0)); osg::ref_ptr<osgEarth::Features::Feature> feature = new osgEarth::Features::Feature(polygon, mapSRS);
osg::ref_ptr<osgEarth::Annotation::FeatureNode> featureNode = new osgEarth::Annotation::FeatureNode(feature, geomStyle);
geometryGroup->addChild(featureNode);
osg::ref_ptr<osgEarth::Annotation::FeatureEditor> editor = new osgEarth::Annotation::FeatureEditor(featureNode);
mapNode->addChild(editor);

FeatureNode作为参数来构造出一个FeatureEditor,然后将该FeatureEditor添加到MapNode中,即可实现通过鼠标拖动对FeatureNode的编辑功能。那么FeatureEditor是如何做到的呢?

先看看FeatureEditor的继承图

FeatureEditor继承自AnnotationEditorAnnotationEditor继承自Group,看看AnnotationEditor的代码

class OSGEARTHANNO_EXPORT AnnotationEditor : public osg::Group
{
protected:
AnnotationEditor(); virtual ~AnnotationEditor() { }
};
AnnotationEditor::AnnotationEditor() :
osg::Group()
{
// editor geometry should always be visible.
osg::StateSet* stateSet = this->getOrCreateStateSet();
stateSet->setMode(GL_DEPTH_TEST, 0);
stateSet->setRenderBinDetails(99, "RenderBin");
}

AnnotationEditor这个类并没有做太多的事情,仅仅是在构造函数中设置了一下渲染状态而已。

接着看看FeatureEditor这个类

class OSGEARTHANNO_EXPORT FeatureEditor : public AnnotationEditor
{
public:
/**
* Constructs a new FeatureEditor
* @param featureNode
* The FeatureNode to edit
*/
FeatureEditor( FeatureNode* featureNode ); /**
*Gets the color of the draggers when they are selected
*/
const osg::Vec4f& getPickColor() const; /**
*Sets the color of the draggers when they are selected
*/
void setPickColor( const osg::Vec4f& pickColor ); /**
*Gets the color of the draggers
*/
const osg::Vec4f& getColor() const; /**
*Sets the color of the draggers
*/
void setColor( const osg::Vec4f& color ); /**
*Gets the dragger size
*/
float getSize() const; /**
*Sets the dragger size
*/
void setSize( float size ); protected:
void init(); osg::Vec4f _pickColor;
osg::Vec4f _color;
float _size; osg::ref_ptr< FeatureNode > _featureNode;
};

这个类的代码也比较简单,只包含四个成员变量,两个color,一个size,以及该Editor所要编辑的FeatureNode

FeatureEditor::FeatureEditor( FeatureNode* featureNode):
_featureNode( featureNode ),
_color(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f)),
_pickColor(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f)),
_size( 5.0f )
{
init();
}

构造函数初始化了四个成员变量,然后调用init()函数。

void
FeatureEditor::init()
{
removeChildren( 0, getNumChildren() ); Feature* feature = _featureNode->getFeatures().front().get();
//Create a dragger for each point
for (unsigned int i = 0; i < feature->getGeometry()->size(); i++)
{
SphereDragger* dragger = new SphereDragger( _featureNode->getMapNode() );
dragger->setColor( _color );
dragger->setPickColor( _pickColor );
dragger->setSize( _size );
dragger->setPosition(GeoPoint(feature->getSRS(), (*feature->getGeometry())[i].x(), (*feature->getGeometry())[i].y()));
dragger->addPositionChangedCallback(new MoveFeatureDraggerCallback( _featureNode.get(), i) ); addChild(dragger);
}
}

正如官方所给的注释:这个函数为geometry中的每个point都创建了一个draggerdragger又是个什么东东?这个我们现在还不知道,接下来我们的内容就是研究dragger,在此处,我们主要需要注意这四句代码,此时不懂不要紧,看完后面,就会串起来了。

void
FeatureEditor::init()
{
for (unsigned int i = 0; i < feature->getGeometry()->size(); i++)
{
//构造函数
SphereDragger* dragger = new SphereDragger( _featureNode->getMapNode() );
//根据point的位置来设置dragger的位置
dragger->setPosition(GeoPoint(feature->getSRS(), (*feature->getGeometry())[i].x(), (*feature->getGeometry())[i].y()));
//为dragger添加回调
dragger->addPositionChangedCallback(new MoveFeatureDraggerCallback( _featureNode.get(), i) );
//将dragger添加到场景中
addChild(dragger);
}
}

下面来看看dragger

dragger继承自GeoPositionNode,对于GeoPositionNode,我们不需要知道太多,GeoPositionNode继承自Group,它的特殊之处在于它具有地理信息(毕竟人家叫 Geo Positon嘛),它提供了一个setPosition接口,我们可以通过这个接口来设置GeoPositionNode的位置。

/**
* Dragger is a handle you can use to control things in the scene.
* You drag it around with the mouse and it fires PositionChangedCallback's
* that you can listen to to repond to.
*/
class OSGEARTHANNO_EXPORT Dragger : public GeoPositionNode
{
public:
/**
* Callback that is fired when the position changes
*/
struct PositionChangedCallback : public osg::Referenced
{
public:
virtual void onPositionChanged(const Dragger* sender, const osgEarth::GeoPoint& position) {};
virtual ~PositionChangedCallback() { }
}; typedef std::list< osg::ref_ptr<PositionChangedCallback> > PositionChangedCallbackList; enum DragMode
{
DRAGMODE_HORIZONTAL,
DRAGMODE_VERTICAL
}; Dragger( MapNode* mapNode, int modKeyMask=0, const DragMode& defaultMode=DRAGMODE_HORIZONTAL ); /** dtor */
virtual ~Dragger(); /** Sets the map position of the dragger, optionally firing a PositionChanged event. */
void setPosition(const osgEarth::GeoPoint& position, bool fireEvents); /** Drag mode */
void setDefaultDragMode(const DragMode& mode) { _defaultMode = mode; }
DragMode& getDefaultDragMode() { return _defaultMode; } /** Add a callback that runs whenever the user moves the dragger */
void addPositionChangedCallback( PositionChangedCallback* callback ); /** Remove a callback. */
void removePositionChangedCallback( PositionChangedCallback* callback ); public: // GeoPositionNode virtual void setPosition(const GeoPoint& point); public: // osg::Node virtual void traverse(osg::NodeVisitor& nv); protected:
virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa);
void firePositionChanged(); bool _dragging;
bool _hovered;
PositionChangedCallbackList _callbacks; osg::ref_ptr< osgManipulator::LineProjector > _projector;
osgManipulator::PointerInfo _pointer;
osg::Vec3d _startProjectedPoint;
bool _elevationDragging;
int _modKeyMask;
DragMode _defaultMode;
double _verticalMinimum;
};

dragger的代码有一丢丢长,为求直观,上面的代码中我删除了其中一部分代码。

/**
* Dragger is a handle you can use to control things in the scene.
* You drag it around with the mouse and it fires PositionChangedCallback's
* that you can listen to to repond to.
*/

官方给的注释是:可以利用dragger来control scene中的对象,当你用鼠标拖动dragger时,会触发PositionChangedCallback回调。

还记得之前FeatureEditor中init函数中的这句代码么?

//为dragger添加回调
dragger->addPositionChangedCallback(new MoveFeatureDraggerCallback( _featureNode.get(), i) );

我们先不管MoveFeatureDraggerCallback具体是什么,只需要知道它继承自PositionChangedCallback(PostionChangedCallback的代码在上面的代码里),PositionChangedCallback这个函数包含一个虚函数:

virtual void onPositionChanged(const Dragger* sender, const osgEarth::GeoPoint& position) {};

显然MoveFeatureChangedCallback对这个函数进行了重写,重写的内容具体是什么,我们先不管,先将注意力放在addPositionChangedCallback这个函数上。

void Dragger::addPositionChangedCallback( PositionChangedCallback* callback )
{
_callbacks.push_back( callback );
}

这个函数的功能也很直观,将PositionChangedCallback添加进一个列表中,这个列表何时被用到?

void Dragger::firePositionChanged()
{
for( PositionChangedCallbackList::iterator i = _callbacks.begin(); i != _callbacks.end(); i++ )
{
i->get()->onPositionChanged(this, getPosition());
}
}

firePositonChanged函数遍历列表中的PositionChangedCallback对象,调用其onPositionChanged函数。firePositionChanged函数何时被调用?

void Dragger::setPosition(const GeoPoint& position, bool fireEvents)
{
GeoPositionNode::setPosition( position );
if ( fireEvents )
firePositionChanged();
}

再看看handle函数。handle函数是用来处理gui事件的,在handle函数中,firePositionChanged()setPosition()多次出现过。

bool Dragger::handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
{
if (ea.getHandled()) return false; osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
if (!view) return false;
if (!getMapNode()) return false; if (ea.getEventType() == osgGA::GUIEventAdapter::PUSH)
{
IntersectionPicker picker( view, this );
IntersectionPicker::Hits hits; if ( picker.pick( ea.getX(), ea.getY(), hits ) )
{
const GeoPoint& position = getPosition(); _dragging = true; //Check for and handle vertical dragging if necessary
bool pressedAlt = _modKeyMask && (ea.getModKeyMask() & _modKeyMask) > 0;
_elevationDragging = (_defaultMode == Dragger::DRAGMODE_VERTICAL && !pressedAlt) || (_defaultMode == Dragger::DRAGMODE_HORIZONTAL && pressedAlt); if (_elevationDragging)
{
_pointer.reset(); // set movement range
// TODO: values 0.0 and 300000.0 are rather experimental
GeoPoint posStart(position.getSRS(), position.x(), position.y(), 0.0, ALTMODE_ABSOLUTE);
osg::Vec3d posStartXYZ;
posStart.toWorld(posStartXYZ); GeoPoint posEnd(position.getSRS(), position.x(), position.y(), 300000.0, ALTMODE_ABSOLUTE);
osg::Vec3d posEndXYZ;
posEnd.toWorld(posEndXYZ); _projector->setLine(posStartXYZ, posEndXYZ); // set camera
osgUtil::LineSegmentIntersector::Intersections intersections;
osg::Node::NodeMask intersectionMask = 0xffffffff;
osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
if ( !view )
return true; if (view->computeIntersections(ea.getX(),ea.getY(),intersections, intersectionMask))
{
for (osgUtil::LineSegmentIntersector::Intersections::iterator hitr = intersections.begin(); hitr != intersections.end(); ++hitr)
{
_pointer.addIntersection(hitr->nodePath, hitr->getLocalIntersectPoint());
} bool draggerFound = false;
for (osgManipulator::PointerInfo::IntersectionList::iterator piit = _pointer._hitList.begin(); piit != _pointer._hitList.end(); ++piit)
{
for (osg::NodePath::iterator itr = piit->first.begin(); itr != piit->first.end(); ++itr)
{
Dragger* dragger = dynamic_cast<Dragger*>(*itr);
if (dragger==this)
{
draggerFound = true;
osg::Camera *rootCamera = view->getCamera();
osg::NodePath nodePath = _pointer._hitList.front().first;
osg::NodePath::reverse_iterator ritr;
for (ritr = nodePath.rbegin(); ritr != nodePath.rend(); ++ritr)
{
osg::Camera* camera = dynamic_cast<osg::Camera*>(*ritr);
if (camera && (camera->getReferenceFrame()!=osg::Transform::RELATIVE_RF || camera->getParents().empty()))
{
rootCamera = camera;
break;
}
}
_pointer.setCamera(rootCamera);
_pointer.setMousePosition(ea.getX(), ea.getY()); break;
}
} if (draggerFound)
break;
}
}
} aa.requestRedraw();
return true;
}
}
else if (ea.getEventType() == osgGA::GUIEventAdapter::RELEASE)
{
_elevationDragging = false; if ( _dragging )
{
_dragging = false;
firePositionChanged();
} aa.requestRedraw();
}
else if (ea.getEventType() == osgGA::GUIEventAdapter::DRAG)
{
if (_elevationDragging)
{
_pointer._hitIter = _pointer._hitList.begin();
_pointer.setMousePosition(ea.getX(), ea.getY()); if (_projector->project(_pointer, _startProjectedPoint))
{
const GeoPoint& position = getPosition(); //Get the absolute mapPoint that they've drug it to.
GeoPoint projectedPos;
projectedPos.fromWorld(position.getSRS(), _startProjectedPoint); // make sure point is not dragged down below
// TODO: think of a better solution / HeightAboveTerrain performance issues?
if (projectedPos.z() >= _verticalMinimum)
{
//If the current position is relative, we need to convert the absolute world point to relative.
//If the point is absolute then just emit the absolute point.
if (position.altitudeMode() == ALTMODE_RELATIVE)
{
projectedPos.transformZ(ALTMODE_RELATIVE, getMapNode()->getTerrain());
} setPosition( projectedPos );
aa.requestRedraw();
}
} return true;
} if (_dragging)
{
osg::Vec3d world;
if ( getMapNode() && getMapNode()->getTerrain()->getWorldCoordsUnderMouse(view, ea.getX(), ea.getY(), world) )
{
const GeoPoint& position = getPosition(); //Get the absolute mapPoint that they've drug it to.
GeoPoint mapPoint;
mapPoint.fromWorld( getMapNode()->getMapSRS(), world ); //If the current position is relative, we need to convert the absolute world point to relative.
//If the point is absolute then just emit the absolute point.
if (position.altitudeMode() == ALTMODE_RELATIVE)
{
mapPoint.alt() = position.alt();
mapPoint.altitudeMode() = ALTMODE_RELATIVE;
} setPosition( mapPoint );
aa.requestRedraw();
return true;
}
}
}
else if (ea.getEventType() == osgGA::GUIEventAdapter::MOVE)
{
IntersectionPicker picker( view, this );
IntersectionPicker::Hits hits; if ( picker.pick( ea.getX(), ea.getY(), hits ) )
{
setHover( true );
}
else
{
setHover( false );
}
aa.requestRedraw();
}
return false;
}

dragger有两种模式,水平模式和垂直模式,这两种模式的差异在handle()中得到了体现。

enum DragMode
{
DRAGMODE_HORIZONTAL,
DRAGMODE_VERTICAL
};

handle函数的内容我大体说明一下:当鼠标push时,判断是否点击到了dragger上,如果击中的话,判断当前拖动模式是水平模式还是垂直模式,如果是垂直模式的话,就创建一个projector对象,根据dragger当前的位置得到一条直线,将projector的投影对象设为该直线。之后drag时,将鼠标所处位置的世界坐标投影到该直线上。(顺带一提,这部分代码可以参考一下osg中的dragger类的handle函数,两者还是挺相似的,都用到了投影的方法

若为水平模式,那就不需要投影了,在drag时,直接根据鼠标的位置,得到地面坐标就ok了。

故handle函数的主要作用就是当drag dragger时,利用setPosition函数实时地更新dragger的位置,并触发回调。

最后再回头看看MoveFeatureChangedCallback()这个类。

class MoveFeatureDraggerCallback : public Dragger::PositionChangedCallback
{
public:
MoveFeatureDraggerCallback(FeatureNode* featureNode, int point):
_featureNode( featureNode ),
_point(point)
{} virtual void onPositionChanged(const Dragger* sender, const osgEarth::GeoPoint& position)
{
Feature* feature = _featureNode->getFeatures().front().get();
(*feature->getGeometry())[_point] = osg::Vec3d(position.x(), position.y(), 0);
_featureNode->init();
} osg::ref_ptr< FeatureNode > _featureNode; int _point; };

重点关注onPositionChanged这个重写的虚函数,它有两个参数,sender代表调用这个函数的dragger,position表示dragger的位置,函数内容很简单,就是根据dragger的位置来更改geometry中point的位置,最后再重绘geometry。

剖析到此结束,我们总结一下:假设一个geometry有十个point,当我们根据这个geometry创建一个FeatureEditor是,FeatureEditor会创建十个Dragger,每个Dragger的初始位置都是Point所处的位置,当Draggr被移动时,会触发回调,从而改变这个Dragger所绑定的point的位置,以上!

建议再重头看一下上面的剖析过程。

最新文章

  1. Java中判断字符串是否为数字的五种方法
  2. [转]jQuery Popup Login and Contact Form
  3. hduoj 4708 Rotation Lock Puzzle 2013 ACM/ICPC Asia Regional Online —— Warmup
  4. 越狱Season 1-Episode 17: J-Cat
  5. mysql优化小技巧
  6. Java Concurrency - Semaphore 信号量
  7. Android基于GridView实现的翻牌游戏效果
  8. 简单地使用jquery的validate
  9. 认识cocos2d-x jsbinding
  10. Spring Boot快速入门
  11. Linux 内核综述
  12. 前端Blob对二进制流数据的处理方式
  13. UNIX网络编程——解决TCP网络传输“粘包”问题
  14. 在Web中获取MAC地址
  15. SDN网络中hypervisor带来的控制器时延(Hypervisor位置的优化)
  16. 15Linux_DHCP_Postfix_Dovecot_LDAP
  17. HashTable使用举例--C#
  18. 挖矿病毒 qW3xT.2 最终解决方案
  19. 那些不错的 [ Html5 + CSS3 + Canvas ] 效果!
  20. Sword redis配置

热门文章

  1. C++类中的重载
  2. ReactNative: 将自定义的ReactNative组件制作成第三方库的详细流程(制作--&gt;发布)
  3. java 支持分词的高性能拼音转换工具,速度是 pinyin4j 的两倍
  4. Activiti 手工任务(manualTask)
  5. ES.01.Elasticsearch安装配置
  6. Oracle GoldenGate 19.1新特性
  7. 「 深入浅出 」集合Map
  8. 关于Spring集成Quartz的concurrent属性
  9. typescript step by step interface class
  10. NOIP游记