From ca7acb1f2cb10e4a8db2434642f44f2eb23cce6e Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Thu, 26 Feb 2015 22:34:21 +0100 Subject: [PATCH] canvas::Map: Property to keep children aligned to given hdg. Setting the 'hdg' property on child elements will rotate them with respect to the heading set on the map projection. --- simgear/canvas/elements/CanvasMap.cxx | 162 ++++++++++++--------- simgear/canvas/elements/CanvasMap.hxx | 10 +- simgear/canvas/elements/map/projection.hxx | 39 +++-- 3 files changed, 134 insertions(+), 77 deletions(-) diff --git a/simgear/canvas/elements/CanvasMap.cxx b/simgear/canvas/elements/CanvasMap.cxx index bbf5645e..ee60bf24 100644 --- a/simgear/canvas/elements/CanvasMap.cxx +++ b/simgear/canvas/elements/CanvasMap.cxx @@ -44,6 +44,7 @@ namespace canvas //---------------------------------------------------------------------------- const std::string GEO = "-geo"; + const std::string HDG = "hdg"; const std::string Map::TYPE_NAME = "map"; //---------------------------------------------------------------------------- @@ -112,87 +113,44 @@ namespace canvas //---------------------------------------------------------------------------- void Map::childAdded(SGPropertyNode* parent, SGPropertyNode* child) { - if( !boost::ends_with(child->getNameString(), GEO) ) + if( boost::ends_with(child->getNameString(), GEO) ) + _geo_nodes[child].reset(new GeoNodePair()); + else if( parent != _node && child->getNameString() == HDG ) + _hdg_nodes.insert(child); + else 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) ) + if( boost::ends_with(child->getNameString(), GEO) ) + // TODO remove from other node + _geo_nodes.erase(child); + else if( parent != _node && child->getName() == HDG ) + _hdg_nodes.erase(child); + else return Element::childRemoved(parent, child); - - // TODO remove from other node - _geo_nodes.erase(child); } //---------------------------------------------------------------------------- - void Map::valueChanged(SGPropertyNode * 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 ) + if( child->getParent() != _node ) { - // Detect lat, lon tuples... - GeoCoord coord = parseGeoCoord(child->getStringValue()); - int index_other = -1; + const std::string& name = child->getNameString(); - 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()) ); + if( boost::ends_with(name, GEO) ) + return geoNodeChanged(child); + else if( name == HDG ) + return hdgNodeChanged(child); } + + return Group::valueChanged(child); } //---------------------------------------------------------------------------- - void Map::childChanged(SGPropertyNode * child) + void Map::childChanged(SGPropertyNode* child) { if( child->getParent() != _node ) return Group::childChanged(child); @@ -201,8 +159,14 @@ namespace canvas || child->getNameString() == "ref-lon" ) _projection->setWorldPosition( _node->getDoubleValue("ref-lat"), _node->getDoubleValue("ref-lon") ); - else if( child->getNameString() == "hdg" ) + else if( child->getNameString() == HDG ) + { _projection->setOrientation(child->getFloatValue()); + for( NodeSet::iterator it = _hdg_nodes.begin(); + it != _hdg_nodes.end(); + ++it ) + hdgNodeChanged(*it); + } else if( child->getNameString() == "range" ) _projection->setRange(child->getDoubleValue()); else if( child->getNameString() == "screen-range" ) @@ -213,6 +177,74 @@ namespace canvas _projection_dirty = true; } + //---------------------------------------------------------------------------- + void Map::geoNodeChanged(SGPropertyNode* child) + { + GeoNodes::iterator it_geo_node = _geo_nodes.find(child); + if( it_geo_node == _geo_nodes.end() ) + LOG_GEO_RET("GeoNode not found!") + GeoNodePair* geo_node = it_geo_node->second.get(); + + geo_node->setDirty(); + + if( !(geo_node->getStatus() & GeoNodePair::INCOMPLETE) ) + return; + + // 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") + } + + const std::string& name = child->getNameString(); + 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::hdgNodeChanged(SGPropertyNode* child) + { + child->getParent()->setFloatValue( + "tf[0]/rot", + SGMiscf::deg2rad(child->getFloatValue() - _projection->orientation()) + ); + } + //---------------------------------------------------------------------------- Map::GeoCoord Map::parseGeoCoord(const std::string& val) const { diff --git a/simgear/canvas/elements/CanvasMap.hxx b/simgear/canvas/elements/CanvasMap.hxx index 6bd2e54d..4af74ac7 100644 --- a/simgear/canvas/elements/CanvasMap.hxx +++ b/simgear/canvas/elements/CanvasMap.hxx @@ -24,6 +24,7 @@ #include #include +#include namespace simgear { @@ -59,14 +60,18 @@ namespace canvas typedef boost::unordered_map< SGPropertyNode*, boost::shared_ptr > GeoNodes; + typedef boost::unordered_set NodeSet; + GeoNodes _geo_nodes; + NodeSet _hdg_nodes; boost::shared_ptr _projection; bool _projection_dirty; struct GeoCoord { GeoCoord(): - type(INVALID) + type(INVALID), + value(0) {} enum { @@ -77,6 +82,9 @@ namespace canvas double value; }; + void geoNodeChanged(SGPropertyNode * child); + void hdgNodeChanged(SGPropertyNode * child); + GeoCoord parseGeoCoord(const std::string& val) const; }; diff --git a/simgear/canvas/elements/map/projection.hxx b/simgear/canvas/elements/map/projection.hxx index 9a3f65d7..d60f7d5f 100644 --- a/simgear/canvas/elements/map/projection.hxx +++ b/simgear/canvas/elements/map/projection.hxx @@ -34,7 +34,10 @@ namespace canvas public: struct ScreenPosition { - ScreenPosition() {} + ScreenPosition(): + x(0), + y(0) + {} ScreenPosition(double x, double y): x(x), @@ -67,8 +70,11 @@ namespace canvas public: HorizontalProjection(): - _cos_rot(1), - _sin_rot(0), + _ref_lat(0), + _ref_lon(0), + _angle(0), + _cos_angle(1), + _sin_angle(0), _range(5) { setScreenRange(200); @@ -88,9 +94,19 @@ namespace canvas */ void setOrientation(float hdg) { + _angle = hdg; + hdg = SGMiscf::deg2rad(hdg); - _sin_rot = sin(hdg); - _cos_rot = cos(hdg); + _sin_angle = sin(hdg); + _cos_angle = cos(hdg); + } + + /** + * Get orientation/heading of the projection (in degree) + */ + float orientation() const + { + return _angle; } void setRange(double range) @@ -114,8 +130,8 @@ namespace canvas pos.y *= scale; return ScreenPosition ( - _cos_rot * pos.x - _sin_rot * pos.y, - -_sin_rot * pos.x - _cos_rot * pos.y + _cos_angle * pos.x - _sin_angle * pos.y, + -_sin_angle * pos.x - _cos_angle * pos.y ); } @@ -129,10 +145,11 @@ namespace canvas */ virtual ScreenPosition project(double lat, double lon) const = 0; - double _ref_lat, - _ref_lon, - _cos_rot, - _sin_rot, + double _ref_lat, ///