X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;ds=sidebyside;f=src%2FInstrumentation%2FNavDisplay.cxx;h=ecf9bdfc5532fbf67ee98b2258c9ec893681129a;hb=7d547d128702a643c40b54809e2b4e1a38c4a16d;hp=4cc9d5b3b1ce28dfba986a4a0b8a750c5afbaad1;hpb=10e6cc016bcfe39332dce2f360eaa90ff8366cf2;p=flightgear.git diff --git a/src/Instrumentation/NavDisplay.cxx b/src/Instrumentation/NavDisplay.cxx index 4cc9d5b3b..ecf9bdfc5 100644 --- a/src/Instrumentation/NavDisplay.cxx +++ b/src/Instrumentation/NavDisplay.cxx @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -42,6 +43,7 @@ #include #include #include +#include #include #include @@ -56,6 +58,8 @@ using std::setw; using std::setfill; using std::cout; using std::endl; +using std::map; +using std::string; #include
#include
@@ -155,29 +159,49 @@ public: virtual void valueChanged (SGPropertyNode * prop) { _nd->invalidatePositionedCache(); - SG_LOG(SG_INSTR, SG_INFO, "invalidating NavDisplay cache"); } private: NavDisplay* _nd; }; +class NavDisplay::ForceUpdateListener : public SGPropertyChangeListener +{ +public: + ForceUpdateListener(NavDisplay *nd) : + _nd(nd) + {} + + virtual void valueChanged (SGPropertyNode * prop) + { + _nd->forceUpdate(); + } +private: + NavDisplay* _nd; +}; + /////////////////////////////////////////////////////////////////// -class SymbolDef +class SymbolRule { public: - SymbolDef() : - enable(NULL) - {} - - bool initFromNode(SGPropertyNode* node) + SymbolRule() + { + + } + + bool initFromNode(SGPropertyNode* node, NavDisplay* owner) { + if (!node->getChild("type")) { + return false; + } + type = node->getStringValue("type"); + boost::to_lower(type); SGPropertyNode* enableNode = node->getChild("enable"); if (enableNode) { - enable = sgReadCondition(fgGetNode("/"), enableNode); + enable.reset(sgReadCondition(fgGetNode("/"), enableNode)); } - + int n=0; while (node->hasChild("state", n)) { string m = node->getChild("state", n++)->getStringValue(); @@ -188,21 +212,90 @@ public: } } // of matches parsing - if (node->hasChild("width")) { - float w = node->getFloatValue("width"); - float h = node->getFloatValue("height", w); - xy0.x() = -w * 0.5; - xy0.y() = -h * 0.5; - xy1.x() = w * 0.5; - xy1.y() = h * 0.5; - } else { - xy0.x() = node->getFloatValue("x0", 0.0); - xy0.y() = node->getFloatValue("y0", 0.0); - xy1.x() = node->getFloatValue("x1", 5); - xy1.y() = node->getFloatValue("y1", 5); - } + + return true; + } + + void setDefinition(SymbolDef* d) + { + definition = d; + } + + SymbolDef* getDefinition() const + { return definition; } + + bool matches(const string_set& states) const + { + BOOST_FOREACH(const string& s, required_states) { + if (states.count(s) == 0) { + return false; + } + } + + BOOST_FOREACH(const string& s, excluded_states) { + if (states.count(s) != 0) { + return false; + } + } + + return true; + } + + // return if the enabled state changed (needs a cache update) + bool checkEnabled() + { + if (enable.get()) { + bool wasEnabled = enabled; + enabled = enable->test(); + return (enabled != wasEnabled); + } else { + enabled = true; + return false; + } + } + + bool enabled; // cached enabled state + std::string type; + + // record instances for limiting by count + int instanceCount; +private: + SymbolDef* definition; + + std::auto_ptr enable; + string_set required_states; + string_set excluded_states; +}; + +class SymbolDef +{ +public: + SymbolDef() : limitCount(0) { } + + bool initFromNode(SGPropertyNode* node, NavDisplay* owner) + { + if (node->getChild("type")) { + SymbolRule* builtinRule = new SymbolRule; + builtinRule->initFromNode(node, owner); + builtinRule->setDefinition(this); + owner->addRule(builtinRule); + } + + if (node->hasChild("width")) { + float w = node->getFloatValue("width"); + float h = node->getFloatValue("height", w); + xy0.x() = -w * 0.5; + xy0.y() = -h * 0.5; + xy1.x() = w * 0.5; + xy1.y() = h * 0.5; + } else { + xy0.x() = node->getFloatValue("x0", 0.0); + xy0.y() = node->getFloatValue("y0", 0.0); + xy1.x() = node->getFloatValue("x1", 5); + xy1.y() = node->getFloatValue("y1", 5); + } - double texSize = node->getFloatValue("texture-size", 1.0); + double texSize = node->getFloatValue("texture-size", owner->textureSize()); uv0.x() = node->getFloatValue("u0", 0) / texSize; uv0.y() = node->getFloatValue("v0", 0) / texSize; @@ -222,6 +315,11 @@ public: textOffset.x() = node->getFloatValue("text-offset-x", 0); textOffset.y() = node->getFloatValue("text-offset-y", 0); textColor = readColor(node->getChild("text-color"), color); + + SGPropertyNode* enableNode = node->getChild("text-enable"); + if (enableNode) { + textEnable.reset(sgReadCondition(fgGetNode("/"), enableNode)); + } } drawLine = node->getBoolValue("draw-line", false); @@ -232,20 +330,18 @@ public: if (stretchSymbol) { stretchY2 = node->getFloatValue("y2"); stretchY3 = node->getFloatValue("y3"); - stretchV2 = node->getFloatValue("v2"); - stretchV3 = node->getFloatValue("v3"); + stretchV2 = node->getFloatValue("v2") / texSize; + stretchV3 = node->getFloatValue("v3") / texSize; } + SGPropertyNode* limitNode = node->getChild("limit"); + if (limitNode) { + limitCount = limitNode->getIntValue(); + } + return true; } - 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; @@ -255,6 +351,8 @@ public: bool rotateToHeading; bool roundPos; ///< should position be rounded to integer values bool hasText; + std::auto_ptr textEnable; + bool textEnabled; ///< cache condition result osg::Vec4 textColor; osg::Vec2 textOffset; osgText::Text::AlignmentType alignment; @@ -273,22 +371,7 @@ public: bool drawRouteLeg; - bool matches(const string_set& states) const - { - BOOST_FOREACH(const string& s, required_states) { - if (states.count(s) == 0) { - return false; - } - } - - BOOST_FOREACH(const string& s, excluded_states) { - if (states.count(s) != 0) { - return false; - } - } - - return true; - } + int limitCount, instanceCount; }; class SymbolInstance @@ -353,13 +436,14 @@ NavDisplay::NavDisplay(SGPropertyNode *node) : _num(node->getIntValue("number", 0)), _time(0.0), _updateInterval(node->getDoubleValue("update-interval-sec", 0.1)), + _forceUpdate(true), _odg(0), _scale(0), _view_heading(0), - _resultTexture(0), _font_size(0), _font_spacing(0), - _rangeNm(0) + _rangeNm(0), + _maxSymbols(100) { _Instrument = fgGetNode(string("/instrumentation/" + _name).c_str(), _num, true); _font_node = _Instrument->getNode("font", true); @@ -374,41 +458,87 @@ NavDisplay::NavDisplay(SGPropertyNode *node) : INITFONT("color/alpha", 1, Float); #undef INITFONT + _textureSize = _Instrument->getNode("symbol-texture-size", true)->getIntValue(); SGPropertyNode* symbolsNode = node->getNode("symbols"); SGPropertyNode* symbol; - + + map definitionDict; for (int i = 0; (symbol = symbolsNode->getChild("symbol", i)) != NULL; ++i) { SymbolDef* def = new SymbolDef; - if (!def->initFromNode(symbol)) { + if (!def->initFromNode(symbol, this)) { delete def; continue; } - _rules.push_back(def); + const char* id = symbol->getStringValue("id"); + if (id && strlen(id)) { + definitionDict[id] = def; + } + + _definitions.push_back(def); } // of symbol definition parsing + + SGPropertyNode* rulesNode = node->getNode("rules"); + if (rulesNode) { + SGPropertyNode* rule; + + for (int i = 0; (rule = rulesNode->getChild("rule", i)) != NULL; ++i) { + SymbolRule* r = new SymbolRule; + if (!r->initFromNode(rule, this)) { + delete r; + continue; + } + + const char* id = symbol->getStringValue("symbol"); + if (id && strlen(id) && (definitionDict.find(id) != definitionDict.end())) { + r->setDefinition(definitionDict[id]); + } else { + SG_LOG(SG_INSTR, SG_WARN, "symbol rule has missing/unknown definition id:" << id); + delete r; + continue; + } + + addRule(r); + } // of symbol rule parsing + } + } NavDisplay::~NavDisplay() { + delete _odg; } - void NavDisplay::init () { _cachedItemsValid = false; _cacheListener.reset(new CacheListener(this)); - + _forceUpdateListener.reset(new ForceUpdateListener(this)); + _serviceable_node = _Instrument->getNode("serviceable", true); _rangeNode = _Instrument->getNode("range", true); - _rangeNode->setDoubleValue(40.0); + if (!_rangeNode->hasValue()) { + _rangeNode->setDoubleValue(40.0); + } _rangeNode->addChangeListener(_cacheListener.get()); - + _rangeNode->addChangeListener(_forceUpdateListener.get()); + + _xCenterNode = _Instrument->getNode("x-center"); + if (!_xCenterNode->hasValue()) { + _xCenterNode->setDoubleValue(0.5); + } + _xCenterNode->addChangeListener(_forceUpdateListener.get()); + _yCenterNode = _Instrument->getNode("y-center"); + if (!_yCenterNode->hasValue()) { + _yCenterNode->setDoubleValue(0.5); + } + _yCenterNode->addChangeListener(_forceUpdateListener.get()); + // texture name to use in 2D and 3D instruments _texture_path = _Instrument->getStringValue("radar-texture-path", "Aircraft/Instruments/Textures/od_wxradar.rgb"); - _resultTexture = FGTextureManager::createTexture(_texture_path.c_str(), false); string path = _Instrument->getStringValue("symbol-texture-path", "Aircraft/Instruments/Textures/nd-symbols.png"); @@ -420,8 +550,7 @@ NavDisplay::init () // no mipmap or else alpha will mix with pixels on the border of shapes, ruining the effect _symbolTexture = SGLoadTexture2D(tpath, NULL, false, false); - FGInstrumentMgr *imgr = (FGInstrumentMgr *)globals->get_subsystem("instrumentation"); - _odg = (FGODGauge *)imgr->get_subsystem("od_gauge"); + _odg = new FGODGauge; _odg->setSize(_Instrument->getIntValue("texture-size", 512)); _route = static_cast(globals->get_subsystem("route-manager")); @@ -431,7 +560,10 @@ NavDisplay::init () _excessDataNode = _Instrument->getChild("excess-data", 0, true); _excessDataNode->setBoolValue(false); + _testModeNode = _Instrument->getChild("test-mode", 0, true); + _testModeNode->setBoolValue(false); + _viewHeadingNode = _Instrument->getChild("view-heading-deg", 0, true); // OSG geometry setup _radarGeode = new osg::Geode; @@ -440,7 +572,8 @@ NavDisplay::init () osg::StateSet *stateSet = _geom->getOrCreateStateSet(); stateSet->setTextureAttributeAndModes(0, _symbolTexture.get()); - + stateSet->setDataVariance(osg::Object::STATIC); + // Initially allocate space for 128 quads _vertices = new osg::Vec2Array; _vertices->setDataVariance(osg::Object::DYNAMIC); @@ -519,25 +652,45 @@ NavDisplay::update (double delta_time_sec) return; } - _time += delta_time_sec; - if (_time < _updateInterval){ - return; + if (_forceUpdate) { + _forceUpdate = false; + _time = 0.0; + } else { + _time += delta_time_sec; + if (_time < _updateInterval){ + return; + } + _time -= _updateInterval; } - _time -= _updateInterval; _rangeNm = _rangeNode->getFloatValue(); - if (_Instrument->getBoolValue("aircraft-heading-up", true)) { + if (_testModeNode->getBoolValue()) { + _view_heading = 90; + } else if (_Instrument->getBoolValue("aircraft-heading-up", true)) { _view_heading = fgGetDouble("/orientation/heading-deg"); } else { _view_heading = _Instrument->getFloatValue("heading-up-deg", 0.0); } + _viewHeadingNode->setDoubleValue(_view_heading); + + double xCenterFrac = _xCenterNode->getDoubleValue(); + double yCenterFrac = _yCenterNode->getDoubleValue(); + int pixelSize = _odg->size(); + + int rangePixels = _Instrument->getIntValue("range-pixels", -1); + if (rangePixels < 0) { + // hacky - assume (as is very common) that x-frac doesn't vary, and + // y-frac is used to position the center at either the top or bottom of + // the pixel area. Measure from the center to the furthest edge (top or bottom) + rangePixels = pixelSize * std::max(fabs(1.0 - yCenterFrac), fabs(yCenterFrac)); + } + + _scale = rangePixels / _rangeNm; + _Instrument->setDoubleValue("scale", _scale); - _scale = _odg->size() / _rangeNm; - double xCenterFrac = _Instrument->getDoubleValue("x-center", 0.5); - double yCenterFrac = _Instrument->getDoubleValue("y-center", 0.5); - _centerTrans = osg::Matrixf::translate(xCenterFrac * _odg->size(), - yCenterFrac * _odg->size(), 0.0); + _centerTrans = osg::Matrixf::translate(xCenterFrac * pixelSize, + yCenterFrac * pixelSize, 0.0); // scale from nm to display units, rotate so aircraft heading is up // (as opposed to north), and compensate for centering @@ -551,9 +704,6 @@ NavDisplay::update (double delta_time_sec) SGVec3d cartNow(SGVec3d::fromGeod(_pos)); double movedNm = dist(_cachedPos, cartNow) * SG_METER_TO_NM; _cachedItemsValid = (movedNm < 1.0); - if (!_cachedItemsValid) { - SG_LOG(SG_INSTR, SG_INFO, "invalidating NavDisplay cache due to moving: " << movedNm); - } } _vertices->clear(); @@ -567,20 +717,32 @@ NavDisplay::update (double delta_time_sec) delete si; } _symbols.clear(); - - BOOST_FOREACH(SymbolDef* def, _rules) { - if (def->enable) { - def->enabled = def->enable->test(); - } else { - def->enabled = true; - } + + BOOST_FOREACH(SymbolDef* d, _definitions) { + d->instanceCount = 0; + d->textEnabled = d->textEnable.get() ? d->textEnable->test() : true; } - processRoute(); - processNavRadios(); - processAI(); - findItems(); - limitDisplayedSymbols(); + bool enableChanged = false; + BOOST_FOREACH(SymbolRule* r, _rules) { + enableChanged |= r->checkEnabled(); + } + + if (enableChanged) { + SG_LOG(SG_INSTR, SG_INFO, "NS rule enables changed, rebuilding cache"); + _cachedItemsValid = false; + } + + if (_testModeNode->getBoolValue()) { + addTestSymbols(); + } else { + processRoute(); + processNavRadios(); + processAI(); + findItems(); + limitDisplayedSymbols(); + } + addSymbolsToScene(); _symbolPrimSet->set(osg::PrimitiveSet::QUADS, 0, _vertices->size()); @@ -635,7 +797,7 @@ void NavDisplay::addSymbolToScene(SymbolInstance* sym) verts[3] = osg::Vec2(def->xy0.x(), def->xy1.y()); if (def->rotateToHeading) { - osg::Matrixf m(degRotation(sym->headingDeg)); + osg::Matrixf m(degRotation(sym->headingDeg - _view_heading)); for (int i=0; i<4; ++i) { verts[i] = mult(verts[i], m); } @@ -663,7 +825,7 @@ void NavDisplay::addSymbolToScene(SymbolInstance* sym) stretchVerts[2] = osg::Vec2(def->xy1.x(), def->stretchY3); stretchVerts[3] = osg::Vec2(def->xy0.x(), def->stretchY3); - osg::Matrixf m(degRotation(sym->headingDeg)); + osg::Matrixf m(degRotation(sym->headingDeg - _view_heading)); for (int i=0; i<4; ++i) { stretchVerts[i] = mult(stretchVerts[i], m); } @@ -699,7 +861,7 @@ void NavDisplay::addSymbolToScene(SymbolInstance* sym) addLine(sym->pos, sym->endPos, def->lineColor); } - if (!def->hasText) { + if (!def->hasText || !def->textEnabled) { return; } @@ -730,14 +892,15 @@ public: void NavDisplay::limitDisplayedSymbols() { - unsigned int maxSymbols = _Instrument->getIntValue("max-symbols", 100); - if (_symbols.size() <= maxSymbols) { +// gloabl symbol limit + _maxSymbols= _Instrument->getIntValue("max-symbols", _maxSymbols); + if ((int) _symbols.size() <= _maxSymbols) { _excessDataNode->setBoolValue(false); return; } std::sort(_symbols.begin(), _symbols.end(), OrderByPriority()); - _symbols.resize(maxSymbols); + _symbols.resize(_maxSymbols); _excessDataNode->setBoolValue(true); } @@ -784,6 +947,8 @@ osg::Vec2 NavDisplay::projectGeod(const SGGeod& geod) const class Filter : public FGPositioned::Filter { public: + Filter(NavDisplay* nd) : _owner(nd) { } + double minRunwayLengthFt; virtual bool pass(FGPositioned* aPos) const @@ -803,7 +968,8 @@ public: } } - return true; + // check against current rule states + return _owner->isPositionedShown(aPos); } virtual FGPositioned::Type minType() const { @@ -813,19 +979,24 @@ public: virtual FGPositioned::Type maxType() const { return FGPositioned::OBSTACLE; } + +private: + NavDisplay* _owner; }; void NavDisplay::findItems() { if (!_cachedItemsValid) { - SG_LOG(SG_INSTR, SG_INFO, "re-validating NavDisplay cache"); - Filter filt; - filt.minRunwayLengthFt = 2000; - _itemsInRange = FGPositioned::findWithinRange(_pos, _rangeNm, &filt); + Filter filt(this); + filt.minRunwayLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft", 2000); + _itemsInRange = FGPositioned::findClosestN(_pos, _maxSymbols, _rangeNm, &filt); _cachedItemsValid = true; _cachedPos = SGVec3d::fromGeod(_pos); } + // sort by distance from pos, so symbol limits are accurate + FGPositioned::sortByRange(_itemsInRange, _pos); + BOOST_FOREACH(FGPositioned* pos, _itemsInRange) { foundPositionedItem(pos); } @@ -834,54 +1005,60 @@ void NavDisplay::findItems() void NavDisplay::processRoute() { _routeSources.clear(); - RoutePath path(_route->waypts()); + flightgear::FlightPlan* fp = _route->flightPlan(); + RoutePath path(fp); int current = _route->currentIndex(); - for (int w=0; w<_route->numWaypts(); ++w) { - flightgear::WayptRef wpt(_route->wayptAtIndex(w)); + for (int l=0; lnumLegs(); ++l) { + flightgear::FlightPlan::Leg* leg = fp->legAtIndex(l); + flightgear::WayptRef wpt(leg->waypoint()); _routeSources.insert(wpt->source()); string_set state; state.insert("on-active-route"); - if (w < current) { + if (l < current) { state.insert("passed"); } - if (w == current) { + if (l == current) { state.insert("current-wp"); } - if (w > current) { + if (l > current) { state.insert("future"); } - if (w == (current + 1)) { + if (l == (current + 1)) { state.insert("next-wp"); } - SymbolDefVector rules; + SymbolRuleVector rules; findRules("waypoint" , state, rules); if (rules.empty()) { return; // no rules matched, we can skip this item } - SGGeod g = path.positionForIndex(w); - SGPropertyNode* vars = _route->wayptNodeAtIndex(w); + SGGeod g = path.positionForIndex(l); + SGPropertyNode* vars = _route->wayptNodeAtIndex(l); + if (!vars) { + continue; // shouldn't happen, but let's guard against it + } + double heading; computeWayptPropsAndHeading(wpt, g, vars, heading); osg::Vec2 projected = projectGeod(g); - BOOST_FOREACH(SymbolDef* r, rules) { - addSymbolInstance(projected, heading, r, vars); + BOOST_FOREACH(SymbolRule* r, rules) { + addSymbolInstance(projected, heading, r->getDefinition(), vars); - if (r->drawRouteLeg) { - SGGeodVec gv(path.pathForIndex(w)); + if (r->getDefinition()->drawRouteLeg) { + SGGeodVec gv(path.pathForIndex(l)); if (!gv.empty()) { osg::Vec2 pr = projectGeod(gv[0]); for (unsigned int i=1; ilineColor); + addLine(pr, p, r->getDefinition()->lineColor); pr = p; } } @@ -911,8 +1088,8 @@ void NavDisplay::processNavRadios() FGNavRecord* NavDisplay::processNavRadio(const SGPropertyNode_ptr& radio) { - double mhz = radio->getDoubleValue("frequencies/selected-mhz", 0.0); - FGNavRecord* nav = globals->get_navlist()->findByFreq(mhz, _pos); + double mhz = radio->getDoubleValue("frequencies/selected-mhz", 0.0); + FGNavRecord* nav = FGNavList::findByFreq(mhz, _pos, FGNavList::navFilter()); if (!nav || (nav->ident() != radio->getStringValue("nav-id"))) { // station was not found return NULL; @@ -924,7 +1101,7 @@ FGNavRecord* NavDisplay::processNavRadio(const SGPropertyNode_ptr& radio) bool NavDisplay::anyRuleForType(const string& type) const { - BOOST_FOREACH(SymbolDef* r, _rules) { + BOOST_FOREACH(SymbolRule* r, _rules) { if (!r->enabled) { continue; } @@ -937,24 +1114,9 @@ bool NavDisplay::anyRuleForType(const string& type) const 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) +void NavDisplay::findRules(const string& type, const string_set& states, SymbolRuleVector& rules) { - BOOST_FOREACH(SymbolDef* candidate, _rules) { + BOOST_FOREACH(SymbolRule* candidate, _rules) { if (!candidate->enabled || (candidate->type != type)) { continue; } @@ -965,33 +1127,55 @@ void NavDisplay::findRules(const string& type, const string_set& states, SymbolD } } +bool NavDisplay::isPositionedShown(FGPositioned* pos) +{ + SymbolRuleVector rules; + isPositionedShownInner(pos, rules); + return !rules.empty(); +} + +void NavDisplay::isPositionedShownInner(FGPositioned* pos, SymbolRuleVector& rules) +{ + string type = FGPositioned::nameForType(pos->type()); + boost::to_lower(type); + if (!anyRuleForType(type)) { + return; // not diplayed at all, we're done + } + + string_set states; + computePositionedState(pos, states); + + findRules(type, states, rules); +} + 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); + SymbolRuleVector rules; + isPositionedShownInner(pos, rules); if (rules.empty()) { - return; // no rules matched, we can skip this item + return; } - + 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); + if (pos->type() == FGPositioned::RUNWAY) { + FGRunway* rwy = (FGRunway*) pos; + projected = projectGeod(rwy->threshold()); + } + + BOOST_FOREACH(SymbolRule* r, rules) { + SymbolInstance* ins = addSymbolInstance(projected, heading, r->getDefinition(), vars); + if ((ins)&&(pos->type() == FGPositioned::RUNWAY)) { + FGRunway* rwy = (FGRunway*) pos; + ins->endPos = projectGeod(rwy->end()); + } } } @@ -1001,6 +1185,7 @@ void NavDisplay::computePositionedPropsAndHeading(FGPositioned* pos, SGPropertyN nd->setStringValue("name", pos->name()); nd->setDoubleValue("elevation-ft", pos->elevation()); nd->setIntValue("heading-deg", 0); + heading = 0.0; switch (pos->type()) { case FGPositioned::VOR: @@ -1010,11 +1195,12 @@ void NavDisplay::computePositionedPropsAndHeading(FGPositioned* pos, SGPropertyN nd->setDoubleValue("frequency-mhz", nav->get_freq()); if (pos == _nav1Station) { - nd->setIntValue("heading-deg", _navRadio1Node->getDoubleValue("radials/target-radial-deg")); + heading = _navRadio1Node->getDoubleValue("radials/target-radial-deg"); } else if (pos == _nav2Station) { - nd->setIntValue("heading-deg", _navRadio2Node->getDoubleValue("radials/target-radial-deg")); + heading = _navRadio2Node->getDoubleValue("radials/target-radial-deg"); } + nd->setIntValue("heading-deg", heading); break; } @@ -1026,7 +1212,8 @@ void NavDisplay::computePositionedPropsAndHeading(FGPositioned* pos, SGPropertyN case FGPositioned::RUNWAY: { FGRunway* rwy = static_cast(pos); - nd->setDoubleValue("heading-deg", rwy->headingDeg()); + heading = rwy->headingDeg(); + nd->setDoubleValue("heading-deg", heading); nd->setIntValue("length-ft", rwy->lengthFt()); nd->setStringValue("airport", rwy->airport()->ident()); break; @@ -1043,6 +1230,7 @@ void NavDisplay::computePositionedState(FGPositioned* pos, string_set& states) states.insert("on-active-route"); } + flightgear::FlightPlan* fp = _route->flightPlan(); switch (pos->type()) { case FGPositioned::VOR: case FGPositioned::LOC: @@ -1063,21 +1251,21 @@ void NavDisplay::computePositionedState(FGPositioned* pos, string_set& states) // mark alternates! // once the FMS system has some way to tell us about them, of course - if (pos == _route->departureAirport()) { + if (pos == fp->departureAirport()) { states.insert("departure"); } - if (pos == _route->destinationAirport()) { + if (pos == fp->destinationAirport()) { states.insert("destination"); } break; case FGPositioned::RUNWAY: - if (pos == _route->departureRunway()) { + if (pos == fp->departureRunway()) { states.insert("departure"); } - if (pos == _route->destinationRunway()) { + if (pos == fp->destinationRunway()) { states.insert("destination"); } break; @@ -1100,6 +1288,16 @@ void NavDisplay::computePositionedState(FGPositioned* pos, string_set& states) } // FGPositioned::Type switch } +static string mapAINodeToType(SGPropertyNode* model) +{ + // assume all multiplayer items are aircraft for the moment. Not ideal. + if (!strcmp(model->getName(), "multiplayer")) { + return "ai-aircraft"; + } + + return string("ai-") + model->getName(); +} + void NavDisplay::processAI() { SGPropertyNode *ai = fgGetNode("/ai/models", true); @@ -1113,8 +1311,8 @@ void NavDisplay::processAI() // with fg-positioned. string_set ss; computeAIStates(model, ss); - SymbolDefVector rules; - findRules(string("ai-") + model->getName(), ss, rules); + SymbolRuleVector rules; + findRules(mapAINodeToType(model), ss, rules); if (rules.empty()) { return; // no rules matched, we can skip this item } @@ -1128,8 +1326,8 @@ void NavDisplay::processAI() model->setIntValue("flight-level", fl * 10); osg::Vec2 projected = projectGeod(aiModelPos); - BOOST_FOREACH(SymbolDef* r, rules) { - addSymbolInstance(projected, heading, r, (SGPropertyNode*) model); + BOOST_FOREACH(SymbolRule* r, rules) { + addSymbolInstance(projected, heading, r->getDefinition(), (SGPropertyNode*) model); } } // of ai models iteration } @@ -1137,11 +1335,15 @@ void NavDisplay::processAI() 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)); - } - + if (threatLevel < 1) + threatLevel = 0; + + states.insert("tcas"); + + std::ostringstream os; + os << "tcas-threat-level-" << threatLevel; + states.insert(os.str()); + double vspeed = ai->getDoubleValue("velocities/vertical-speed-fps"); if (vspeed < -3.0) { states.insert("descending"); @@ -1150,15 +1352,20 @@ void NavDisplay::computeAIStates(const SGPropertyNode* ai, string_set& states) } } -bool NavDisplay::addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars) +SymbolInstance* NavDisplay::addSymbolInstance(const osg::Vec2& proj, double heading, SymbolDef* def, SGPropertyNode* vars) { if (isProjectedClipped(proj)) { - return false; + return NULL; } + if ((def->limitCount > 0) && (def->instanceCount >= def->limitCount)) { + return NULL; + } + + ++def->instanceCount; SymbolInstance* sym = new SymbolInstance(proj, heading, def, vars); _symbols.push_back(sym); - return true; + return sym; } bool NavDisplay::isProjectedClipped(const osg::Vec2& projected) const @@ -1170,6 +1377,59 @@ bool NavDisplay::isProjectedClipped(const osg::Vec2& projected) const (projected.y() >= size); } +void NavDisplay::addTestSymbol(const std::string& type, const std::string& states, const SGGeod& pos, double heading, SGPropertyNode* vars) +{ + string_set stateSet; + BOOST_FOREACH(std::string s, simgear::strutils::split(states, ",")) { + stateSet.insert(s); + } + + SymbolRuleVector rules; + findRules(type, stateSet, rules); + if (rules.empty()) { + return; // no rules matched, we can skip this item + } + + osg::Vec2 projected = projectGeod(pos); + BOOST_FOREACH(SymbolRule* r, rules) { + addSymbolInstance(projected, heading, r->getDefinition(), vars); + } +} +void NavDisplay::addTestSymbols() +{ + _pos = SGGeod::fromDeg(-122.3748889, 37.6189722); // KSFO + + SGGeod a1; + double dummy; + SGGeodesy::direct(_pos, 45.0, 20.0 * SG_NM_TO_METER, a1, dummy); + + addTestSymbol("airport", "", a1, 0.0, NULL); + + SGGeodesy::direct(_pos, 95.0, 40.0 * SG_NM_TO_METER, a1, dummy); + + addTestSymbol("vor", "", a1, 0.0, NULL); + + SGGeodesy::direct(_pos, 120, 80.0 * SG_NM_TO_METER, a1, dummy); + + addTestSymbol("airport", "destination", a1, 0.0, NULL); + + SGGeodesy::direct(_pos, 80.0, 20.0 * SG_NM_TO_METER, a1, dummy); + addTestSymbol("fix", "", a1, 0.0, NULL); + + + SGGeodesy::direct(_pos, 140.0, 20.0 * SG_NM_TO_METER, a1, dummy); + addTestSymbol("fix", "", a1, 0.0, NULL); + + SGGeodesy::direct(_pos, 110.0, 10.0 * SG_NM_TO_METER, a1, dummy); + addTestSymbol("fix", "", a1, 0.0, NULL); + + SGGeodesy::direct(_pos, 110.0, 5.0 * SG_NM_TO_METER, a1, dummy); + addTestSymbol("fix", "", a1, 0.0, NULL); +} +void NavDisplay::addRule(SymbolRule* r) +{ + _rules.push_back(r); +}