From 563614f36c9e530279eb13ddf2311763987b153a Mon Sep 17 00:00:00 2001 From: James Turner Date: Sun, 3 Jul 2011 17:56:01 +0100 Subject: [PATCH] Expose route-manager WP mirror nodes on the API --- src/Autopilot/route_mgr.cxx | 11 +- src/Autopilot/route_mgr.hxx | 2 + src/Instrumentation/NavDisplay.cxx | 1267 +++++++++++++++++----------- src/Instrumentation/NavDisplay.hxx | 96 +-- 4 files changed, 837 insertions(+), 539 deletions(-) diff --git a/src/Autopilot/route_mgr.cxx b/src/Autopilot/route_mgr.cxx index f9bd77091..178b7cfa2 100644 --- a/src/Autopilot/route_mgr.cxx +++ b/src/Autopilot/route_mgr.cxx @@ -978,7 +978,6 @@ void FGRouteMgr::update_mirror() const SGGeod& pos(wp->position()); prop->setStringValue("id", wp->ident().c_str()); - //prop->setStringValue("name", wp.get_name().c_str()); prop->setDoubleValue("longitude-deg", pos.getLongitudeDeg()); prop->setDoubleValue("latitude-deg",pos.getLatitudeDeg()); @@ -995,6 +994,7 @@ void FGRouteMgr::update_mirror() double ft = wp->altitudeFt(); prop->setDoubleValue("altitude-m", ft * SG_FEET_TO_METER); prop->setDoubleValue("altitude-ft", ft); + prop->setIntValue("flight-level", static_cast(ft / 1000) * 10); } else { prop->setDoubleValue("altitude-m", -9999.9); prop->setDoubleValue("altitude-ft", -9999.9); @@ -1273,6 +1273,15 @@ Waypt* FGRouteMgr::wayptAtIndex(int index) const return _route[index]; } +SGPropertyNode_ptr FGRouteMgr::wayptNodeAtIndex(int index) const +{ + if ((index < 0) || (index >= numWaypts())) { + throw sg_range_exception("waypt index out of range", "FGRouteMgr::wayptAtIndex"); + } + + return mirror->getChild("wp", index); +} + bool FGRouteMgr::saveRoute(const SGPath& path) { SG_LOG(SG_IO, SG_INFO, "Saving route to " << path.str()); diff --git a/src/Autopilot/route_mgr.hxx b/src/Autopilot/route_mgr.hxx index a309bb364..2b3409b13 100644 --- a/src/Autopilot/route_mgr.hxx +++ b/src/Autopilot/route_mgr.hxx @@ -94,6 +94,8 @@ public: flightgear::Waypt* wayptAtIndex(int index) const; + SGPropertyNode_ptr wayptNodeAtIndex(int index) const; + /** * Find a waypoint in the route, by position, and return its index, or * -1 if no matching waypoint was found in the route. diff --git a/src/Instrumentation/NavDisplay.cxx b/src/Instrumentation/NavDisplay.cxx index c5d573f08..164fb40fd 100644 --- a/src/Instrumentation/NavDisplay.cxx +++ b/src/Instrumentation/NavDisplay.cxx @@ -25,6 +25,10 @@ #include "NavDisplay.hxx" +#include +#include +#include + #include #include #include @@ -67,10 +71,252 @@ using std::endl; #include "instrument_mgr.hxx" #include "od_gauge.hxx" -static const int SYMBOL_TEX_DIM = 8; -static const float UNIT = 1.0 / SYMBOL_TEX_DIM; static const char *DEFAULT_FONT = "typewriter.txf"; +static +osg::Matrixf degRotation(float angle) +{ + return osg::Matrixf::rotate(angle * SG_DEGREES_TO_RADIANS, 0.0f, 0.0f, -1.0f); +} + +static osg::Vec4 readColor(SGPropertyNode* colorNode, const osg::Vec4& c) +{ + osg::Vec4 result; + result.r() = colorNode->getDoubleValue("red", c.r()); + result.g() = colorNode->getDoubleValue("green", c.g()); + result.b() = colorNode->getDoubleValue("blue", c.b()); + result.a() = colorNode->getDoubleValue("alpha", c.a()); + return result; +} + +static osgText::Text::AlignmentType readAlignment(const std::string& t) +{ + if (t == "left-top") { + return osgText::Text::LEFT_TOP; + } else if (t == "left-center") { + return osgText::Text::LEFT_CENTER; + } else if (t == "left-bottom") { + return osgText::Text::LEFT_BOTTOM; + } else if (t == "center-top") { + return osgText::Text::CENTER_TOP; + } else if (t == "center-center") { + return osgText::Text::CENTER_CENTER; + } else if (t == "center-bottom") { + return osgText::Text::CENTER_BOTTOM; + } else if (t == "right-top") { + return osgText::Text::RIGHT_TOP; + } else if (t == "right-center") { + return osgText::Text::RIGHT_CENTER; + } else if (t == "right-bottom") { + return osgText::Text::RIGHT_BOTTOM; + } else if (t == "left-baseline") { + return osgText::Text::LEFT_BASE_LINE; + } else if (t == "center-baseline") { + return osgText::Text::CENTER_BASE_LINE; + } else if (t == "right-baseline") { + return osgText::Text::RIGHT_BASE_LINE; + } + + return osgText::Text::BASE_LINE; +} + +static string formatPropertyValue(SGPropertyNode* nd, const string& format) +{ + assert(nd); + static char buf[512]; + if (format.find('d') >= 0) { + ::snprintf(buf, 512, format.c_str(), nd->getIntValue()); + return buf; + } + + if (format.find('s') >= 0) { + ::snprintf(buf, 512, format.c_str(), nd->getStringValue()); + return buf; + } + +// assume it's a double/float + ::snprintf(buf, 512, format.c_str(), nd->getDoubleValue()); + return buf; +} + +static osg::Vec2 mult(const osg::Vec2& v, const osg::Matrixf& m) +{ + osg::Vec3 r = m.preMult(osg::Vec3(v.x(), v.y(), 0.0)); + return osg::Vec2(r.x(), r.y()); +} + +/////////////////////////////////////////////////////////////////// + +class SymbolDef +{ +public: + void initFromNode(SGPropertyNode* node) + { + type = node->getStringValue("type"); + enable = sgReadCondition(fgGetNode("/"), node->getChild("enable")); + int n=0; + while (node->hasChild("state", n)) { + string m = node->getChild("state", n++)->getStringValue(); + if (m[0] == '!') { + excluded_states.insert(m.substr(1)); + } else { + required_states.insert(m); + } + } // of matches parsing + + xy0.x() = node->getFloatValue("x0", -5); + xy0.y() = node->getFloatValue("y0", -5); + xy1.x() = node->getFloatValue("x1", 5); + xy1.y() = node->getFloatValue("y1", 5); + + double texSize = node->getFloatValue("texture-size", 1.0); + + uv0.x() = node->getFloatValue("u0", 0) / texSize; + uv0.y() = node->getFloatValue("v0", 0) / texSize; + uv1.x() = node->getFloatValue("u1", 1) / texSize; + uv1.y() = node->getFloatValue("v1", 1) / texSize; + + color = readColor(node->getChild("color"), osg::Vec4(1, 1, 1, 1)); + priority = node->getIntValue("priority", 0); + zOrder = node->getIntValue("zOrder", 0); + rotateToHeading = node->getBoolValue("rotate-to-heading", false); + roundPos = node->getBoolValue("round-position", true); + hasText = false; + if (node->hasChild("text")) { + hasText = true; + alignment = readAlignment(node->getStringValue("text-align")); + textTemplate = node->getStringValue("text"); + textOffset.x() = node->getFloatValue("text-offset-x", 0); + textOffset.y() = node->getFloatValue("text-offset-y", 0); + textColor = readColor(node->getChild("text-color"), color); + } + + drawLine = node->getBoolValue("draw-line", false); + lineColor = readColor(node->getChild("line-color"), color); + drawRouteLeg = node->getBoolValue("draw-line", false); + + stretchSymbol = node->getBoolValue("stretch-symbol", false); + if (stretchSymbol) { + stretchY2 = node->getFloatValue("y2"); + stretchY3 = node->getFloatValue("y3"); + stretchV2 = node->getFloatValue("v2"); + stretchV3 = node->getFloatValue("v3"); + } + } + + SGCondition* enable; + bool enabled; // cached enabled state + + std::string type; + string_set required_states; + string_set excluded_states; + + osg::Vec2 xy0, xy1; + osg::Vec2 uv0, uv1; + osg::Vec4 color; + + int priority; + int zOrder; + bool rotateToHeading; + bool roundPos; ///< should position be rounded to integer values + bool hasText; + osg::Vec4 textColor; + osg::Vec2 textOffset; + osgText::Text::AlignmentType alignment; + string textTemplate; + + bool drawLine; + osg::Vec4 lineColor; + +// symbol stretching creates three quads (instead of one) - a start, +// middle and end quad, positioned along the line of the symbol. +// X (and U) axis values determined by the values above, so we only need +// to define the Y (and V) values to build the other quads. + bool stretchSymbol; + double stretchY2, stretchY3; + double stretchV2, stretchV3; + + bool drawRouteLeg; + + bool matches(const string_set& states) const + { + string_set::const_iterator it = states.begin(), + end = states.end(); + for (; it != end; ++it) { + if (required_states.count(*it) == 0) { + // required state not matched + return false; + } + + if (excluded_states.count(*it) > 0) { + // excluded state matched + return false; + } + } // of applicable states iteration + + return true; // matches! + } +}; + +class SymbolInstance +{ +public: + SymbolInstance(const osg::Vec2& p, double h, SymbolDef* def, SGPropertyNode* vars) : + pos(p), + headingDeg(h), + definition(def), + props(vars) + { } + + osg::Vec2 pos; // projected position + osg::Vec2 endPos; + double headingDeg; + SymbolDef* definition; + SGPropertyNode_ptr props; + + string text() const + { + assert(definition->hasText); + string r; + + int pos = 0; + int lastPos = 0; + + for (; pos < (int) definition->textTemplate.size();) { + pos = definition->textTemplate.find('{', pos); + if (pos == -1) { // no more replacements + r.append(definition->textTemplate.substr(lastPos)); + break; + } + + r.append(definition->textTemplate.substr(lastPos, pos - lastPos)); + + int endReplacement = definition->textTemplate.find('}', pos+1); + if (endReplacement <= pos) { + return "bad replacement"; + } + + string spec = definition->textTemplate.substr(pos + 1, endReplacement - (pos + 1)); + // look for formatter in spec + int colonPos = spec.find(':'); + if (colonPos < 0) { + // simple replacement + r.append(props->getStringValue(spec)); + } else { + string format = spec.substr(colonPos + 1); + string prop = spec.substr(0, colonPos); + r.append(formatPropertyValue(props->getNode(prop), format)); + } + + lastPos = endReplacement + 1; + } + + return r; + } +}; + +////////////////////////////////////////////////////////////////// + NavDisplay::NavDisplay(SGPropertyNode *node) : _name(node->getStringValue("name", "nd")), _num(node->getIntValue("number", 0)), @@ -120,7 +366,7 @@ NavDisplay::init () SGPath tpath = globals->resolve_aircraft_path(path); // no mipmap or else alpha will mix with pixels on the border of shapes, ruining the effect - _symbols = SGLoadTexture2D(tpath, NULL, false, false); + _symbolTexture = SGLoadTexture2D(tpath, NULL, false, false); FGInstrumentMgr *imgr = (FGInstrumentMgr *)globals->get_subsystem("instrumentation"); _odg = (FGODGauge *)imgr->get_subsystem("od_gauge"); @@ -131,22 +377,6 @@ NavDisplay::init () _user_lon_node = fgGetNode("/position/longitude-deg", true); _user_alt_node = fgGetNode("/position/altitude-ft", true); - SGPropertyNode *n = _Instrument->getNode("display-controls", true); - _radar_weather_node = n->getNode("WX", true); - _radar_position_node = n->getNode("pos", true); - _radar_data_node = n->getNode("data", true); - _radar_symbol_node = n->getNode("symbol", true); - _radar_centre_node = n->getNode("centre", true); - _radar_tcas_node = n->getNode("tcas", true); - _radar_absalt_node = n->getNode("abs-altitude", true); - _radar_arpt_node = n->getNode("airport", true); - _radar_station_node = n->getNode("station", true); - _draw_track_node = n->getNode("ground-track", true); - _draw_heading_node = n->getNode("heading", true); - _draw_north_node = n->getNode("north", true); - _draw_fix_node = n->getNode("fixes", true); - - _ai_enabled_node = fgGetNode("/sim/ai/enabled", true); _route = static_cast(globals->get_subsystem("route-manager")); _navRadio1Node = fgGetNode("/instrumentation/nav[0]", true); @@ -159,7 +389,7 @@ NavDisplay::init () _geom->setUseDisplayList(false); osg::StateSet *stateSet = _geom->getOrCreateStateSet(); - stateSet->setTextureAttributeAndModes(0, _symbols.get()); + stateSet->setTextureAttributeAndModes(0, _symbolTexture.get()); // Initially allocate space for 128 quads _vertices = new osg::Vec2Array; @@ -171,7 +401,7 @@ NavDisplay::init () _texCoords->reserve(128 * 4); _geom->setTexCoordArray(0, _texCoords); - _quadColors = new osg::Vec3Array; + _quadColors = new osg::Vec4Array; _geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX); _geom->setColorArray(_quadColors); @@ -199,7 +429,7 @@ NavDisplay::init () _lineGeometry->setVertexArray(_lineVertices); - _lineColors = new osg::Vec3Array; + _lineColors = new osg::Vec4Array; _lineColors->setDataVariance(osg::Object::DYNAMIC); _lineGeometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX); _lineGeometry->setColorArray(_lineColors); @@ -225,28 +455,6 @@ NavDisplay::init () updateFont(); } - -// Local coordinates for each echo -const osg::Vec3f symbolCoords[4] = { - osg::Vec3f(-.7f, -.7f, 0.0f), osg::Vec3f(.7f, -.7f, 0.0f), - osg::Vec3f(.7f, .7f, 0.0f), osg::Vec3f(-.7f, .7f, 0.0f) -}; - - -const osg::Vec2f symbolTexCoords[4] = { - osg::Vec2f(0.0f, 0.0f), osg::Vec2f(UNIT, 0.0f), - osg::Vec2f(UNIT, UNIT), osg::Vec2f(0.0f, UNIT) -}; - - -// Rotate by a heading value -static inline -osg::Matrixf wxRotate(float angle) -{ - return osg::Matrixf::rotate(angle, 0.0f, 0.0f, -1.0f); -} - - void NavDisplay::update (double delta_time_sec) { @@ -265,17 +473,14 @@ NavDisplay::update (double delta_time_sec) } _time -= _updateInterval; - string mode = _Instrument->getStringValue("display-mode", "arc"); _rangeNm = _Instrument->getFloatValue("range", 40.0); if (_Instrument->getBoolValue("aircraft-heading-up", true)) { _view_heading = fgGetDouble("/orientation/heading-deg"); } else { _view_heading = _Instrument->getFloatValue("heading-up-deg", 0.0); } - _view_heading *= SG_DEGREES_TO_RADIANS; _scale = _odg->size() / _rangeNm; - _drawData = _radar_data_node->getBoolValue(); double xCenterFrac = _Instrument->getDoubleValue("x-center", 0.5); double yCenterFrac = _Instrument->getDoubleValue("y-center", 0.5); @@ -285,7 +490,7 @@ NavDisplay::update (double delta_time_sec) // scale from nm to display units, rotate so aircraft heading is up // (as opposed to north), and compensate for centering _projectMat = osg::Matrixf::scale(_scale, _scale, 1.0) * - wxRotate(-_view_heading) * _centerTrans; + degRotation(-_view_heading) * _centerTrans; _pos = SGGeod::fromDegFt(_user_lon_node->getDoubleValue(), _user_lat_node->getDoubleValue(), @@ -297,25 +502,16 @@ NavDisplay::update (double delta_time_sec) _texCoords->clear(); _textGeode->removeDrawables(0, _textGeode->getNumDrawables()); - update_aircraft(); - update_stations(); - update_airports(); - update_waypoints(); - update_route(); - - osg::Vec2 origin = projectGeod(_pos); - if (_draw_heading_node->getBoolValue()) { - addLine(origin, projectBearingRange(fgGetDouble("/orientation/heading-deg"), _rangeNm), osg::Vec3(1, 1, 1)); + BOOST_FOREACH(SymbolDef* def, _rules) { + def->enabled = def->enable->test(); } - if (_draw_track_node->getBoolValue()) { - double groundTrackDeg = fgGetDouble("/instrumentation/gps/indicated-track-true-deg"); - addLine(origin, projectBearingRange(groundTrackDeg, _rangeNm), osg::Vec3(1, 1, 1)); - } - - if (_draw_north_node->getBoolValue()) { - addLine(origin, projectBearingRange(0, _rangeNm), osg::Vec3(0, 1, 1)); - } + processRoute(); + processNavRadios(); + processAI(); + findItems(); + limitDisplayedSymbols(); + addSymbolsToScene(); _symbolPrimSet->set(osg::PrimitiveSet::QUADS, 0, _vertices->size()); _symbolPrimSet->dirty(); @@ -323,423 +519,6 @@ NavDisplay::update (double delta_time_sec) _linePrimSet->dirty(); } -void NavDisplay::addLine(osg::Vec2 a, osg::Vec2 b, const osg::Vec3& color) -{ - _lineVertices->push_back(a); - _lineVertices->push_back(b); - _lineColors->push_back(color); - _lineColors->push_back(color); -} - -osg::Vec2 NavDisplay::projectBearingRange(double bearingDeg, double rangeNm) const -{ - osg::Vec3 p(0, rangeNm, 0.0); - p = wxRotate(bearingDeg * SG_DEGREES_TO_RADIANS).preMult(p); - p = _projectMat.preMult(p); - return osg::Vec2(p.x(), p.y()); -} - -osg::Matrixf NavDisplay::project(const SGGeod& geod) const -{ - double rangeM, bearing, az2; - SGGeodesy::inverse(_pos, geod, bearing, az2, rangeM); - - double radius = ((rangeM * SG_METER_TO_NM) / _rangeNm) * _scale; - bearing *= SG_DEGREES_TO_RADIANS; - - return osg::Matrixf(wxRotate(_view_heading - bearing) - * osg::Matrixf::translate(0.0f, radius, 0.0f) - * wxRotate(bearing) * _centerTrans); - -} - -osg::Vec2 NavDisplay::projectGeod(const SGGeod& geod) const -{ - double rangeM, bearing, az2; - SGGeodesy::inverse(_pos, geod, bearing, az2, rangeM); - return projectBearingRange(bearing, rangeM * SG_METER_TO_NM); -} - -void -NavDisplay::addSymbol(const SGGeod& pos, int symbolIndex, const std::string& data, const osg::Vec3& color) -{ - osg::Vec2 xy = projectGeod(pos); - double scale = 20.0; - int symbolRow = (SYMBOL_TEX_DIM - 1) - (symbolIndex >> 4); - int symbolColumn = symbolIndex & 0x0f; - const osg::Vec2f texBase(UNIT * symbolColumn, UNIT * symbolRow); - - for (int i = 0; i < 4; i++) { - _texCoords->push_back(texBase + symbolTexCoords[i]); - osg::Vec2 coord(symbolCoords[i].x() * scale, symbolCoords[i].y() * scale); - _vertices->push_back(xy + coord); - _quadColors->push_back(color); - } - -// add data drawable - osgText::Text* text = new osgText::Text; - text->setFont(_font.get()); - text->setFontResolution(12, 12); - text->setCharacterSize(_font_size); - text->setLineSpacing(_font_spacing); - - text->setAlignment(osgText::Text::LEFT_CENTER); - text->setText(data); - - osg::Vec4 textColor(color.x(), color.y(), color.z(), 1); - text->setColor(textColor); - text->setPosition( osg::Vec3(xy.x() + 16, xy.y(), 0)); - _textGeode->addDrawable(text); -} - -void -NavDisplay::update_route() -{ - if (_route->numWaypts() < 2) { - return; - } - - RoutePath path(_route->waypts()); - for (int w=0; w<_route->numWaypts(); ++w) { - osg::Vec3 color(1.0, 0.0, 1.0); - SGGeodVec gv(path.pathForIndex(w)); - if (!gv.empty()) { - osg::Vec2 pr = projectGeod(gv[0]); - for (unsigned int i=1; iwayptAtIndex(w)); - SGGeod g = path.positionForIndex(w); - if (!(g == SGGeod())) { - int symbolIndex = (6 << 4); - osg::Vec3 color(1.0, 1.0, 1.0); - // active waypoint is magenta, not white - if (w == _route->currentIndex()) { - color = osg::Vec3(1.0, 0.0, 1.0); - } - - addSymbol(g, symbolIndex, wpt->ident(), color); - } - } // of waypoints iteration -} - -void -NavDisplay::update_stations() -{ - FGNavRecord* nav1 = drawTunedNavaid(_navRadio1Node); - FGNavRecord* nav2 = drawTunedNavaid(_navRadio2Node); - - if (_radar_station_node->getBoolValue()) { - osg::Vec3 cyanColor(0, 1, 1); - FGPositioned::TypeFilter filt(FGPositioned::VOR); - FGPositioned::List stations = - FGPositioned::findWithinRange(_pos, _rangeNm, &filt); - - FGPositioned::List::const_iterator it; - for (it = stations.begin(); it != stations.end(); ++it) { - FGPositioned* sta = *it; - if ((sta == nav1) || (sta == nav2)) { - continue; - } - - int symbolIndex = (6 << 4) + 2; - addSymbol(sta->geod(), symbolIndex, sta->ident(), cyanColor); - } - } // of stations beiong drawn -} - -class FixFilter : public FGPositioned::Filter -{ -public: - virtual bool pass(FGPositioned* aPos) const - { - // ignore fixes which end in digits - if (isdigit(aPos->ident()[3]) && isdigit(aPos->ident()[4])) { - return false; - } - - return true; - } - - virtual FGPositioned::Type minType() const { - return FGPositioned::FIX; - } - - virtual FGPositioned::Type maxType() const { - return FGPositioned::FIX; - } - -private: - bool _fixes, _navaids; -}; - -void -NavDisplay::update_waypoints() -{ - if (!_draw_fix_node->getBoolValue()) { - return; - } - - std::set routeWaypts; - for (int w=0; w<_route->numWaypts(); ++w) { - flightgear::WayptRef wpt(_route->wayptAtIndex(w)); - routeWaypts.insert(wpt->source()); - } - - osg::Vec3 cyanColor(0, 1, 1); - FixFilter filt; - FGPositioned::List fixes = - FGPositioned::findWithinRange(_pos, _rangeNm, &filt); - - FGPositioned::List::const_iterator it; - for (it = fixes.begin(); it != fixes.end(); ++it) { - FGPositioned* fix = *it; - if (routeWaypts.count(fix)) { - continue; // part of active route, don't draw here - } - - int symbolIndex = (6 << 4) + 0; - addSymbol(fix->geod(), symbolIndex, fix->ident(), cyanColor); - } // of draw fixes iteration -} - -FGNavRecord* -NavDisplay::drawTunedNavaid(const SGPropertyNode_ptr& radio ) -{ - double mhz = radio->getDoubleValue("frequencies/selected-mhz", 0.0); - FGNavRecord* nav = globals->get_navlist()->findByFreq(mhz, _pos); - if (!nav || (nav->ident() != radio->getStringValue("nav-id"))) { - // station was not found - return NULL; - } - - osg::Vec3 greenColor(0, 1, 0); - int symbolIndex = (6 << 4) + 2; - addSymbol(nav->geod(), symbolIndex, nav->ident(), greenColor); - -// Boeing: only show radial + reciprocal if manually tuned ... hmmmm - osg::Vec2 stationXY = projectGeod(nav->geod()); - SGGeod radialEnd; - double az2; - double trueRadial = radio->getDoubleValue("radials/target-radial-deg"); - SGGeodesy::direct(nav->geod(), trueRadial, - nav->get_range() * SG_NM_TO_METER, radialEnd, az2); - osg::Vec2 radialXY = projectGeod(radialEnd); - - osg::Vec2 d = radialXY - stationXY; - addLine(stationXY - d, radialXY, greenColor); - -// Boeing: if POS is selected, show radial to station - osg::Vec2 posXY = projectGeod(_pos); - addLine(posXY, stationXY, greenColor); - - osgText::Text* text = new osgText::Text; - text->setFont(_font.get()); - text->setFontResolution(12, 12); - text->setCharacterSize(_font_size); - text->setLineSpacing(_font_spacing); - text->setAlignment(osgText::Text::CENTER_BOTTOM); - text->setColor(osg::Vec4(0, 1, 0, 1)); - - stringstream s; - s << "R-" << setw(3) << setfill('0') << static_cast(trueRadial); - text->setText(s.str()); - osg::Vec2 mid = (posXY + stationXY) * 0.5; // radial mid-point - text->setPosition( osg::Vec3(mid.x(), mid.y(), 0)); - _textGeode->addDrawable(text); - - return nav; -} - -void -NavDisplay::update_airports() -{ - FGAirport* dep = _route->departureAirport(), - *arr = _route->destinationAirport(); - - if (_radar_arpt_node->getBoolValue()) { - osg::Vec3 cyanColor(0, 1, 1); - int symbolIndex = (6 << 4) + 3; - double minRunway = _Instrument->getDoubleValue("display-controls/min-runway-len-ft", 0.0); - FGAirport::HardSurfaceFilter filt(minRunway); - FGPositioned::List apts = - FGPositioned::findWithinRange(_pos, _rangeNm, &filt); - - FGPositioned::List::const_iterator it; - for (it = apts.begin(); it != apts.end(); ++it) { - FGPositioned* apt = *it; - if ((apt == dep) || (apt == arr)) { - // added seperately - continue; - } - - addSymbol(apt->geod(), symbolIndex, apt->ident(), cyanColor); - } // of airports iteration - } - - int symbolIndex = (7 << 4) + 1; - osg::Vec3 whiteColor(1, 1, 1); - FGRunway* rwy = _route->departureRunway(); - if (rwy) { - addSymbol(dep->geod(), symbolIndex, dep->ident() + "\n" + rwy->ident(), whiteColor); - } - - rwy = _route->destinationRunway(); - if (rwy) { - addSymbol(arr->geod(), symbolIndex, arr->ident() + "\n" + rwy->ident(), whiteColor); - } -} - -void -NavDisplay::update_aircraft() -{ - if (!_ai_enabled_node->getBoolValue()) { - return; - } - - bool draw_tcas = _radar_tcas_node->getBoolValue(); - if (!draw_tcas) { - return; - } - - bool draw_absolute = _radar_absalt_node->getBoolValue(); - - const SGPropertyNode *ai = fgGetNode("/ai/models", true); - for (int i = ai->nChildren() - 1; i >= 0; i--) { - const SGPropertyNode *model = ai->getChild(i); - if (!model->nChildren()) { - continue; - } - - double echo_radius, sigma; - const string name = model->getName(); - - if (name == "aircraft" || name == "tanker") - echo_radius = 1, sigma = 1; - else if (name == "multiplayer" || name == "wingman" || name == "static") - echo_radius = 1.5, sigma = 1; - else - continue; - - SGGeod aiModelPos = SGGeod::fromDegFt(model->getDoubleValue("position/longitude-deg"), - model->getDoubleValue("position/latitude-deg"), - model->getDoubleValue("position/altitude-ft")); - - double heading = model->getDoubleValue("orientation/true-heading-deg"); - double rangeM, bearing, az2; - SGGeodesy::inverse(_pos, aiModelPos, bearing, az2, rangeM); - - bearing *= SG_DEGREES_TO_RADIANS; - heading *= SG_DEGREES_TO_RADIANS; - - float radius = rangeM * _scale; - - bool is_tcas_contact = update_tcas(model,aiModelPos, - _pos.getElevationFt(), - aiModelPos.getElevationFt(), - draw_absolute); - - update_data(model, aiModelPos.getElevationFt(), heading, radius, bearing, false); - } // of ai models iteration -} - -/** Update TCAS display. - * Return true when processed as TCAS contact, false otherwise. */ -bool -NavDisplay::update_tcas(const SGPropertyNode *model, const SGGeod& modelPos, - double user_alt,double alt, bool absMode) -{ - int threatLevel = model->getIntValue("tcas/threat-level",-1); - if (threatLevel == -1) { - // no TCAS information (i.e. no transponder) => not visible to TCAS - return false; - } - - int row = 4; - int col = threatLevel; - double vspeed = model->getDoubleValue("velocities/vertical-speed-fps"); - if (vspeed < -3.0) // descending - row+=1; - // else -// if (vspeed > 3.0) // climbing - // col+=2; - - osg::Vec4 color = _tcas_colors[threatLevel]; - - stringstream text; - // altStr->setAlignment(osgText::Text::LEFT_CENTER); - int altDif = (alt-user_alt+50)/100; - char sign = 0; - int dy=0; - if (altDif>=0) - { - sign='+'; - dy=2; - } - else - if (altDif<0) - { - sign='-'; - altDif = -altDif; - dy=-30; - } - // altStr->setPosition(osg::Vec3((int)pos.x()-30, (int)pos.y()+dy, 0)); - if (absMode) - { - // absolute altitude display - text << setprecision(0) << fixed - << setw(3) << setfill('0') << alt/100 << endl; - } - else // relative altitude display - if (sign) - { - text << sign - << setprecision(0) << fixed - << setw(2) << setfill('0') << altDif << endl; - } - - addSymbol(modelPos, (col << 4) | row, text.str(), - osg::Vec3(color.r(), color.g(), color.b())); - - return true; -} - -void NavDisplay::update_data(const SGPropertyNode *ac, double altitude, double heading, - double radius, double bearing, bool selected) -{ - osgText::Text *callsign = new osgText::Text; - callsign->setFont(_font.get()); - callsign->setFontResolution(12, 12); - callsign->setCharacterSize(_font_size); - callsign->setColor(selected ? osg::Vec4(1, 1, 1, 1) : _font_color); - osg::Matrixf m(wxRotate(-bearing) - * osg::Matrixf::translate(0.0f, radius, 0.0f) - * wxRotate(bearing) * _centerTrans); - - osg::Vec3 pos = m.preMult(osg::Vec3(16, 16, 0)); - // cast to int's, otherwise text comes out ugly - callsign->setPosition(osg::Vec3((int)pos.x(), (int)pos.y(), 0)); - callsign->setAlignment(osgText::Text::LEFT_BOTTOM_BASE_LINE); - callsign->setLineSpacing(_font_spacing); - - const char *identity = ac->getStringValue("transponder-id"); - if (!identity[0]) - identity = ac->getStringValue("callsign"); - - stringstream text; - text << identity << endl - << setprecision(0) << fixed - << setw(3) << setfill('0') << heading * SG_RADIANS_TO_DEGREES << "\xB0 " - << setw(0) << altitude << "ft" << endl - << ac->getDoubleValue("velocities/true-airspeed-kt") << "kts"; - - callsign->setText(text.str()); - _textGeode->addDrawable(callsign); -} void NavDisplay::updateFont() @@ -777,17 +556,525 @@ NavDisplay::updateFont() _font->setGlyphImageMargin(0); _font->setGlyphImageMarginRatio(0); } +} - for (int i=0;i<4;i++) +void NavDisplay::addSymbolToScene(SymbolInstance* sym) +{ + SymbolDef* def = sym->definition; + + osg::Vec2 verts[4]; + verts[0] = def->xy0; + verts[1] = osg::Vec2(def->xy1.x(), def->xy0.y()); + verts[2] = def->xy1; + verts[3] = osg::Vec2(def->xy0.x(), def->xy1.y()); + + if (def->rotateToHeading) { + osg::Matrixf m(degRotation(sym->headingDeg)); + for (int i=0; i<4; ++i) { + verts[i] = mult(verts[i], m); + } + } + + osg::Vec2 pos = sym->pos; + if (def->roundPos) { + pos = osg::Vec2((int) pos.x(), (int) pos.y()); + } + + _texCoords->push_back(def->uv0); + _texCoords->push_back(osg::Vec2(def->uv1.x(), def->uv0.y())); + _texCoords->push_back(def->uv1); + _texCoords->push_back(osg::Vec2(def->uv0.x(), def->uv1.y())); + + for (int i=0; i<4; ++i) { + _vertices->push_back(verts[i] + pos); + _quadColors->push_back(def->color); + } + + if (def->stretchSymbol) { + osg::Vec2 stretchVerts[4]; + stretchVerts[0] = osg::Vec2(def->xy0.x(), def->stretchY2); + stretchVerts[1] = osg::Vec2(def->xy1.x(), def->stretchY2); + stretchVerts[2] = osg::Vec2(def->xy1.x(), def->stretchY3); + stretchVerts[3] = osg::Vec2(def->xy0.x(), def->stretchY3); + + osg::Matrixf m(degRotation(sym->headingDeg)); + for (int i=0; i<4; ++i) { + stretchVerts[i] = mult(stretchVerts[i], m); + } + + // stretched quad + _vertices->push_back(verts[2] + pos); + _vertices->push_back(stretchVerts[1] + sym->endPos); + _vertices->push_back(stretchVerts[0] + sym->endPos); + _vertices->push_back(verts[3] + pos); + + _texCoords->push_back(def->uv1); + _texCoords->push_back(osg::Vec2(def->uv1.x(), def->stretchV2)); + _texCoords->push_back(osg::Vec2(def->uv0.x(), def->stretchV2)); + _texCoords->push_back(osg::Vec2(def->uv0.x(), def->uv1.y())); + + for (int i=0; i<4; ++i) { + _quadColors->push_back(def->color); + } + + // quad three, for the end portion + for (int i=0; i<4; ++i) { + _vertices->push_back(stretchVerts[i] + sym->endPos); + _quadColors->push_back(def->color); + } + + _texCoords->push_back(osg::Vec2(def->uv0.x(), def->stretchV2)); + _texCoords->push_back(osg::Vec2(def->uv1.x(), def->stretchV2)); + _texCoords->push_back(osg::Vec2(def->uv1.x(), def->stretchV3)); + _texCoords->push_back(osg::Vec2(def->uv0.x(), def->stretchV3)); + } + + if (def->drawLine) { + addLine(sym->pos, sym->endPos, def->lineColor); + } + + if (!def->hasText) { + return; + } + + osgText::Text* t = new osgText::Text; + t->setFont(_font.get()); + t->setFontResolution(12, 12); + t->setCharacterSize(_font_size); + t->setLineSpacing(_font_spacing); + t->setColor(def->textColor); + t->setAlignment(def->alignment); + t->setText(sym->text()); + + + osg::Vec2 textPos = def->textOffset + pos; +// ensure we use ints here, or text visual quality goes bad + t->setPosition(osg::Vec3((int)textPos.x(), (int)textPos.y(), 0)); + _textGeode->addDrawable(t); +} + +class OrderByPriority +{ +public: + bool operator()(SymbolInstance* a, SymbolInstance* b) { - const float defaultColors[4][3] = {{0,1,1},{0,1,1},{1,0.5,0},{1,0,0}}; - SGPropertyNode_ptr color_node = _font_node->getNode("tcas/color",i,true); - float red = color_node->getFloatValue("red",defaultColors[i][0]); - float green = color_node->getFloatValue("green",defaultColors[i][1]); - float blue = color_node->getFloatValue("blue",defaultColors[i][2]); - float alpha = color_node->getFloatValue("alpha",1); - _tcas_colors[i]=osg::Vec4(red, green, blue, alpha); + return a->definition->priority > b->definition->priority; + } +}; + +void NavDisplay::limitDisplayedSymbols() +{ + unsigned int maxSymbols = _Instrument->getIntValue("max-symbols"); + if (_symbols.size() <= maxSymbols) { + _excessDataNode->setBoolValue(false); + return; + } + + std::sort(_symbols.begin(), _symbols.end(), OrderByPriority()); + _symbols.resize(maxSymbols); + _excessDataNode->setBoolValue(true); +} + +class OrderByZ +{ +public: + bool operator()(SymbolInstance* a, SymbolInstance* b) + { + return a->definition->zOrder > b->definition->zOrder; + } +}; + +void NavDisplay::addSymbolsToScene() +{ + std::sort(_symbols.begin(), _symbols.end(), OrderByZ()); + BOOST_FOREACH(SymbolInstance* sym, _symbols) { + addSymbolToScene(sym); } } +void NavDisplay::addLine(osg::Vec2 a, osg::Vec2 b, const osg::Vec4& color) +{ + _lineVertices->push_back(a); + _lineVertices->push_back(b); + _lineColors->push_back(color); + _lineColors->push_back(color); +} + +osg::Vec2 NavDisplay::projectBearingRange(double bearingDeg, double rangeNm) const +{ + osg::Vec3 p(0, rangeNm, 0.0); + p = degRotation(bearingDeg).preMult(p); + p = _projectMat.preMult(p); + return osg::Vec2(p.x(), p.y()); +} + +osg::Vec2 NavDisplay::projectGeod(const SGGeod& geod) const +{ + double rangeM, bearing, az2; + SGGeodesy::inverse(_pos, geod, bearing, az2, rangeM); + return projectBearingRange(bearing, rangeM * SG_METER_TO_NM); +} + +class Filter : public FGPositioned::Filter +{ +public: + virtual bool pass(FGPositioned* aPos) const + { + if (aPos->type() == FGPositioned::FIX) { + string ident(aPos->ident()); + // ignore fixes which end in digits + if ((ident.size() > 4) && isdigit(ident[3]) && isdigit(ident[4])) { + return false; + } + } + + return true; + } + + virtual FGPositioned::Type minType() const { + return FGPositioned::AIRPORT; + } + + virtual FGPositioned::Type maxType() const { + return FGPositioned::OBSTACLE; + } +}; + +void NavDisplay::findItems() +{ + Filter filt; + FGPositioned::List items = + FGPositioned::findWithinRange(_pos, _rangeNm, &filt); + + FGPositioned::List::const_iterator it; + for (it = items.begin(); it != items.end(); ++it) { + foundPositionedItem(*it); + } +} + +void NavDisplay::processRoute() +{ + _routeSources.clear(); + RoutePath path(_route->waypts()); + int current = _route->currentIndex(); + + for (int w=0; w<_route->numWaypts(); ++w) { + flightgear::WayptRef wpt(_route->wayptAtIndex(w)); + _routeSources.insert(wpt->source()); + + string_set state; + state.insert("on-active-route"); + + if (w < current) { + state.insert("passed"); + } + + if (w == current) { + state.insert("current-wp"); + } + + if (w > current) { + state.insert("future"); + } + + if (w == (current + 1)) { + state.insert("next-wp"); + } + + SymbolDefVector rules; + findRules(wpt->type() , state, rules); + if (rules.empty()) { + return; // no rules matched, we can skip this item + } + + SGGeod g = path.positionForIndex(w); + SGPropertyNode* vars = _route->wayptNodeAtIndex(w); + double heading; + computeWayptPropsAndHeading(wpt, g, vars, heading); + + osg::Vec2 projected = projectGeod(g); + BOOST_FOREACH(SymbolDef* r, rules) { + addSymbolInstance(projected, heading, r, vars); + + if (r->drawRouteLeg) { + SGGeodVec gv(path.pathForIndex(w)); + if (!gv.empty()) { + osg::Vec2 pr = projectGeod(gv[0]); + for (unsigned int i=1; ilineColor); + pr = p; + } + } + } // of leg drawing enabled + } // of matching rules iteration + } // of waypoints iteration +} + +void NavDisplay::computeWayptPropsAndHeading(flightgear::Waypt* wpt, const SGGeod& pos, SGPropertyNode* nd, double& heading) +{ + double rangeM, az2; + SGGeodesy::inverse(_pos, pos, heading, az2, rangeM); + nd->setIntValue("radial", heading); + nd->setDoubleValue("distance-nm", rangeM * SG_METER_TO_NM); + + heading = nd->getDoubleValue("leg-bearing-true-deg"); +} + +void NavDisplay::processNavRadios() +{ + _nav1Station = processNavRadio(_navRadio1Node); + _nav2Station = processNavRadio(_navRadio2Node); + + foundPositionedItem(_nav1Station); + foundPositionedItem(_nav2Station); +} + +FGNavRecord* NavDisplay::processNavRadio(const SGPropertyNode_ptr& radio) +{ + double mhz = radio->getDoubleValue("frequencies/selected-mhz", 0.0); + FGNavRecord* nav = globals->get_navlist()->findByFreq(mhz, _pos); + if (!nav || (nav->ident() != radio->getStringValue("nav-id"))) { + // station was not found + return NULL; + } + + + return nav; +} + +bool NavDisplay::anyRuleForType(const string& type) const +{ + BOOST_FOREACH(SymbolDef* r, _rules) { + if (!r->enabled) { + continue; + } + + if (r->type == type) { + return true; + } + } + + return false; +} + +bool NavDisplay::anyRuleMatches(const string& type, const string_set& states) const +{ + BOOST_FOREACH(SymbolDef* r, _rules) { + if (!r->enabled || (r->type != type)) { + continue; + } + + if (r->matches(states)) { + return true; + } + } // of rules iteration + + return false; +} + +void NavDisplay::findRules(const string& type, const string_set& states, SymbolDefVector& rules) +{ + BOOST_FOREACH(SymbolDef* candidate, _rules) { + if (!candidate->enabled) { + continue; + } + + if (candidate->matches(states)) { + rules.push_back(candidate); + } + } +} + +void NavDisplay::foundPositionedItem(FGPositioned* pos) +{ + if (!pos) { + return; + } + + string type = FGPositioned::nameForType(pos->type()); + if (!anyRuleForType(type)) { + return; // not diplayed at all, we're done + } + + string_set states; + computePositionedState(pos, states); + + SymbolDefVector rules; + findRules(type, states, rules); + if (rules.empty()) { + return; // no rules matched, we can skip this item + } + + SGPropertyNode_ptr vars(new SGPropertyNode); + double heading; + computePositionedPropsAndHeading(pos, vars, heading); + + osg::Vec2 projected = projectGeod(pos->geod()); + BOOST_FOREACH(SymbolDef* r, rules) { + addSymbolInstance(projected, heading, r, vars); + } +} + +void NavDisplay::computePositionedPropsAndHeading(FGPositioned* pos, SGPropertyNode* nd, double& heading) +{ + nd->setStringValue("id", pos->ident()); + nd->setStringValue("name", pos->name()); + nd->setDoubleValue("elevation-ft", pos->elevation()); + nd->setIntValue("heading-deg", 0); + + switch (pos->type()) { + case FGPositioned::VOR: + case FGPositioned::LOC: { + FGNavRecord* nav = static_cast(pos); + nd->setDoubleValue("frequency-mhz", nav->get_freq()); + + if (pos == _nav1Station) { + nd->setIntValue("heading-deg", _navRadio1Node->getDoubleValue("radials/target-radial-deg")); + } else if (pos == _nav2Station) { + nd->setIntValue("heading-deg", _navRadio2Node->getDoubleValue("radials/target-radial-deg")); + } + + break; + } + + case FGPositioned::AIRPORT: + case FGPositioned::SEAPORT: + case FGPositioned::HELIPORT: + + break; + + case FGPositioned::RUNWAY: { + FGRunway* rwy = static_cast(pos); + nd->setDoubleValue("heading-deg", rwy->headingDeg()); + nd->setIntValue("length-ft", rwy->lengthFt()); + nd->setStringValue("airport", rwy->airport()->ident()); + break; + } + + default: + break; + } +} + +void NavDisplay::computePositionedState(FGPositioned* pos, string_set& states) +{ + if (_routeSources.count(pos) != 0) { + states.insert("on-active-route"); + } + + switch (pos->type()) { + case FGPositioned::VOR: + case FGPositioned::LOC: + if (pos == _nav1Station) { + states.insert("tuned"); + states.insert("nav1"); + } + + if (pos == _nav2Station) { + states.insert("tuned"); + states.insert("nav2"); + } + break; + + case FGPositioned::AIRPORT: + case FGPositioned::SEAPORT: + case FGPositioned::HELIPORT: + // mark alternates! + // once the FMS system has some way to tell us about them, of course + + if (pos == _route->departureAirport()) { + states.insert("departure"); + } + + if (pos == _route->destinationAirport()) { + states.insert("destination"); + } + break; + + case FGPositioned::RUNWAY: + if (pos == _route->departureRunway()) { + states.insert("departure"); + } + + if (pos == _route->destinationRunway()) { + states.insert("destination"); + } + break; + + case FGPositioned::OBSTACLE: + #if 0 + FGObstacle* obs = (FGObstacle*) pos; + if (obj->isLit()) { + states.insert("lit"); + } + + if (obj->getHeightAGLFt() >= 1000) { + states.insert("greater-1000-ft"); + } + #endif + break; + + default: + break; + } // FGPositioned::Type switch +} + +void NavDisplay::processAI() +{ + SGPropertyNode *ai = fgGetNode("/ai/models", true); + for (int i = ai->nChildren() - 1; i >= 0; i--) { + SGPropertyNode *model = ai->getChild(i); + if (!model->nChildren()) { + continue; + } + + // prefix types with 'ai-', to avoid any chance of namespace collisions + // with fg-positioned. + string_set ss; + computeAIStates(model, ss); + SymbolDefVector rules; + findRules(string("ai-") + model->getName(), ss, rules); + if (rules.empty()) { + return; // no rules matched, we can skip this item + } + + double heading = model->getDoubleValue("orientation/true-heading-deg"); + SGGeod aiModelPos = SGGeod::fromDegFt(model->getDoubleValue("position/longitude-deg"), + model->getDoubleValue("position/latitude-deg"), + model->getDoubleValue("position/altitude-ft")); + // compute some additional props + int fl = (aiModelPos.getElevationFt() / 1000); + model->setIntValue("flight-level", fl * 10); + + osg::Vec2 projected = projectGeod(aiModelPos); + BOOST_FOREACH(SymbolDef* r, rules) { + addSymbolInstance(projected, heading, r, (SGPropertyNode*) model); + } + } // of ai models iteration +} + +void NavDisplay::computeAIStates(const SGPropertyNode* ai, string_set& states) +{ + int threatLevel = ai->getIntValue("tcas/threat-level",-1); + if (threatLevel >= 0) { + states.insert("tcas"); + // states.insert("tcas-threat-level-" + itoa(threatLevel)); + } + + double vspeed = ai->getDoubleValue("velocities/vertical-speed-fps"); + if (vspeed < -3.0) { + states.insert("descending"); + } else if (vspeed > 3.0) { + states.insert("climbing"); + } +} + +void NavDisplay::addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars) +{ + SymbolInstance* sym = new SymbolInstance(proj, heading, def, vars); + _symbols.push_back(sym); +} + + diff --git a/src/Instrumentation/NavDisplay.hxx b/src/Instrumentation/NavDisplay.hxx index 8d72bb80a..1c8a73885 100644 --- a/src/Instrumentation/NavDisplay.hxx +++ b/src/Instrumentation/NavDisplay.hxx @@ -38,6 +38,18 @@ class FGODGauge; class FGRouteMgr; class FGNavRecord; +class FGPositioned; + +class SymbolInstance; +class SymbolDef; + +namespace flightgear +{ + class Waypt; +} + +typedef std::set string_set; +typedef std::vector SymbolDefVector; class NavDisplay : public SGSubsystem { @@ -72,36 +84,41 @@ protected: SGPropertyNode *getInstrumentNode(const char *name, DefaultType value); private: + void addSymbolsToScene(); + void addSymbolToScene(SymbolInstance* sym); + void limitDisplayedSymbols(); + + void findItems(); + void foundPositionedItem(FGPositioned* pos); + void computePositionedPropsAndHeading(FGPositioned* pos, SGPropertyNode* nd, double& heading); + void computePositionedState(FGPositioned* pos, string_set& states); + void processRoute(); + void computeWayptPropsAndHeading(flightgear::Waypt* wpt, const SGGeod& pos, SGPropertyNode* nd, double& heading); + void processNavRadios(); + FGNavRecord* processNavRadio(const SGPropertyNode_ptr& radio); + void processAI(); + void computeAIStates(const SGPropertyNode* ai, string_set& states); + + bool anyRuleForType(const std::string& type) const; + bool anyRuleMatches(const std::string& type, const string_set& states) const; + void findRules(const std::string& type, const string_set& states, SymbolDefVector& rules); + + void addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars); + void addLine(osg::Vec2 a, osg::Vec2 b, const osg::Vec4& color); + osg::Vec2 projectBearingRange(double bearingDeg, double rangeNm) const; + osg::Vec2 projectGeod(const SGGeod& geod) const; + + void updateFont(); + string _texture_path; - typedef enum { ARC, MAP, PLAN, ROSE, BSCAN} DisplayMode; - DisplayMode _display_mode; float _scale; // factor to convert nm to display units float _view_heading; - bool _drawData; SGPropertyNode_ptr _Radar_controls; - SGPropertyNode_ptr _radar_weather_node; - SGPropertyNode_ptr _radar_position_node; - SGPropertyNode_ptr _radar_data_node; - SGPropertyNode_ptr _radar_symbol_node; - SGPropertyNode_ptr _radar_arpt_node; - SGPropertyNode_ptr _radar_station_node; - - SGPropertyNode_ptr _radar_centre_node; - SGPropertyNode_ptr _radar_coverage_node; - SGPropertyNode_ptr _radar_ref_rng_node; - SGPropertyNode_ptr _radar_hdg_marker_node; - SGPropertyNode_ptr _radar_rotate_node; - SGPropertyNode_ptr _radar_tcas_node; - SGPropertyNode_ptr _radar_absalt_node; - SGPropertyNode_ptr _draw_track_node; - SGPropertyNode_ptr _draw_heading_node; - SGPropertyNode_ptr _draw_north_node; - SGPropertyNode_ptr _draw_fix_node; SGPropertyNode_ptr _font_node; SGPropertyNode_ptr _ai_enabled_node; @@ -109,7 +126,7 @@ private: SGPropertyNode_ptr _navRadio2Node; osg::ref_ptr _resultTexture; - osg::ref_ptr _symbols; + osg::ref_ptr _symbolTexture; osg::ref_ptr _radarGeode; osg::ref_ptr _textGeode; @@ -118,12 +135,12 @@ private: osg::DrawArrays* _symbolPrimSet; osg::Vec2Array *_vertices; osg::Vec2Array *_texCoords; - osg::Vec3Array* _quadColors; + osg::Vec4Array* _quadColors; osg::Geometry* _lineGeometry; osg::DrawArrays* _linePrimSet; osg::Vec2Array* _lineVertices; - osg::Vec3Array* _lineColors; + osg::Vec4Array* _lineColors; osg::Matrixf _centerTrans; @@ -131,36 +148,19 @@ private: osg::ref_ptr _font; osg::Vec4 _font_color; - osg::Vec4 _tcas_colors[4]; float _font_size; float _font_spacing; FGRouteMgr* _route; SGGeod _pos; double _rangeNm; - - void update_route(); - void update_aircraft(); - void update_stations(); - void update_airports(); - void update_waypoints(); - - bool update_tcas(const SGPropertyNode *model, const SGGeod& modelPos, - double user_alt,double alt, bool absMode); - void update_data(const SGPropertyNode *ac, double altitude, double heading, - double radius, double bearing, bool selected); - void updateFont(); - - osg::Matrixf project(const SGGeod& geod) const; - osg::Vec2 projectBearingRange(double bearingDeg, double rangeNm) const; - osg::Vec2 projectGeod(const SGGeod& geod) const; - - void addSymbol(const SGGeod& pos, int symbolIndex, - const std::string& data, const osg::Vec3& color); - - void addLine(osg::Vec2 a, osg::Vec2 b, const osg::Vec3& color); - - FGNavRecord* drawTunedNavaid(const SGPropertyNode_ptr& radio ); + + SymbolDefVector _rules; + FGNavRecord* _nav1Station; + FGNavRecord* _nav2Station; + std::vector _symbols; + std::set _routeSources; + SGPropertyNode_ptr _excessDataNode; }; #endif // _INST_ND_HXX