From: James Turner Date: Tue, 14 Jan 2014 13:11:06 +0000 (+0000) Subject: Crashfix: move spatial, AI queries in map-widget X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=1b9394fc37c14a594b0d3e2d1354fad63354a521;p=flightgear.git Crashfix: move spatial, AI queries in map-widget In threaded OSG drawing, MapWidget::draw runs in the render thread context, but touches lots of main thread state. Move most of the work to an update() helper run on the main thread instead, so draw() mostly makes pure GL calls. (This is a fix for 3.0, the real solution is to migrate to a Canvas-based map and GUI) --- diff --git a/src/GUI/FGPUIDialog.cxx b/src/GUI/FGPUIDialog.cxx index a57aeba5e..a7e2dddbf 100644 --- a/src/GUI/FGPUIDialog.cxx +++ b/src/GUI/FGPUIDialog.cxx @@ -977,6 +977,7 @@ FGPUIDialog::makeObject (SGPropertyNode *props, int parentWidth, int parentHeigh } else if (type == "map") { MapWidget* mapWidget = new MapWidget(x, y, x + width, y + height); setupObject(mapWidget, props); + _activeWidgets.push_back(mapWidget); return mapWidget; } else if (type == "canvas") { CanvasWidget* canvasWidget = new CanvasWidget( x, y, diff --git a/src/GUI/MapWidget.cxx b/src/GUI/MapWidget.cxx index 3c2cc8ba9..8a5a0b715 100644 --- a/src/GUI/MapWidget.cxx +++ b/src/GUI/MapWidget.cxx @@ -69,7 +69,7 @@ static bool puBoxIntersect(const puBox& a, const puBox& b) return (x0 <= x1) && (y0 <= y1); } - + class MapData; typedef std::vector MapDataVec; @@ -380,6 +380,76 @@ int MapData::_fontDescender = 0; /////////////////////////////////////////////////////////////////////////// +// anonymous namespace +namespace +{ + +class MapAirportFilter : public FGAirport::AirportFilter +{ +public: + MapAirportFilter(SGPropertyNode_ptr nd) + { + _heliports = nd->getBoolValue("show-heliports", false); + _hardRunwaysOnly = nd->getBoolValue("hard-surfaced-airports", true); + _minLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft", 2000); + } + + virtual FGPositioned::Type maxType() const { + return _heliports ? FGPositioned::HELIPORT : FGPositioned::AIRPORT; + } + + virtual bool passAirport(FGAirport* aApt) const { + if (_hardRunwaysOnly) { + return aApt->hasHardRunwayOfLengthFt(_minLengthFt); + } + + return true; + } + + void showAll() + { + _hardRunwaysOnly = false; + } + +private: + bool _heliports; + bool _hardRunwaysOnly; + double _minLengthFt; +}; + +class NavaidFilter : public FGPositioned::Filter +{ +public: + NavaidFilter(bool fixesEnabled, bool navaidsEnabled) : + _fixes(fixesEnabled), + _navaids(navaidsEnabled) + {} + + virtual bool pass(FGPositioned* aPos) const { + if (_fixes && (aPos->type() == FGPositioned::FIX)) { + // ignore fixes which end in digits - expirmental + if (aPos->ident().length() > 4 && isdigit(aPos->ident()[3]) && isdigit(aPos->ident()[4])) { + return false; + } + } + + return true; + } + + virtual FGPositioned::Type minType() const { + return _fixes ? FGPositioned::FIX : FGPositioned::NDB; + } + + virtual FGPositioned::Type maxType() const { + return _navaids ? FGPositioned::VOR : FGPositioned::FIX; + } + +private: + bool _fixes, _navaids; +}; + +} // of anonymous namespace + const int MAX_ZOOM = 12; const int SHOW_DETAIL_ZOOM = 8; const int SHOW_DETAIL2_ZOOM = 5; @@ -544,41 +614,127 @@ void MapWidget::zoomOut() _root->setIntValue("zoom", zoom() - 1); } -void MapWidget::draw(int dx, int dy) +void MapWidget::update() { - _aircraft = globals->get_aircraft_position(); + _aircraft = globals->get_aircraft_position(); - bool mag = _root->getBoolValue("magnetic-headings"); - if (mag != _magneticHeadings) { - clearData(); // flush cached data text, since it often includes heading - _magneticHeadings = mag; - } - - if (_hasPanned) { - _root->setBoolValue("centre-on-aircraft", false); - _hasPanned = false; - } - else if (_root->getBoolValue("centre-on-aircraft")) { - _projectionCenter = _aircraft; - } + bool mag = _root->getBoolValue("magnetic-headings"); + if (mag != _magneticHeadings) { + clearData(); // flush cached data text, since it often includes heading + _magneticHeadings = mag; + } + + if (_hasPanned) { + _root->setBoolValue("centre-on-aircraft", false); + _hasPanned = false; + } + else if (_root->getBoolValue("centre-on-aircraft")) { + _projectionCenter = _aircraft; + } + + double julianDate = globals->get_time_params()->getJD(); + _magVar->update(_projectionCenter, julianDate); + + _aircraftUp = _root->getBoolValue("aircraft-heading-up"); + if (_aircraftUp) { + _upHeading = fgGetDouble("/orientation/heading-deg"); + } else { + _upHeading = 0.0; + } + + if (_magneticHeadings) { + _displayHeading = (int) fgGetDouble("/orientation/heading-magnetic-deg"); + } else { + _displayHeading = (int) _upHeading; + } + + _cachedZoom = MAX_ZOOM - zoom(); + SGGeod topLeft = unproject(SGVec2d(_width/2, _height/2)); + // compute draw range, including a fudge factor for ILSs and other 'long' + // symbols + _drawRangeNm = SGGeodesy::distanceNm(_projectionCenter, topLeft) + 10.0; + + + FGFlightHistory* history = (FGFlightHistory*) globals->get_subsystem("history"); + if (history && _root->getBoolValue("draw-flight-history")) { + _flightHistoryPath = history->pathForHistory(); + } else { + _flightHistoryPath.clear(); + } + +// make spatial queries. This can trigger loading of XML files, etc, so we do +// not want to do it in draw(), which can be called from an arbitrary OSG +// rendering thread. + + MapAirportFilter af(_root); + if (_cachedZoom <= SHOW_DETAIL2_ZOOM) { + // show all airports when zoomed in sufficently + af.showAll(); + } + + bool partial = false; + FGPositionedList newItemsToDraw = + FGPositioned::findWithinRangePartial(_projectionCenter, _drawRangeNm, &af, partial); + + bool fixes = _root->getBoolValue("draw-fixes"); + NavaidFilter f(fixes, _root->getBoolValue("draw-navaids")); + if (f.minType() <= f.maxType()) { + FGPositionedList navs = FGPositioned::findWithinRange(_projectionCenter, _drawRangeNm, &f); + newItemsToDraw.insert(newItemsToDraw.end(), navs.begin(), navs.end()); + } - double julianDate = globals->get_time_params()->getJD(); - _magVar->update(_projectionCenter, julianDate); + FGPositioned::TypeFilter tf(FGPositioned::COUNTRY); + if (_cachedZoom <= SHOW_DETAIL_ZOOM) { + tf.addType(FGPositioned::CITY); + } + + if (_cachedZoom <= SHOW_DETAIL2_ZOOM) { + tf.addType(FGPositioned::TOWN); + } + + FGPositionedList poi = FGPositioned::findWithinRange(_projectionCenter, _drawRangeNm, &tf); + newItemsToDraw.insert(newItemsToDraw.end(), poi.begin(), poi.end()); + + _itemsToDraw.swap(newItemsToDraw); + + updateAIObjects(); +} - bool aircraftUp = _root->getBoolValue("aircraft-heading-up"); - if (aircraftUp) { - _upHeading = fgGetDouble("/orientation/heading-deg"); - } else { - _upHeading = 0.0; - } +void MapWidget::updateAIObjects() +{ + if (!_root->getBoolValue("draw-traffic") || (_cachedZoom > SHOW_DETAIL_ZOOM)) { + _aiDrawVec.clear(); + return; + } + + AIDrawVec newDrawVec; + + const SGPropertyNode* ai = fgGetNode("/ai/models", true); + for (int i = 0; i < ai->nChildren(); ++i) { + const SGPropertyNode *model = ai->getChild(i); + // skip bad or dead entries + if (!model || model->getIntValue("id", -1) == -1) { + continue; + } + + SGGeod pos = SGGeod::fromDegFt( + model->getDoubleValue("position/longitude-deg"), + model->getDoubleValue("position/latitude-deg"), + model->getDoubleValue("position/altitude-ft")); + + double dist = SGGeodesy::distanceNm(_projectionCenter, pos); + if (dist > _drawRangeNm) { + continue; + } + + newDrawVec.push_back(DrawAIObject((SGPropertyNode*) model, pos)); + } // of ai/models iteration - _cachedZoom = MAX_ZOOM - zoom(); - SGGeod topLeft = unproject(SGVec2d(_width/2, _height/2)); - // compute draw range, including a fudge factor for ILSs and other 'long' - // symbols - _drawRangeNm = SGGeodesy::distanceNm(_projectionCenter, topLeft) + 10.0; + _aiDrawVec.swap(newDrawVec); +} -// drawing operations +void MapWidget::draw(int dx, int dy) +{ GLint sx = (int) abox.min[0], sy = (int) abox.min[1]; glScissor(dx + sx, dy + sy, _width, _height); @@ -592,7 +748,7 @@ void MapWidget::draw(int dx, int dy) drawLatLonGrid(); - if (aircraftUp) { + if (_aircraftUp) { int textHeight = legendFont.getStringHeight() + 5; // draw heading line @@ -600,23 +756,14 @@ void MapWidget::draw(int dx, int dy) glColor3f(1.0, 1.0, 1.0); drawLine(loc, SGVec2d(loc.x(), (_height / 2) - textHeight)); - int displayHdg; - if (_magneticHeadings) { - displayHdg = (int) fgGetDouble("/orientation/heading-magnetic-deg"); - } else { - displayHdg = (int) _upHeading; - } - double y = (_height / 2) - textHeight; char buf[16]; - ::snprintf(buf, 16, "%d", displayHdg); + ::snprintf(buf, 16, "%d", _displayHeading); int sw = legendFont.getStringWidth(buf); legendFont.drawString(buf, loc.x() - sw/2, y); } - drawAirports(); - drawNavaids(); - drawPOIs(); + drawPositioned(); drawTraffic(); drawGPSData(); drawNavRadio(fgGetNode("/instrumentation/nav[0]", false)); @@ -657,8 +804,6 @@ void MapWidget::paintRuler() d->setAnchor(clickPos); d->setOffset(MapData::VALIGN_TOP | MapData::HALIGN_CENTER, 15); d->setPriority(20000); - - } void MapWidget::paintAircraftLocation(const SGGeod& aircraftPos) @@ -762,20 +907,17 @@ void MapWidget::paintRoute() void MapWidget::drawFlightHistory() { - FGFlightHistory* history = (FGFlightHistory*) globals->get_subsystem("history"); - if (!history || !_root->getBoolValue("draw-flight-history")) { + if (_flightHistoryPath.empty()) return; - } - + // first pass, draw the actual lines glLineWidth(2.0); - SGGeodVec gv(history->pathForHistory()); glColor4f(0.0, 0.0, 1.0, 0.7); glBegin(GL_LINE_STRIP); - for (unsigned int i=0; igetBoolValue("show-heliports", false); - _hardRunwaysOnly = nd->getBoolValue("hard-surfaced-airports", true); - _minLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft", 2000); - } - - virtual FGPositioned::Type maxType() const { - return _heliports ? FGPositioned::HELIPORT : FGPositioned::AIRPORT; - } - - virtual bool passAirport(FGAirport* aApt) const { - if (_hardRunwaysOnly) { - return aApt->hasHardRunwayOfLengthFt(_minLengthFt); - } - - return true; - } - - void showAll() - { - _hardRunwaysOnly = false; - } - -private: - bool _heliports; - bool _hardRunwaysOnly; - double _minLengthFt; -}; - -void MapWidget::drawAirports() -{ - MapAirportFilter af(_root); - if (_cachedZoom <= SHOW_DETAIL2_ZOOM) { - // show all airports when zoomed in sufficently - af.showAll(); - } - - bool partial = false; - FGPositionedList apts = FGPositioned::findWithinRangePartial(_projectionCenter, _drawRangeNm, &af, partial); - for (unsigned int i=0; itype() == FGPositioned::FIX)) { - // ignore fixes which end in digits - expirmental - if (aPos->ident().length() > 4 && isdigit(aPos->ident()[3]) && isdigit(aPos->ident()[4])) { - return false; - } - } - - return true; - } - - virtual FGPositioned::Type minType() const { - return _fixes ? FGPositioned::FIX : FGPositioned::NDB; - } - - virtual FGPositioned::Type maxType() const { - return _navaids ? FGPositioned::VOR : FGPositioned::FIX; - } - -private: - bool _fixes, _navaids; -}; - -void MapWidget::drawNavaids() -{ - bool fixes = _root->getBoolValue("draw-fixes"); - NavaidFilter f(fixes, _root->getBoolValue("draw-navaids")); - - if (f.minType() <= f.maxType()) { - FGPositionedList navs = FGPositioned::findWithinRange(_projectionCenter, _drawRangeNm, &f); - - glLineWidth(1.0); - for (unsigned int i=0; itype(); - if (ty == FGPositioned::NDB) { - drawNDB(false, (FGNavRecord*) navs[i].get()); - } else if (ty == FGPositioned::VOR) { - drawVOR(false, (FGNavRecord*) navs[i].get()); - } else if (ty == FGPositioned::FIX) { - drawFix((FGFix*) navs[i].get()); - } - } // of navaid iteration - } // of navaids || fixes are drawn test -} - -void MapWidget::drawPOIs() +void MapWidget::drawPositioned() { - FGPositioned::TypeFilter f(FGPositioned::COUNTRY); - f.addType(FGPositioned::CITY); - f.addType(FGPositioned::TOWN); - FGPositionedList poi = FGPositioned::findWithinRange(_projectionCenter, _drawRangeNm, &f); - - glLineWidth(1.0); - for (unsigned int i=0; itype(); - if (ty == FGPositioned::COUNTRY) { - drawCountries((FGNavRecord*) poi[i].get()); - } else if (ty == FGPositioned::CITY) { - drawCities((FGNavRecord*) poi[i].get()); - } else if (ty == FGPositioned::TOWN) { - drawTowns((FGNavRecord*) poi[i].get()); - } - } // of navaid iteration + for (unsigned int i=0; i<_itemsToDraw.size(); ++i) { + FGPositionedRef p = _itemsToDraw[i]; + switch (p->type()) { + case FGPositioned::AIRPORT: + drawAirport((FGAirport*) p.get()); + break; + case FGPositioned::NDB: + drawNDB(false, (FGNavRecord*) p.get()); + break; + case FGPositioned::VOR: + drawVOR(false, (FGNavRecord*) p.get()); + break; + case FGPositioned::FIX: + drawFix((FGFix*) p.get()); + break; + case FGPositioned::TOWN: + case FGPositioned::CITY: + case FGPositioned::COUNTRY: + drawPOI(p); + break; + + default: + SG_LOG(SG_GENERAL, SG_WARN, "unhandled type in MapWidget::drawPositioned"); + } // of positioned type switch + } // of items to draw iteration } void MapWidget::drawNDB(bool tuned, FGNavRecord* ndb) @@ -1193,91 +1243,38 @@ void MapWidget::drawTunedLocalizer(SGPropertyNode_ptr radio) } } -void MapWidget::drawCountries(FGNavRecord* rec) +void MapWidget::drawPOI(FGPositioned* poi) { - if (_cachedZoom < 9) { - return; // hide labels beyond a certain zoom level - } - - SGVec2d pos = project(rec->geod()); + SGVec2d pos = project(poi->geod()); glColor3f(1.0, 1.0, 0.0); + glLineWidth(1.0); - circleAt(pos, 4, 10); + int radius = 10; + if (poi->type() == FGPositioned::CITY) { + radius = 8; + glColor3f(0.0, 1.0, 0.0); + } else if (poi->type() == FGPositioned::TOWN) { + radius = 5; + glColor3f(0.2, 1.0, 0.0); + } + + circleAt(pos, 4, radius); - if (validDataForKey(rec)) { - setAnchorForKey(rec, pos); + if (validDataForKey(poi)) { + setAnchorForKey(poi, pos); return; } char buffer[1024]; ::snprintf(buffer, 1024, "%s", - rec->name().c_str()); + poi->name().c_str()); - MapData* d = createDataForKey(rec); + MapData* d = createDataForKey(poi); d->setPriority(200); - d->setLabel(rec->ident()); - d->setText(buffer); - d->setOffset(MapData::HALIGN_CENTER | MapData::VALIGN_BOTTOM, 10); - d->setAnchor(pos); - -} - -void MapWidget::drawCities(FGNavRecord* rec) -{ - if (_cachedZoom > SHOW_DETAIL_ZOOM) { - return; // hide labels beyond a certain zoom level - } - - SGVec2d pos = project(rec->geod()); - glColor3f(0.0, 1.0, 0.0); - - circleAt(pos, 4, 8); - - if (validDataForKey(rec)) { - setAnchorForKey(rec, pos); - return; - } - - char buffer[1024]; - ::snprintf(buffer, 1024, "%s", - rec->name().c_str()); - - MapData* d = createDataForKey(rec); - d->setPriority(40); - d->setLabel(rec->ident()); - d->setText(buffer); - d->setOffset(MapData::HALIGN_CENTER | MapData::VALIGN_BOTTOM, 10); - d->setAnchor(pos); - -} - -void MapWidget::drawTowns(FGNavRecord* rec) -{ - if (_cachedZoom > SHOW_DETAIL2_ZOOM) { - return; // hide labels beyond a certain zoom level - } - - SGVec2d pos = project(rec->geod()); - glColor3f(0.2, 1.0, 0.0); - - circleAt(pos, 4, 5); - - if (validDataForKey(rec)) { - setAnchorForKey(rec, pos); - return; - } - - char buffer[1024]; - ::snprintf(buffer, 1024, "%s", - rec->name().c_str()); - - MapData* d = createDataForKey(rec); - d->setPriority(40); - d->setLabel(rec->ident()); + d->setLabel(poi->ident()); d->setText(buffer); d->setOffset(MapData::HALIGN_CENTER | MapData::VALIGN_BOTTOM, 10); d->setAnchor(pos); - } /* @@ -1491,42 +1488,10 @@ void MapWidget::drawILS(bool tuned, FGRunway* rwy) void MapWidget::drawTraffic() { - if (!_root->getBoolValue("draw-traffic")) { - return; - } - - if (_cachedZoom > SHOW_DETAIL_ZOOM) { - return; - } - - const SGPropertyNode* ai = fgGetNode("/ai/models", true); - - for (int i = 0; i < ai->nChildren(); ++i) { - const SGPropertyNode *model = ai->getChild(i); - // skip bad or dead entries - if (!model || model->getIntValue("id", -1) == -1) { - continue; - } - - const std::string& name(model->getName()); - SGGeod pos = SGGeod::fromDegFt( - model->getDoubleValue("position/longitude-deg"), - model->getDoubleValue("position/latitude-deg"), - model->getDoubleValue("position/altitude-ft")); - - double dist = SGGeodesy::distanceNm(_projectionCenter, pos); - if (dist > _drawRangeNm) { - continue; - } - - double heading = model->getDoubleValue("orientation/true-heading-deg"); - if ((name == "aircraft") || (name == "multiplayer") || - (name == "wingman") || (name == "tanker")) { - drawAIAircraft(model, pos, heading); - } else if ((name == "ship") || (name == "carrier") || (name == "escort")) { - drawAIShip(model, pos, heading); + AIDrawVec::const_iterator it; + for (it = _aiDrawVec.begin(); it != _aiDrawVec.end(); ++it) { + drawAI(*it); } - } // of ai/models iteration } void MapWidget::drawHelipad(FGHelipad* hp) @@ -1555,105 +1520,36 @@ void MapWidget::drawHelipad(FGHelipad* hp) d->setAnchor(pos); } -void MapWidget::drawAIAircraft(const SGPropertyNode* model, const SGGeod& pos, double hdg) +void MapWidget::drawAI(const DrawAIObject& dai) { + SGVec2d p = project(dai.pos); - SGVec2d p = project(pos); + if (dai.boat) { + glColor3f(0.0, 0.0, 0.5); - glColor3f(0.0, 0.0, 0.0); + } else { + glColor3f(0.0, 0.0, 0.0); + } glLineWidth(2.0); circleAt(p, 4, 6.0); // black diamond // draw heading vector - int speedKts = static_cast(model->getDoubleValue("velocities/true-airspeed-kt")); - if (speedKts > 1) { + if (dai.speedKts > 1) { glLineWidth(1.0); - const double dt = 15.0 / (3600.0); // 15 seconds look-ahead - double distanceM = speedKts * SG_NM_TO_METER * dt; - - SGGeod advance; - double az2; - SGGeodesy::direct(pos, hdg, distanceM, advance, az2); - + double distanceM = dai.speedKts * SG_NM_TO_METER * dt; + SGGeod advance = SGGeodesy::direct(dai.pos, dai.heading, distanceM); drawLine(p, project(advance)); } - // try to access the flight-plan of the aircraft. There are several layers - // of potential NULL-ness here, so we have to be defensive at each stage. - std::string originICAO, destinationICAO; - FGAIManager* aiManager = static_cast(globals->get_subsystem("ai-model")); - FGAIBasePtr aircraft = aiManager ? aiManager->getObjectFromProperty(model) : NULL; - if (aircraft) { - FGAIAircraft* p = static_cast(aircraft.get()); - if (p->GetFlightPlan()) { - if (p->GetFlightPlan()->departureAirport()) { - originICAO = p->GetFlightPlan()->departureAirport()->ident(); - } - - if (p->GetFlightPlan()->arrivalAirport()) { - destinationICAO = p->GetFlightPlan()->arrivalAirport()->ident(); - } - } // of flight-plan exists - } // of check for AIBase-derived instance - - // draw callsign / altitude / speed - int altFt50 = static_cast(pos.getElevationFt() / 50.0) * 50; - std::ostringstream ss; - ss << model->getStringValue("callsign", "<>"); - if (speedKts > 1) { - ss << "\n" << altFt50 << "' " << speedKts << "kts"; - } - - if (!originICAO.empty() || ! destinationICAO.empty()) { - ss << "\n" << originICAO << " -> " << destinationICAO; - } - - MapData* d = getOrCreateDataForKey((void*) model); - d->setText(ss.str().c_str()); - d->setLabel(model->getStringValue("callsign", "<>")); - d->setPriority(speedKts > 5 ? 60 : 10); // low priority for parked aircraft + MapData* d = getOrCreateDataForKey((void*) dai.model); + d->setText(dai.legend); + d->setLabel(dai.label); + d->setPriority(dai.speedKts > 5 ? 60 : 10); // low priority for parked aircraft d->setOffset(MapData::VALIGN_CENTER | MapData::HALIGN_LEFT, 10); d->setAnchor(p); } -void MapWidget::drawAIShip(const SGPropertyNode* model, const SGGeod& pos, double hdg) -{ - SGVec2d p = project(pos); - - glColor3f(0.0, 0.0, 0.5); - glLineWidth(2.0); - circleAt(p, 4, 6.0); // blue diamond (to differentiate from aircraft. - -// draw heading vector - int speedKts = static_cast(model->getDoubleValue("velocities/speed-kts")); - if (speedKts > 1) { - glLineWidth(1.0); - - const double dt = 15.0 / (3600.0); // 15 seconds look-ahead - double distanceM = speedKts * SG_NM_TO_METER * dt; - - SGGeod advance; - double az2; - SGGeodesy::direct(pos, hdg, distanceM, advance, az2); - - drawLine(p, project(advance)); - } - - // draw callsign / speed - char buffer[1024]; - ::snprintf(buffer, 1024, "%s\n%dkts", - model->getStringValue("name", "<>"), - speedKts); - - MapData* d = getOrCreateDataForKey((void*) model); - d->setText(buffer); - d->setLabel(model->getStringValue("name", "<>")); - d->setPriority(speedKts > 2 ? 30 : 10); // low priority for slow moving ships - d->setOffset(MapData::VALIGN_CENTER | MapData::HALIGN_LEFT, 10); - d->setAnchor(p); -} - SGVec2d MapWidget::project(const SGGeod& geod) const { SGVec2d p; @@ -1979,3 +1875,63 @@ int MapWidget::displayHeading(double h) const SG_NORMALIZE_RANGE(h, 0.0, 360.0); return SGMiscd::roundToInt(h); } + +MapWidget::DrawAIObject::DrawAIObject(SGPropertyNode* m, const SGGeod& g) : + model(m), + boat(false), + pos(g), + speedKts(0) +{ + std::string name(model->getNameString()); + heading = model->getDoubleValue("orientation/true-heading-deg"); + + if ((name == "aircraft") || (name == "multiplayer") || + (name == "wingman") || (name == "tanker")) + { + speedKts = static_cast(model->getDoubleValue("velocities/true-airspeed-kt")); + label = model->getStringValue("callsign", "<>"); + + // try to access the flight-plan of the aircraft. There are several layers + // of potential NULL-ness here, so we have to be defensive at each stage. + std::string originICAO, destinationICAO; + FGAIManager* aiManager = static_cast(globals->get_subsystem("ai-model")); + FGAIBasePtr aircraft = aiManager ? aiManager->getObjectFromProperty(model) : NULL; + if (aircraft) { + FGAIAircraft* p = static_cast(aircraft.get()); + if (p->GetFlightPlan()) { + if (p->GetFlightPlan()->departureAirport()) { + originICAO = p->GetFlightPlan()->departureAirport()->ident(); + } + + if (p->GetFlightPlan()->arrivalAirport()) { + destinationICAO = p->GetFlightPlan()->arrivalAirport()->ident(); + } + } // of flight-plan exists + } // of check for AIBase-derived instance + + // draw callsign / altitude / speed + int altFt50 = static_cast(pos.getElevationFt() / 50.0) * 50; + std::ostringstream ss; + ss << model->getStringValue("callsign", "<>"); + if (speedKts > 1) { + ss << "\n" << altFt50 << "' " << speedKts << "kts"; + } + + if (!originICAO.empty() || ! destinationICAO.empty()) { + ss << "\n" << originICAO << " -> " << destinationICAO; + } + + legend = ss.str(); + } else if ((name == "ship") || (name == "carrier") || (name == "escort")) { + boat = true; + speedKts = static_cast(model->getDoubleValue("velocities/speed-kts")); + label = model->getStringValue("name", "<>"); + + char buffer[1024]; + ::snprintf(buffer, 1024, "%s\n%dkts", + model->getStringValue("name", "<>"), + speedKts); + legend = buffer; + } +} + diff --git a/src/GUI/MapWidget.hxx b/src/GUI/MapWidget.hxx index 0a826f761..1ad5aa15a 100644 --- a/src/GUI/MapWidget.hxx +++ b/src/GUI/MapWidget.hxx @@ -6,9 +6,10 @@ #include #include +#include #include -#include "dialog.hxx" // for GUI_ID +#include "FGPUIDialog.hxx" // forward decls class FGRouteMgr; @@ -20,18 +21,25 @@ class FGFix; class MapData; class SGMagVar; -class MapWidget : public puObject +typedef std::vector SGGeodVec; + +class MapWidget : public puObject, public FGPUIDialog::ActiveWidget { public: MapWidget(int x, int y, int width, int height); virtual ~MapWidget(); + // puObject over-rides virtual void setSize(int width, int height); virtual void doHit( int button, int updown, int x, int y ) ; virtual void draw( int dx, int dy ) ; virtual int checkKey(int key, int updown); void setProperty(SGPropertyNode_ptr prop); + + // PUIDialog::ActiveWidget over-rides + virtual void update(); + private: enum Projection { PROJECTION_SAMSON_FLAMSTEED, @@ -60,7 +68,6 @@ private: SGVec2d gridPoint(int ix, int iy); bool drawLineClipped(const SGVec2d& a, const SGVec2d& b); - void drawAirports(); void drawAirport(FGAirport* apt); int scoreAirportRunways(FGAirport* apt); void drawRunwayPre(FGRunway* rwy); @@ -68,19 +75,34 @@ private: void drawHelipad(FGHelipad* hp); void drawILS(bool tuned, FGRunway* rwy); - void drawNavaids(); - void drawPOIs(); + void drawPositioned(); void drawNDB(bool tuned, FGNavRecord* nav); void drawVOR(bool tuned, FGNavRecord* nav); void drawFix(FGFix* fix); - void drawCountries(FGNavRecord* rec); - void drawCities(FGNavRecord* rec); - void drawTowns(FGNavRecord* rec); - + void drawPOI(FGPositioned* rec); + void drawTraffic(); - void drawAIAircraft(const SGPropertyNode* model, const SGGeod& pos, double hdg); - void drawAIShip(const SGPropertyNode* model, const SGGeod& pos, double hdg); + + class DrawAIObject + { + public: + DrawAIObject(SGPropertyNode* model, const SGGeod& g); + + SGPropertyNode* model; + bool boat; + SGGeod pos; + double heading; + int speedKts; + std::string label; + std::string legend; + }; + + typedef std::vector AIDrawVec; + AIDrawVec _aiDrawVec; + + void updateAIObjects(); + void drawAI(const DrawAIObject& dai); void drawData(); bool validDataForKey(void* key); @@ -107,7 +129,9 @@ private: double _upHeading; // true heading corresponding to +ve y-axis bool _magneticHeadings; bool _hasPanned; - + bool _aircraftUp; + int _displayHeading; + SGGeod _projectionCenter; Projection _projection; SGGeod _aircraft; @@ -116,7 +140,10 @@ private: FGRouteMgr* _route; SGPropertyNode_ptr _root; SGPropertyNode_ptr _gps; - + SGGeodVec _flightHistoryPath; + + FGPositionedList _itemsToDraw; + typedef std::map KeyDataMap; KeyDataMap _mapData; std::vector _dataQueue;