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; }
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; }