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)
This commit is contained in:
parent
976f3115a9
commit
1b9394fc37
3 changed files with 359 additions and 375 deletions
|
@ -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,
|
||||
|
|
|
@ -69,7 +69,7 @@ static bool puBoxIntersect(const puBox& a, const puBox& b)
|
|||
|
||||
return (x0 <= x1) && (y0 <= y1);
|
||||
}
|
||||
|
||||
|
||||
class MapData;
|
||||
typedef std::vector<MapData*> 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::update()
|
||||
{
|
||||
_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;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
_aiDrawVec.swap(newDrawVec);
|
||||
}
|
||||
|
||||
void MapWidget::draw(int dx, int dy)
|
||||
{
|
||||
_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;
|
||||
}
|
||||
|
||||
double julianDate = globals->get_time_params()->getJD();
|
||||
_magVar->update(_projectionCenter, julianDate);
|
||||
|
||||
bool aircraftUp = _root->getBoolValue("aircraft-heading-up");
|
||||
if (aircraftUp) {
|
||||
_upHeading = fgGetDouble("/orientation/heading-deg");
|
||||
} else {
|
||||
_upHeading = 0.0;
|
||||
}
|
||||
|
||||
_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;
|
||||
|
||||
// drawing operations
|
||||
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; i<gv.size(); ++i) {
|
||||
SGVec2d p = project(gv[i]);
|
||||
for (unsigned int i=0; i<_flightHistoryPath.size(); ++i) {
|
||||
SGVec2d p = project(_flightHistoryPath[i]);
|
||||
glVertex2d(p.x(), p.y());
|
||||
}
|
||||
|
||||
|
@ -914,125 +1056,33 @@ void MapWidget::drawGPSData()
|
|||
}
|
||||
}
|
||||
|
||||
class MapAirportFilter : public FGAirport::AirportFilter
|
||||
void MapWidget::drawPositioned()
|
||||
{
|
||||
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;
|
||||
};
|
||||
|
||||
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; i<apts.size(); ++i) {
|
||||
drawAirport((FGAirport*) apts[i].get());
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
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; i<navs.size(); ++i) {
|
||||
FGPositioned::Type ty = navs[i]->type();
|
||||
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()
|
||||
{
|
||||
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; i<poi.size(); ++i) {
|
||||
FGPositioned::Type ty = poi[i]->type();
|
||||
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->setLabel(poi->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->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;
|
||||
AIDrawVec::const_iterator it;
|
||||
for (it = _aiDrawVec.begin(); it != _aiDrawVec.end(); ++it) {
|
||||
drawAI(*it);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
} // 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<int>(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<FGAIManager*>(globals->get_subsystem("ai-model"));
|
||||
FGAIBasePtr aircraft = aiManager ? aiManager->getObjectFromProperty(model) : NULL;
|
||||
if (aircraft) {
|
||||
FGAIAircraft* p = static_cast<FGAIAircraft*>(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<int>(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<int>(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<int>(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<FGAIManager*>(globals->get_subsystem("ai-model"));
|
||||
FGAIBasePtr aircraft = aiManager ? aiManager->getObjectFromProperty(model) : NULL;
|
||||
if (aircraft) {
|
||||
FGAIAircraft* p = static_cast<FGAIAircraft*>(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<int>(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<int>(model->getDoubleValue("velocities/speed-kts"));
|
||||
label = model->getStringValue("name", "<>");
|
||||
|
||||
char buffer[1024];
|
||||
::snprintf(buffer, 1024, "%s\n%dkts",
|
||||
model->getStringValue("name", "<>"),
|
||||
speedKts);
|
||||
legend = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
#include <simgear/math/SGMath.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
#include <Navaids/positioned.hxx>
|
||||
#include <plib/pu.h>
|
||||
|
||||
#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<SGGeod> 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<DrawAIObject> 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<void*, MapData*> KeyDataMap;
|
||||
KeyDataMap _mapData;
|
||||
std::vector<MapData*> _dataQueue;
|
||||
|
|
Loading…
Add table
Reference in a new issue