Added solar system browser to Qt4 front-end.
parent
7a5c9ed95c
commit
1fd6c7c76f
|
@ -23,6 +23,7 @@
|
|||
#include "qtappwin.h"
|
||||
#include "qtglwidget.h"
|
||||
#include "qtpreferencesdialog.h"
|
||||
#include "qtsolarsystembrowser.h"
|
||||
#include "qtcelestialbrowser.h"
|
||||
#include "qtdeepskybrowser.h"
|
||||
#include "qtselectionpopup.h"
|
||||
|
@ -39,7 +40,7 @@ const QPoint DEFAULT_MAIN_WINDOW_POSITION(200, 200);
|
|||
|
||||
// Used when saving and restoring main window state; increment whenever
|
||||
// new dockables or toolbars are added.
|
||||
static const int CELESTIA_MAIN_WINDOW_VERSION = 2;
|
||||
static const int CELESTIA_MAIN_WINDOW_VERSION = 3;
|
||||
|
||||
|
||||
// Terrible hack required because context menu callback doesn't retain
|
||||
|
@ -105,6 +106,10 @@ CelestiaAppWindow::CelestiaAppWindow(const QString& qConfigFileName,
|
|||
QWidget* deepSkyBrowser = new DeepSkyBrowser(appCore, NULL);
|
||||
deepSkyBrowser->setObjectName("deepsky-browser");
|
||||
|
||||
SolarSystemBrowser* solarSystemBrowser = new SolarSystemBrowser(appCore, NULL);
|
||||
solarSystemBrowser->setObjectName("ssys-browser");
|
||||
|
||||
tabWidget->addTab(solarSystemBrowser, tr("Solar System"));
|
||||
tabWidget->addTab(celestialBrowser, tr("Stars"));
|
||||
tabWidget->addTab(deepSkyBrowser, tr("Deep Sky Objects"));
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QCheckBox>
|
||||
#include <QLabel>
|
||||
#include <celestia/celestiacore.h>
|
||||
#include <vector>
|
||||
|
@ -50,7 +51,7 @@ public:
|
|||
TypeColumn = 1,
|
||||
};
|
||||
|
||||
void buildModel(Star& star);
|
||||
void buildModel(Star* star, bool _groupByClass);
|
||||
|
||||
private:
|
||||
class TreeItem
|
||||
|
@ -64,20 +65,36 @@ private:
|
|||
TreeItem** children;
|
||||
int nChildren;
|
||||
int childIndex;
|
||||
int classification;
|
||||
};
|
||||
|
||||
TreeItem* createTreeItem(Selection sel, TreeItem* parent, int childIndex);
|
||||
void addTreeItemChildren(TreeItem* item,
|
||||
PlanetarySystem* sys,
|
||||
const vector<Star*>* orbitingStars);
|
||||
void addTreeItemChildrenGrouped(TreeItem* item,
|
||||
PlanetarySystem* sys,
|
||||
const vector<Star*>* orbitingStars,
|
||||
Selection parent);
|
||||
TreeItem* createGroupTreeItem(int classification,
|
||||
const vector<Body*>& objects,
|
||||
TreeItem* parent,
|
||||
int childIndex);
|
||||
|
||||
TreeItem* itemAtIndex(const QModelIndex& index) const;
|
||||
|
||||
private:
|
||||
const Universe* universe;
|
||||
TreeItem* rootItem;
|
||||
bool groupByClass;
|
||||
};
|
||||
|
||||
|
||||
SolarSystemTreeModel::TreeItem::TreeItem() :
|
||||
parent(NULL),
|
||||
children(NULL),
|
||||
nChildren(0)
|
||||
nChildren(0),
|
||||
classification(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -95,8 +112,11 @@ SolarSystemTreeModel::TreeItem::~TreeItem()
|
|||
|
||||
SolarSystemTreeModel::SolarSystemTreeModel(const Universe* _universe) :
|
||||
universe(_universe),
|
||||
rootItem(NULL)
|
||||
rootItem(NULL),
|
||||
groupByClass(false)
|
||||
{
|
||||
// Initialize an empty model
|
||||
buildModel(NULL, false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -105,6 +125,28 @@ SolarSystemTreeModel::~SolarSystemTreeModel()
|
|||
}
|
||||
|
||||
|
||||
// Call createTreeItem() to build the parallel tree structure
|
||||
void SolarSystemTreeModel::buildModel(Star* star, bool _groupByClass)
|
||||
{
|
||||
groupByClass = _groupByClass;
|
||||
|
||||
if (rootItem != NULL)
|
||||
delete rootItem;
|
||||
|
||||
rootItem = new TreeItem();
|
||||
rootItem->obj = Selection();
|
||||
|
||||
if (star != NULL)
|
||||
{
|
||||
rootItem->nChildren = 1;
|
||||
rootItem->children = new TreeItem*[1];
|
||||
rootItem->children[0] = createTreeItem(Selection(star), rootItem, 0);
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
|
||||
// Rather than directly use Celestia's solar system data structure for
|
||||
// the tree model, we'll build a parallel structure out of TreeItems.
|
||||
// The additional memory used for this structure is negligible, and
|
||||
|
@ -144,6 +186,20 @@ SolarSystemTreeModel::createTreeItem(Selection sel,
|
|||
orbitingStars = sel.star()->getOrbitingStars();
|
||||
}
|
||||
|
||||
if (groupByClass && sys != NULL)
|
||||
addTreeItemChildrenGrouped(item, sys, orbitingStars, sel);
|
||||
else
|
||||
addTreeItemChildren(item, sys, orbitingStars);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
SolarSystemTreeModel::addTreeItemChildren(TreeItem* item,
|
||||
PlanetarySystem* sys,
|
||||
const vector<Star*>* orbitingStars)
|
||||
{
|
||||
// Calculate the number of children: the number of orbiting stars plus
|
||||
// the number of orbiting solar system bodies.
|
||||
item->nChildren = 0;
|
||||
|
@ -179,34 +235,193 @@ SolarSystemTreeModel::createTreeItem(Selection sel,
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add children to item, but group objects of certain classes
|
||||
// into subtrees to avoid clutter. Stars, planets, and moons
|
||||
// are shown as direct children of the parent. Small moons,
|
||||
// asteroids, and spacecraft a grouped together, as there tend to be
|
||||
// large collections of such objects.
|
||||
void
|
||||
SolarSystemTreeModel::addTreeItemChildrenGrouped(TreeItem* item,
|
||||
PlanetarySystem* sys,
|
||||
const vector<Star*>* orbitingStars,
|
||||
Selection parent)
|
||||
{
|
||||
vector<Body*> asteroids;
|
||||
vector<Body*> spacecraft;
|
||||
vector<Body*> minorMoons;
|
||||
vector<Body*> other;
|
||||
vector<Body*> normal;
|
||||
|
||||
float minorMoonCutoffRadius = 0.0f;
|
||||
bool groupAsteroids = true;
|
||||
bool groupSpacecraft = true;
|
||||
if (parent.body())
|
||||
{
|
||||
// TODO: we should define a class for minor moons. Until then,
|
||||
// we'll call a moon 'minor' if its radius is less than 1/1000
|
||||
// times the radius of the planet it orbits.
|
||||
minorMoonCutoffRadius = parent.body()->getRadius() / 1000.0f;
|
||||
|
||||
// Don't put asteroid moons in the asteroid group; make them
|
||||
// immediate children of the parent.
|
||||
if (parent.body()->getClassification() == Body::Asteroid)
|
||||
groupAsteroids = false;
|
||||
|
||||
if (parent.body()->getClassification() == Body::Spacecraft)
|
||||
groupSpacecraft = false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sys->getSystemSize(); i++)
|
||||
{
|
||||
Body* body = sys->getBody(i);
|
||||
switch (body->getClassification())
|
||||
{
|
||||
case Body::Planet:
|
||||
case Body::Invisible:
|
||||
normal.push_back(body);
|
||||
break;
|
||||
case Body::Moon:
|
||||
if (body->getRadius() < minorMoonCutoffRadius)
|
||||
minorMoons.push_back(body);
|
||||
else
|
||||
normal.push_back(body);
|
||||
break;
|
||||
case Body::Asteroid:
|
||||
case Body::Comet:
|
||||
if (groupAsteroids)
|
||||
asteroids.push_back(body);
|
||||
else
|
||||
normal.push_back(body);
|
||||
break;
|
||||
case Body::Spacecraft:
|
||||
if (groupSpacecraft)
|
||||
spacecraft.push_back(body);
|
||||
else
|
||||
normal.push_back(body);
|
||||
break;
|
||||
default:
|
||||
other.push_back(body);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the total number of children
|
||||
item->nChildren = 0;
|
||||
if (orbitingStars != NULL)
|
||||
item->nChildren += orbitingStars->size();
|
||||
|
||||
item->nChildren += normal.size();
|
||||
if (!asteroids.empty())
|
||||
item->nChildren++;
|
||||
if (!spacecraft.empty())
|
||||
item->nChildren++;
|
||||
if (!minorMoons.empty())
|
||||
item->nChildren++;
|
||||
if (!other.empty())
|
||||
item->nChildren++;
|
||||
|
||||
if (item->nChildren != 0)
|
||||
{
|
||||
int childIndex = 0;
|
||||
item->children = new TreeItem*[item->nChildren];
|
||||
{
|
||||
// Add the stars
|
||||
if (orbitingStars != NULL)
|
||||
{
|
||||
for (unsigned int i = 0; i < orbitingStars->size(); i++)
|
||||
{
|
||||
Selection child(orbitingStars->at(i));
|
||||
item->children[i] = createTreeItem(child, item, childIndex);
|
||||
childIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the direct children
|
||||
for (int i = 0; i < (int) normal.size(); i++)
|
||||
{
|
||||
item->children[childIndex] = createTreeItem(normal[i], item, childIndex);
|
||||
childIndex++;
|
||||
}
|
||||
|
||||
// Add the groups
|
||||
if (!minorMoons.empty())
|
||||
{
|
||||
item->children[childIndex] = createGroupTreeItem(Body::SmallBody,
|
||||
minorMoons,
|
||||
item, childIndex);
|
||||
childIndex++;
|
||||
}
|
||||
|
||||
if (!asteroids.empty())
|
||||
{
|
||||
item->children[childIndex] = createGroupTreeItem(Body::Asteroid,
|
||||
asteroids,
|
||||
item, childIndex);
|
||||
childIndex++;
|
||||
}
|
||||
|
||||
if (!spacecraft.empty())
|
||||
{
|
||||
item->children[childIndex] = createGroupTreeItem(Body::Spacecraft,
|
||||
spacecraft,
|
||||
item, childIndex);
|
||||
childIndex++;
|
||||
}
|
||||
|
||||
if (!other.empty())
|
||||
{
|
||||
item->children[childIndex] = createGroupTreeItem(Body::Unknown,
|
||||
other,
|
||||
item, childIndex);
|
||||
childIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SolarSystemTreeModel::TreeItem*
|
||||
SolarSystemTreeModel::createGroupTreeItem(int classification,
|
||||
const vector<Body*>& objects,
|
||||
TreeItem* parent,
|
||||
int childIndex)
|
||||
{
|
||||
TreeItem* item = new TreeItem();
|
||||
item->parent = parent;
|
||||
item->childIndex = childIndex;
|
||||
item->classification = classification;
|
||||
|
||||
if (!objects.empty())
|
||||
{
|
||||
item->nChildren = (int) objects.size();
|
||||
item->children = new TreeItem*[item->nChildren];
|
||||
for (int i = 0; i < item->nChildren; i++)
|
||||
{
|
||||
item->children[i] = createTreeItem(Selection(objects[i]), item, i);
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
// Call createTreeItem() to build the parallel tree structure
|
||||
void SolarSystemTreeModel::buildModel(Star& star)
|
||||
SolarSystemTreeModel::TreeItem*
|
||||
SolarSystemTreeModel::itemAtIndex(const QModelIndex& index) const
|
||||
{
|
||||
if (rootItem != NULL)
|
||||
delete rootItem;
|
||||
|
||||
rootItem = new TreeItem();
|
||||
rootItem->nChildren = 1;
|
||||
rootItem->children = new TreeItem*[1];
|
||||
rootItem->obj = Selection();
|
||||
|
||||
rootItem->children[0] = createTreeItem(Selection(&star), rootItem, 0);
|
||||
|
||||
reset();
|
||||
if (!index.isValid())
|
||||
return rootItem;
|
||||
else
|
||||
return static_cast<TreeItem*>(index.internalPointer());
|
||||
}
|
||||
|
||||
|
||||
Selection SolarSystemTreeModel::objectAtIndex(const QModelIndex& index) const
|
||||
Selection
|
||||
SolarSystemTreeModel::objectAtIndex(const QModelIndex& index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return rootItem->obj;
|
||||
else
|
||||
return static_cast<TreeItem*>(index.internalPointer())->obj;
|
||||
return itemAtIndex(index)->obj;
|
||||
}
|
||||
|
||||
|
||||
|
@ -284,14 +499,46 @@ static QString objectTypeName(const Selection& sel)
|
|||
}
|
||||
|
||||
|
||||
static QString classificationName(int classification)
|
||||
{
|
||||
if (classification == Body::Planet)
|
||||
return QObject::tr("Planets");
|
||||
else if (classification == Body::Moon)
|
||||
return QObject::tr("Moons");
|
||||
else if (classification == Body::Spacecraft)
|
||||
return QObject::tr("Spacecraft");
|
||||
else if (classification == Body::Asteroid)
|
||||
return QObject::tr("Asteroids & comets");
|
||||
else if (classification == Body::Invisible)
|
||||
return QObject::tr("Reference points");
|
||||
else if (classification == Body::SmallBody) // TODO: should have a separate
|
||||
return QObject::tr("Minor moons"); // category for this.
|
||||
else
|
||||
return QObject::tr("Other objects");
|
||||
}
|
||||
|
||||
|
||||
// Override QAbstractTableModel::data()
|
||||
QVariant SolarSystemTreeModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
Selection sel = objectAtIndex(index);
|
||||
|
||||
if (role != Qt::DisplayRole)
|
||||
return QVariant();
|
||||
|
||||
TreeItem* item = itemAtIndex(index);
|
||||
|
||||
// See if the tree item is a group
|
||||
if (item->classification != 0)
|
||||
{
|
||||
if (index.column() == NameColumn)
|
||||
return classificationName(item->classification);
|
||||
else
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
// Tree item is an object, not a group
|
||||
|
||||
Selection sel = item->obj;
|
||||
|
||||
switch (index.column())
|
||||
{
|
||||
case NameColumn:
|
||||
|
@ -388,6 +635,10 @@ SolarSystemBrowser::SolarSystemBrowser(CelestiaCore* _appCore, QWidget* parent)
|
|||
connect(refreshButton, SIGNAL(clicked()), this, SLOT(slotRefreshTree()));
|
||||
layout->addWidget(refreshButton);
|
||||
|
||||
groupCheckBox = new QCheckBox(tr("Group objects by class"));
|
||||
connect(groupCheckBox, SIGNAL(clicked()), this, SLOT(slotRefreshTree()));
|
||||
layout->addWidget(groupCheckBox);
|
||||
|
||||
slotRefreshTree();
|
||||
|
||||
setLayout(layout);
|
||||
|
@ -412,16 +663,20 @@ void SolarSystemBrowser::slotRefreshTree()
|
|||
if (!solarSys)
|
||||
return;
|
||||
|
||||
//Selection sun = appCore->getSimulation()->getUniverse()->find("Sol");
|
||||
|
||||
Star* rootStar = solarSys->getStar();
|
||||
|
||||
// We want to show all gravitationally associated stars in the
|
||||
// browser; follow the chain up the parent star or barycenter.
|
||||
while (rootStar->getOrbitBarycenter() != NULL)
|
||||
{
|
||||
rootStar = rootStar->getOrbitBarycenter();
|
||||
}
|
||||
|
||||
solarSystemModel->buildModel(*rootStar);
|
||||
bool groupByClass = groupCheckBox->checkState() == Qt::Checked;
|
||||
|
||||
solarSystemModel->buildModel(rootStar, groupByClass);
|
||||
|
||||
treeView->resizeColumnToContents(SolarSystemTreeModel::NameColumn);
|
||||
|
||||
treeView->clearSelection();
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
class QAbstractItemModel;
|
||||
class QTreeView;
|
||||
class QCheckBox;
|
||||
class CelestiaCore;
|
||||
|
||||
class SolarSystemTreeModel;
|
||||
|
@ -43,6 +44,8 @@ Q_OBJECT
|
|||
|
||||
SolarSystemTreeModel* solarSystemModel;
|
||||
QTreeView* treeView;
|
||||
|
||||
QCheckBox* groupCheckBox;
|
||||
};
|
||||
|
||||
#endif // _QTSOLARSYSTEMBROWSER_H_
|
||||
|
|
Loading…
Reference in New Issue