static QString objectTypeName(const Selection& sel) { if (sel.star() != NULL) { if (!sel.star()->getVisibility()) return QObject::tr("Barycenter"); else return QObject::tr("Star"); } else if (sel.body() != NULL) { int classification = sel.body()->getClassification(); if (classification == Body::Planet) return QObject::tr("Planet"); else if (classification == Body::DwarfPlanet) return QObject::tr("Dwarf planet"); else if (classification == Body::Moon || classification == Body::MinorMoon) return QObject::tr("Moon"); else if (classification == Body::Asteroid) return QObject::tr("Asteroid"); else if (classification == Body::Comet) return QObject::tr("Comet"); else if (classification == Body::Spacecraft) return QObject::tr("Spacecraft"); else if (classification == Body::Invisible) return QObject::tr("Reference point"); else if (classification == Body::Component) return QObject::tr("Component"); else if (classification == Body::SurfaceFeature) return QObject::tr("Surface feature"); } return QObject::tr("Unknown"); }
void InfoPanel::buildInfoPage(Selection sel, Universe* universe, double tdb) { QString pageText; QTextStream stream(&pageText, QIODevice::WriteOnly); pageHeader(stream); if (sel.body() != NULL) { buildSolarSystemBodyPage(sel.body(), tdb, stream); } else if (sel.star() != NULL) { buildStarPage(sel.star(), universe, tdb, stream); } else if (sel.deepsky() != NULL) { buildDSOPage(sel.deepsky(), universe, stream); } else { stream << "Error: no object selected!\n"; } pageFooter(stream); textBrowser->setHtml(pageText); }
void CommandPreloadTextures::process(ExecutionEnvironment& env) { Selection target = env.getSimulation()->findObjectFromPath(name); if (target.body() == NULL) return; if (env.getRenderer() != NULL) env.getRenderer()->loadTextures(target.body()); }
void CelestiaAppWindow::slotAddBookmark() { // Set the default bookmark title to the name of the current selection Selection sel = m_appCore->getSimulation()->getSelection(); QString defaultTitle; if (sel.body() != NULL) { defaultTitle = QString::fromUtf8(sel.body()->getName(true).c_str()); } else if (sel.star() != NULL) { Universe* universe = m_appCore->getSimulation()->getUniverse(); defaultTitle = QString::fromUtf8(ReplaceGreekLetterAbbr(universe->getStarCatalog()->getStarName(*sel.star(), true)).c_str()); } else if (sel.deepsky() != NULL) { Universe* universe = m_appCore->getSimulation()->getUniverse(); defaultTitle = QString::fromUtf8(ReplaceGreekLetterAbbr(universe->getDSOCatalog()->getDSOName(sel.deepsky(), true)).c_str()); } else if (sel.location() != NULL) { defaultTitle = sel.location()->getName().c_str(); } if (defaultTitle.isEmpty()) defaultTitle = _("New bookmark"); CelestiaState appState; appState.captureState(m_appCore); // Capture the current frame buffer to use as a bookmark icon. QImage grabbedImage = glWidget->grabFrameBuffer(); int width = grabbedImage.width(); int height = grabbedImage.height(); // Crop the image to a square. QImage iconImage; if (width > height) iconImage = grabbedImage.copy((width - height) / 2, 0, height, height); else iconImage = grabbedImage.copy(0, (height - width) / 2, width, width); AddBookmarkDialog dialog(m_bookmarkManager, defaultTitle, appState, iconImage); dialog.exec(); populateBookmarkMenu(); m_bookmarkToolBar->rebuild(); }
static int object_orbitcoloroverridden(lua_State* l) { CelxLua celx(l); celx.checkArgs(1, 1, "No arguments expected to object:orbitcoloroverridden"); bool isOverridden = false; Selection* sel = this_object(l); if (sel->body() != NULL) { isOverridden = sel->body()->isOrbitColorOverridden(); } lua_pushboolean(l, isOverridden); return 1; }
// Utility function that returns the complete path for a selection. string getSelectionName(const Selection& selection, CelestiaCore* appCore) { Universe *universe = appCore->getSimulation()->getUniverse(); switch (selection.getType()) { case Selection::Type_Body: return getBodyName(universe, selection.body()); case Selection::Type_Star: return universe->getStarCatalog()->getStarName(*selection.star()); case Selection::Type_DeepSky: return universe->getDSOCatalog()->getDSOName(selection.deepsky()); case Selection::Type_Location: { std::string name = selection.location()->getName(); Body* parentBody = selection.location()->getParentBody(); if (parentBody != NULL) name = getBodyName(universe, parentBody) + ":" + name; return name; } default: return ""; } }
static int object_setorbitcolor(lua_State* l) { CelxLua celx(l); celx.checkArgs(4, 4, "Red, green, and blue color values exepected for object:setorbitcolor()"); Selection* sel = this_object(l); float r = (float) celx.safeGetNumber(2, WrongType, "Argument 1 to object:setorbitcolor() must be a number", 0.0); float g = (float) celx.safeGetNumber(3, WrongType, "Argument 2 to object:setorbitcolor() must be a number", 0.0); float b = (float) celx.safeGetNumber(4, WrongType, "Argument 3 to object:setorbitcolor() must be a number", 0.0); Color orbitColor(r, g, b); if (sel->body() != NULL) { sel->body()->setOrbitColor(orbitColor); } return 0; }
// Set the object visibility flag. static int object_setvisible(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "One argument expected to object:setvisible()"); Selection* sel = this_object(l); bool visible = celx.safeGetBoolean(2, AllErrors, "Argument to object:setvisible() must be a boolean"); if (sel->body() != NULL) { sel->body()->setVisible(visible); } else if (sel->deepsky() != NULL) { sel->deepsky()->setVisible(visible); } return 0; }
// Override QAbstractTableModel::data() QVariant SolarSystemTreeModel::data(const QModelIndex& index, int role) const { 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: if (sel.star() != NULL) { string starNameString = ReplaceGreekLetterAbbr(universe->getStarCatalog()->getStarName(*sel.star())); return QString::fromUtf8(starNameString.c_str()); } else if (sel.body() != NULL) { return QVariant(sel.body()->getName().c_str()); } else { return QVariant(); } case TypeColumn: return objectTypeName(sel); default: return QVariant(); } }
Body* ThreeDVisualizationTool::findBody(const StaBody* body) { string cname = body->name().toUtf8().data(); Selection searchContexts[2] = { Selection(m_sun), Selection(m_ssb) }; Selection object = m_celestiaCore->getSimulation()->getUniverse()->find(cname, searchContexts, 2); return object.body(); }
static int object_setorbitcoloroverridden(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "One argument expected to object:setorbitcoloroverridden"); Selection* sel = this_object(l); bool override = celx.safeGetBoolean(2, AllErrors, "Argument to object:setorbitcoloroverridden() must be a boolean"); if (sel->body() != NULL) { sel->body()->setOrbitColorOverridden(override); }
void CommandSetRadius::process(ExecutionEnvironment& env) { Selection sel = env.getSimulation()->findObjectFromPath(object); if (sel.body() != NULL) { Body* body = sel.body(); float iradius = body->getRadius(); if ((radius > 0)) { body->setSemiAxes(body->getSemiAxes() * ((float) radius / iradius)); } if (body->getRings() != NULL) { RingSystem rings(0.0f, 0.0f); rings = *body->getRings(); float inner = rings.innerRadius; float outer = rings.outerRadius; rings.innerRadius = inner * (float) radius / iradius; rings.outerRadius = outer * (float) radius / iradius; body->setRings(rings); } } }
Vector3d SunDirectionArrow::getDirection(double tdb) const { const Body* b = &body; Star* sun = NULL; while (b != NULL) { Selection center = b->getOrbitFrame(tdb)->getCenter(); if (center.star() != NULL) sun = center.star(); b = center.body(); } if (sun != NULL) { return Selection(sun).getPosition(tdb).offsetFromKm(body.getPosition(tdb)); } else { return Vector3d::Zero(); } }
bool LoadSolarSystemObjects(istream& in, Universe& universe, const std::string& directory) { Tokenizer tokenizer(&in); Parser parser(&tokenizer); while (tokenizer.nextToken() != Tokenizer::TokenEnd) { // Read the disposition; if none is specified, the default is Add. Disposition disposition = AddObject; if (tokenizer.getTokenType() == Tokenizer::TokenName) { if (tokenizer.getNameValue() == "Add") { disposition = AddObject; tokenizer.nextToken(); } else if (tokenizer.getNameValue() == "Replace") { disposition = ReplaceObject; tokenizer.nextToken(); } else if (tokenizer.getNameValue() == "Modify") { disposition = ModifyObject; tokenizer.nextToken(); } } // Read the item type; if none is specified the default is Body string itemType("Body"); if (tokenizer.getTokenType() == Tokenizer::TokenName) { itemType = tokenizer.getNameValue(); tokenizer.nextToken(); } if (tokenizer.getTokenType() != Tokenizer::TokenString) { sscError(tokenizer, "object name expected"); return false; } // The name list is a string with zero more names. Multiple names are // delimited by colons. string nameList = tokenizer.getStringValue().c_str(); if (tokenizer.nextToken() != Tokenizer::TokenString) { sscError(tokenizer, "bad parent object name"); return false; } string parentName = tokenizer.getStringValue().c_str(); Value* objectDataValue = parser.readValue(); if (objectDataValue == NULL) { sscError(tokenizer, "bad object definition"); return false; } if (objectDataValue->getType() != Value::HashType) { sscError(tokenizer, "{ expected"); delete objectDataValue; return false; } Hash* objectData = objectDataValue->getHash(); Selection parent = universe.findPath(parentName, NULL, 0); PlanetarySystem* parentSystem = NULL; vector<string> names; // Iterate through the string for names delimited // by ':', and insert them into the name list. if (nameList.empty()) { names.push_back(""); } else { string::size_type startPos = 0; while (startPos != string::npos) { string::size_type next = nameList.find(':', startPos); string::size_type length = string::npos; if (next != string::npos) { length = next - startPos; ++next; } names.push_back(nameList.substr(startPos, length)); startPos = next; } } string primaryName = names.front(); BodyType bodyType = UnknownBodyType; if (itemType == "Body") bodyType = NormalBody; else if (itemType == "ReferencePoint") bodyType = ReferencePoint; else if (itemType == "SurfaceObject") bodyType = SurfaceObject; if (bodyType != UnknownBodyType) { //bool orbitsPlanet = false; if (parent.star() != NULL) { SolarSystem* solarSystem = universe.getSolarSystem(parent.star()); if (solarSystem == NULL) { // No solar system defined for this star yet, so we need // to create it. solarSystem = universe.createSolarSystem(parent.star()); } parentSystem = solarSystem->getPlanets(); } else if (parent.body() != NULL) { // Parent is a planet or moon parentSystem = parent.body()->getSatellites(); if (parentSystem == NULL) { // If the planet doesn't already have any satellites, we // have to create a new planetary system for it. parentSystem = new PlanetarySystem(parent.body()); parent.body()->setSatellites(parentSystem); } //orbitsPlanet = true; } else { errorMessagePrelude(tokenizer); cerr << _("parent body '") << parentName << _("' of '") << primaryName << _("' not found.") << endl; } if (parentSystem != NULL) { Body* existingBody = parentSystem->find(primaryName); if (existingBody) { if (disposition == AddObject) { errorMessagePrelude(tokenizer); cerr << _("warning duplicate definition of ") << parentName << " " << primaryName << '\n'; } else if (disposition == ReplaceObject) { existingBody->setDefaultProperties(); } } Body* body; if (bodyType == ReferencePoint) body = CreateReferencePoint(primaryName, parentSystem, universe, existingBody, objectData, directory, disposition); else body = CreateBody(primaryName, parentSystem, universe, existingBody, objectData, directory, disposition, bodyType); if (body != NULL && disposition == AddObject) { vector<string>::const_iterator iter = names.begin(); iter++; while (iter != names.end()) { body->addAlias(*iter); iter++; } } } } else if (itemType == "AltSurface") { Surface* surface = new Surface(); surface->color = Color(1.0f, 1.0f, 1.0f); surface->hazeColor = Color(0.0f, 0.0f, 0.0f, 0.0f); FillinSurface(objectData, surface, directory); if (surface != NULL && parent.body() != NULL) parent.body()->addAlternateSurface(primaryName, surface); else sscError(tokenizer, _("bad alternate surface")); } else if (itemType == "Location") { if (parent.body() != NULL) { Location* location = CreateLocation(objectData, parent.body()); if (location != NULL) { location->setName(primaryName); parent.body()->addLocation(location); } else { sscError(tokenizer, _("bad location")); } } else { errorMessagePrelude(tokenizer); cerr << _("parent body '") << parentName << _("' of '") << primaryName << _("' not found.\n"); } } delete objectDataValue; } // TODO: Return some notification if there's an error parsing the file return true; }
static bool CreateTimeline(Body* body, PlanetarySystem* system, Universe& universe, Hash* planetData, const string& path, Disposition disposition, BodyType bodyType) { FrameTree* parentFrameTree = NULL; Selection parentObject = GetParentObject(system); bool orbitsPlanet = false; if (parentObject.body()) { parentFrameTree = parentObject.body()->getOrCreateFrameTree(); orbitsPlanet = true; } else if (parentObject.star()) { SolarSystem* solarSystem = universe.getSolarSystem(parentObject.star()); if (solarSystem == NULL) solarSystem = universe.createSolarSystem(parentObject.star()); parentFrameTree = solarSystem->getFrameTree(); } else { // Bad orbit barycenter specified return false; } ReferenceFrame* defaultOrbitFrame = NULL; ReferenceFrame* defaultBodyFrame = NULL; if (bodyType == SurfaceObject) { defaultOrbitFrame = new BodyFixedFrame(parentObject, parentObject); defaultBodyFrame = CreateTopocentricFrame(parentObject, parentObject, Selection(body)); defaultOrbitFrame->addRef(); defaultBodyFrame->addRef(); } else { defaultOrbitFrame = parentFrameTree->getDefaultReferenceFrame(); defaultBodyFrame = parentFrameTree->getDefaultReferenceFrame(); } // If there's an explicit timeline definition, parse that. Otherwise, we'll do // things the old way. Value* value = planetData->getValue("Timeline"); if (value != NULL) { if (value->getType() != Value::ArrayType) { clog << "Error: Timeline must be an array\n"; return false; } Timeline* timeline = CreateTimelineFromArray(body, universe, value->getArray(), path, defaultOrbitFrame, defaultBodyFrame); if (timeline == NULL) { return false; } else { body->setTimeline(timeline); return true; } } // Information required for the object timeline. ReferenceFrame* orbitFrame = NULL; ReferenceFrame* bodyFrame = NULL; Orbit* orbit = NULL; RotationModel* rotationModel = NULL; double beginning = -numeric_limits<double>::infinity(); double ending = numeric_limits<double>::infinity(); // If any new timeline values are specified, we need to overrideOldTimeline will // be set to true. bool overrideOldTimeline = false; // The interaction of Modify with timelines is slightly complicated. If the timeline // is specified by putting the OrbitFrame, Orbit, BodyFrame, or RotationModel directly // in the object definition (i.e. not inside a Timeline structure), it will completely // replace the previous timeline if it contained more than one phase. Otherwise, the // properties of the single phase will be modified individually, for compatibility with // Celestia versions 1.5.0 and earlier. if (disposition == ModifyObject) { const Timeline* timeline = body->getTimeline(); if (timeline->phaseCount() == 1) { const TimelinePhase* phase = timeline->getPhase(0); orbitFrame = phase->orbitFrame(); bodyFrame = phase->bodyFrame(); orbit = phase->orbit(); rotationModel = phase->rotationModel(); beginning = phase->startTime(); ending = phase->endTime(); } } // Get the object's orbit reference frame. bool newOrbitFrame = false; Value* frameValue = planetData->getValue("OrbitFrame"); if (frameValue != NULL) { ReferenceFrame* frame = CreateReferenceFrame(universe, frameValue, parentObject, body); if (frame != NULL) { orbitFrame = frame; newOrbitFrame = true; overrideOldTimeline = true; } } // Get the object's body frame. bool newBodyFrame = false; Value* bodyFrameValue = planetData->getValue("BodyFrame"); if (bodyFrameValue != NULL) { ReferenceFrame* frame = CreateReferenceFrame(universe, bodyFrameValue, parentObject, body); if (frame != NULL) { bodyFrame = frame; newBodyFrame = true; overrideOldTimeline = true; } } // If no orbit or body frame was specified, use the default ones if (orbitFrame == NULL) orbitFrame = defaultOrbitFrame; if (bodyFrame == NULL) bodyFrame = defaultBodyFrame; // If the center of the is a star, orbital element units are // in AU; otherwise, use kilometers. if (orbitFrame->getCenter().star() != NULL) orbitsPlanet = false; else orbitsPlanet = true; Orbit* newOrbit = CreateOrbit(orbitFrame->getCenter(), planetData, path, !orbitsPlanet); if (newOrbit == NULL && orbit == NULL) { if (body->getTimeline() && disposition == ModifyObject) { // The object definition is modifying an existing object with a multiple phase // timeline, but no orbit definition was given. This can happen for completely // sensible reasons, such a Modify definition that just changes visual properties. // Or, the definition may try to change other timeline phase properties such as // the orbit frame, but without providing an orbit. In both cases, we'll just // leave the original timeline alone. return true; } else { clog << "No valid orbit specified for object '" << body->getName() << "'. Skipping.\n"; return false; } } // If a new orbit was given, override any old orbit if (newOrbit != NULL) { orbit = newOrbit; overrideOldTimeline = true; } // Get the rotation model for this body double syncRotationPeriod = orbit->getPeriod(); RotationModel* newRotationModel = CreateRotationModel(planetData, path, syncRotationPeriod); // If a new rotation model was given, override the old one if (newRotationModel != NULL) { rotationModel = newRotationModel; overrideOldTimeline = true; } // If there was no rotation model specified, nor a previous rotation model to // override, create the default one. if (rotationModel == NULL) { // If no rotation model is provided, use a default rotation model-- // a uniform rotation that's synchronous with the orbit (appropriate // for nearly all natural satellites in the solar system.) rotationModel = CreateDefaultRotationModel(syncRotationPeriod); } if (ParseDate(planetData, "Beginning", beginning)) overrideOldTimeline = true; if (ParseDate(planetData, "Ending", ending)) overrideOldTimeline = true; // Something went wrong if the disposition isn't modify and no timeline // is to be created. assert(disposition == ModifyObject || overrideOldTimeline); if (overrideOldTimeline) { if (beginning >= ending) { clog << "Beginning time must be before Ending time.\n"; return false; } // We finally have an orbit, rotation model, frames, and time range. Create // the object timeline. TimelinePhase* phase = TimelinePhase::CreateTimelinePhase(universe, body, beginning, ending, *orbitFrame, *orbit, *bodyFrame, *rotationModel); // We've already checked that beginning < ending; nothing else should go // wrong during the creation of a TimelinePhase. assert(phase != NULL); if (phase == NULL) { clog << "Internal error creating TimelinePhase.\n"; return false; } Timeline* timeline = new Timeline(); timeline->appendPhase(phase); body->setTimeline(timeline); // Check for circular references in frames; this can only be done once the timeline // has actually been set. // TIMELINE-TODO: This check is not comprehensive; it won't find recursion in // multiphase timelines. if (newOrbitFrame && isFrameCircular(*body->getOrbitFrame(0.0), ReferenceFrame::PositionFrame)) { clog << "Orbit frame for " << body->getName() << " is nested too deep (probably circular)\n"; return false; } if (newBodyFrame && isFrameCircular(*body->getBodyFrame(0.0), ReferenceFrame::OrientationFrame)) { clog << "Body frame for " << body->getName() << " is nested too deep (probably circular)\n"; return false; } } return true; }
SelectionPopup::SelectionPopup(const Selection& sel, CelestiaCore* _appCore, QWidget* parent) : QMenu(parent), selection(sel), appCore(_appCore), centerAction(NULL), gotoAction(NULL) { Simulation* sim = appCore->getSimulation(); Vec3d v = sel.getPosition(sim->getTime()) - sim->getObserver().getPosition(); if (sel.body() != NULL) { addAction(boldTextItem(QString::fromUtf8(sel.body()->getName(true).c_str()))); // Start and end dates double startTime = 0.0; double endTime = 0.0; sel.body()->getLifespan(startTime, endTime); if (startTime > -1.0e9 || endTime < 1.0e9) { addSeparator(); if (startTime > -1.0e9) { ostringstream startDateStr; startDateStr << "Start: " << astro::TDBtoUTC(startTime); QAction* startDateAct = new QAction(startDateStr.str().c_str(), this); connect(startDateAct, SIGNAL(triggered()), this, SLOT(slotGotoStartDate())); addAction(startDateAct); } if (endTime < 1.0e9) { ostringstream endDateStr; endDateStr << "End: " << astro::TDBtoUTC(endTime); QAction* endDateAct = new QAction(endDateStr.str().c_str(), this); connect(endDateAct, SIGNAL(triggered()), this, SLOT(slotGotoEndDate())); addAction(endDateAct); } } } else if (sel.star() != NULL) { std::string name = sim->getUniverse()->getStarCatalog()->getStarName(*sel.star()); addAction(boldTextItem(QString::fromUtf8(ReplaceGreekLetterAbbr(name).c_str()))); // Add some text items giving additional information about // the star. double distance = v.length() * 1e-6; char buff[50]; setlocale(LC_NUMERIC, ""); if (abs(distance) >= astro::AUtoLightYears(1000.0f)) sprintf(buff, "%.3f %s", distance, ("ly")); else if (abs(distance) >= astro::kilometersToLightYears(10000000.0)) sprintf(buff, "%.3f %s", astro::lightYearsToAU(distance), ("au")); else if (abs(distance) > astro::kilometersToLightYears(1.0f)) sprintf(buff, "%.3f km", astro::lightYearsToKilometers(distance)); else sprintf(buff, "%.3f m", astro::lightYearsToKilometers(distance) * 1000.0f); addAction(italicTextItem(tr("Distance: ") + QString::fromUtf8(buff))); sprintf(buff, "%.2f (%.2f)", sel.star()->getAbsoluteMagnitude(), astro::absToAppMag(sel.star()->getAbsoluteMagnitude(), (float) distance)); addAction(italicTextItem(tr("Abs (app) mag: ") + QString::fromUtf8(buff))); sprintf(buff, "%s", sel.star()->getSpectralType()); addAction(italicTextItem(tr("Class: ") + QString::fromUtf8(buff))); setlocale(LC_NUMERIC, "C"); } else if (sel.deepsky() != NULL) { addAction(boldTextItem(QString::fromUtf8(sim->getUniverse()->getDSOCatalog()->getDSOName(sel.deepsky()).c_str()))); } addSeparator(); QAction* selectAction = new QAction(tr("&Select"), this); connect(selectAction, SIGNAL(triggered()), this, SLOT(slotSelect())); addAction(selectAction); centerAction = new QAction(tr("&Center"), this); connect(centerAction, SIGNAL(triggered()), this, SLOT(slotCenterSelection())); addAction(centerAction); gotoAction = new QAction(tr("&Goto"), this); connect(gotoAction, SIGNAL(triggered()), this, SLOT(slotGotoSelection())); addAction(gotoAction); QAction* followAction = new QAction(tr("&Follow"), this); connect(followAction, SIGNAL(triggered()), this, SLOT(slotFollowSelection())); addAction(followAction); if (sel.star() == NULL && sel.deepsky() == NULL) { QAction* syncOrbitAction = new QAction(tr("S&ynch Orbit"), this); connect(syncOrbitAction, SIGNAL(triggered()), this, SLOT(slotSyncOrbitSelection())); addAction(syncOrbitAction); } QAction* infoAction = new QAction("Info", this); connect(infoAction, SIGNAL(triggered()), this, SLOT(slotInfo())); addAction(infoAction); if (sel.body() != NULL) { QAction* setVisibilityAction = new QAction("Visible", this); setVisibilityAction->setCheckable(true); setVisibilityAction->setChecked(sel.body()->isVisible()); connect(setVisibilityAction, SIGNAL(toggled(bool)), this, SLOT(slotToggleVisibility(bool))); addAction(setVisibilityAction); }
// 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*> surfaceFeatures; vector<Body*> components; vector<Body*> other; vector<Body*> normal; bool groupAsteroids = true; bool groupSpacecraft = true; bool groupComponents = true; bool groupSurfaceFeatures = true; if (parent.body()) { // 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::DwarfPlanet: case Body::Invisible: normal.push_back(body); break; case Body::Moon: normal.push_back(body); break; case Body::MinorMoon: minorMoons.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; case Body::Component: if (groupComponents) components.push_back(body); else normal.push_back(body); break; case Body::SurfaceFeature: if (groupSurfaceFeatures) surfaceFeatures.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 (!surfaceFeatures.empty()) item->nChildren++; if (!components.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[childIndex] = 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::MinorMoon, 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 (!surfaceFeatures.empty()) { item->children[childIndex] = createGroupTreeItem(Body::SurfaceFeature, surfaceFeatures, item, childIndex); childIndex++; } if (!components.empty()) { item->children[childIndex] = createGroupTreeItem(Body::Component, components, item, childIndex); childIndex++; } if (!other.empty()) { item->children[childIndex] = createGroupTreeItem(Body::Unknown, other, item, childIndex); childIndex++; } } } }