parseobject.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:55k
- // parseobject.cpp
- //
- // Copyright (C) 2004-2008 Chris Laurel <claurel@gmail.com>
- //
- // Functions for parsing objects common to star, solar system, and
- // deep sky catalogs.
- //
- // This program is free software; you can redistribute it and/or
- // modify it under the terms of the GNU General Public License
- // as published by the Free Software Foundation; either version 2
- // of the License, or (at your option) any later version.
- #include <cassert>
- #include <celutil/debug.h>
- #include "parseobject.h"
- #include "customorbit.h"
- #include "customrotation.h"
- #include "spiceorbit.h"
- #include "spicerotation.h"
- #include "scriptorbit.h"
- #include "scriptrotation.h"
- #include "frame.h"
- #include "trajmanager.h"
- #include "rotationmanager.h"
- #include "universe.h"
- using namespace std;
- bool
- ParseDate(Hash* hash, const string& name, double& jd)
- {
- // Check first for a number value representing a Julian date
- if (hash->getNumber(name, jd))
- return true;
- string dateString;
- if (hash->getString(name, dateString))
- {
- astro::Date date(1, 1, 1);
- if (astro::parseDate(dateString, date))
- {
- jd = (double) date;
- return true;
- }
- }
- return false;
- }
- /*!
- * Create a new Keplerian orbit from an ssc property table:
- *
- * EllipticalOrbit
- * {
- * # One of the following is required to specify orbit size:
- * SemiMajorAxis <number>
- * PericenterDistance <number>
- *
- * # Required
- * Period <number>
- *
- * Eccentricity <number> (default: 0.0)
- * Inclination <degrees> (default: 0.0)
- * AscendingNode <degrees> (default: 0.0)
- *
- * # One or none of the following:
- * ArgOfPericenter <degrees> (default: 0.0)
- * LongOfPericenter <degrees> (default: 0.0)
- *
- * Epoch <date> (default J2000.0)
- *
- * # One or none of the following:
- * MeanAnomaly <degrees> (default: 0.0)
- * MeanLongitude <degrees> (default: 0.0)
- * }
- *
- * If usePlanetUnits is true:
- * Period is in Julian years
- * SemiMajorAxis or PericenterDistance is in AU
- * Otherwise:
- * Period is in Julian days
- * SemiMajorAxis or PericenterDistance is in kilometers.
- */
- static EllipticalOrbit*
- CreateEllipticalOrbit(Hash* orbitData,
- bool usePlanetUnits)
- {
- // SemiMajorAxis and Period are absolutely required; everything
- // else has a reasonable default.
- double pericenterDistance = 0.0;
- double semiMajorAxis = 0.0;
- if (!orbitData->getNumber("SemiMajorAxis", semiMajorAxis))
- {
- if (!orbitData->getNumber("PericenterDistance", pericenterDistance))
- {
- clog << "SemiMajorAxis/PericenterDistance missing! Skipping planet . . .n";
- return NULL;
- }
- }
- double period = 0.0;
- if (!orbitData->getNumber("Period", period))
- {
- clog << "Period missing! Skipping planet . . .n";
- return NULL;
- }
- double eccentricity = 0.0;
- orbitData->getNumber("Eccentricity", eccentricity);
- double inclination = 0.0;
- orbitData->getNumber("Inclination", inclination);
- double ascendingNode = 0.0;
- orbitData->getNumber("AscendingNode", ascendingNode);
- double argOfPericenter = 0.0;
- if (!orbitData->getNumber("ArgOfPericenter", argOfPericenter))
- {
- double longOfPericenter = 0.0;
- if (orbitData->getNumber("LongOfPericenter", longOfPericenter))
- argOfPericenter = longOfPericenter - ascendingNode;
- }
- double epoch = astro::J2000;
- ParseDate(orbitData, "Epoch", epoch);
- // Accept either the mean anomaly or mean longitude--use mean anomaly
- // if both are specified.
- double anomalyAtEpoch = 0.0;
- if (!orbitData->getNumber("MeanAnomaly", anomalyAtEpoch))
- {
- double longAtEpoch = 0.0;
- if (orbitData->getNumber("MeanLongitude", longAtEpoch))
- anomalyAtEpoch = longAtEpoch - (argOfPericenter + ascendingNode);
- }
- if (usePlanetUnits)
- {
- semiMajorAxis = astro::AUtoKilometers(semiMajorAxis);
- pericenterDistance = astro::AUtoKilometers(pericenterDistance);
- period = period * 365.25;
- }
- // If we read the semi-major axis, use it to compute the pericenter
- // distance.
- if (semiMajorAxis != 0.0)
- pericenterDistance = semiMajorAxis * (1.0 - eccentricity);
- return new EllipticalOrbit(pericenterDistance,
- eccentricity,
- degToRad(inclination),
- degToRad(ascendingNode),
- degToRad(argOfPericenter),
- degToRad(anomalyAtEpoch),
- period,
- epoch);
- }
- /*!
- * Create a new sampled orbit from an ssc property table:
- *
- * SampledTrajectory
- * {
- * Source <string>
- * Interpolation "Cubic" | "Linear"
- * DoublePrecision <boolean>
- * }
- *
- * Source is the only required field. Interpolation defaults to cubic, and
- * DoublePrecision defaults to true.
- */
- static Orbit*
- CreateSampledTrajectory(Hash* trajData, const string& path)
- {
- string sourceName;
- if (!trajData->getString("Source", sourceName))
- {
- clog << "SampledTrajectory is missing a source.n";
- return NULL;
- }
- // Read interpolation type; string value must be either "Linear" or "Cubic"
- // Default interpolation type is cubic.
- string interpolationString;
- TrajectoryInterpolation interpolation = TrajectoryInterpolationCubic;
- if (trajData->getString("Interpolation", interpolationString))
- {
- if (!compareIgnoringCase(interpolationString, "linear"))
- interpolation = TrajectoryInterpolationLinear;
- else if (!compareIgnoringCase(interpolationString, "cubic"))
- interpolation = TrajectoryInterpolationCubic;
- else
- clog << "Unknown interpolation type " << interpolationString << endl; // non-fatal error
- }
- // Double precision is true by default
- bool useDoublePrecision = true;
- trajData->getBoolean("DoublePrecision", useDoublePrecision);
- TrajectoryPrecision precision = useDoublePrecision ? TrajectoryPrecisionDouble : TrajectoryPrecisionSingle;
- DPRINTF(1, "Attempting to load sampled trajectory from source '%s'n", sourceName.c_str());
- ResourceHandle orbitHandle = GetTrajectoryManager()->getHandle(TrajectoryInfo(sourceName, path, interpolation, precision));
- Orbit* orbit = GetTrajectoryManager()->find(orbitHandle);
- if (orbit == NULL)
- {
- clog << "Could not load sampled trajectory from '" << sourceName << "'n";
- }
- return orbit;
- }
- // Create a new FixedPosition trajectory. A FixedPosition is a property list
- // with one of the following 3-vector properties:
- // Rectangular
- // Planetographic
- // Planetocentric
- // Planetographic and planetocentric coordinates are given in the order longitude,
- // latitude, altitude. Units of altitude are kilometers. Planetographic and
- // and planetocentric coordinates are only practical when the coordinate system
- // is BodyFixed.
- static Orbit*
- CreateFixedPosition(Hash* trajData, const Selection& centralObject, bool usePlanetUnits)
- {
- Vec3d position(0.0, 0.0, 0.0);
- Vec3d v(0.0, 0.0, 0.0);
- if (trajData->getVector("Rectangular", v))
- {
- if (usePlanetUnits)
- v = v * astro::AUtoKilometers(1.0);
- // Convert to Celestia's coordinate system
- position = Vec3d(v.x, v.z, -v.y);
- }
- else if (trajData->getVector("Planetographic", v))
- {
- if (centralObject.getType() != Selection::Type_Body)
- {
- clog << "FixedPosition planetographic coordinates aren't valid for stars.n";
- return NULL;
- }
- // TODO: Need function to calculate planetographic coordinates
- // TODO: Change planetocentricToCartesian so that 180 degree offset isn't required
- position = centralObject.body()->planetocentricToCartesian(180.0 + v.x, v.y, v.z);
- }
- else if (trajData->getVector("Planetocentric", v))
- {
- if (centralObject.getType() != Selection::Type_Body)
- {
- clog << "FixedPosition planetocentric coordinates aren't valid for stars.n";
- return NULL;
- }
- // TODO: Change planetocentricToCartesian so that 180 degree offset isn't required
- position = centralObject.body()->planetocentricToCartesian(180.0 + v.x, v.y, v.z);
- }
- else
- {
- clog << "Missing coordinates for FixedPositionn";
- return NULL;
- }
- return new FixedOrbit(Point3d(position.x, position.y, position.z));
- }
- // Parse a string list--either a single string or an array of strings is permitted.
- static bool
- ParseStringList(Hash* table,
- const string& propertyName,
- list<string>& stringList)
- {
- Value* v = table->getValue(propertyName);
- if (v == NULL)
- return NULL;
- // Check for a single string first.
- if (v->getType() == Value::StringType)
- {
- stringList.push_back(v->getString());
- return true;
- }
- else if (v->getType() == Value::ArrayType)
- {
- Array* array = v->getArray();
- Array::const_iterator iter;
- // Verify that all array entries are strings
- for (iter = array->begin(); iter != array->end(); iter++)
- {
- if ((*iter)->getType() != Value::StringType)
- return false;
- }
- // Add strings to stringList
- for (iter = array->begin(); iter != array->end(); iter++)
- stringList.push_back((*iter)->getString());
- return true;
- }
- else
- {
- return false;
- }
- }
- #ifdef USE_SPICE
- /*! Create a new SPICE orbit. This is just a Celestia wrapper for a trajectory specified
- * in a SPICE SPK file.
- *
- * SpiceOrbit
- * {
- * Kernel <string|string array> # optional
- * Target <string>
- * Origin <string>
- * BoundingRadius <number>
- * Period <number> # optional
- * Beginning <number> # optional
- * Ending <number> # optional
- * }
- *
- * The Kernel property specifies one or more SPK files that must be loaded. Any
- * already loaded kernels will also be used if they contain trajectories for
- * the target or origin.
- * Target and origin are strings that give NAIF IDs for the target and origin
- * objects. Either names or integer IDs are valid, but integer IDs still must
- * be quoted.
- * BoundingRadius gives a conservative estimate of the maximum distance between
- * the target and origin objects. It is required by Celestia for visibility
- * culling when rendering.
- * Beginning and Ending specify the valid time range of the SPICE orbit. It is
- * an error to specify Beginning without Ending, and vice versa. If neither is
- * specified, the valid range is computed from the coverage window in the SPICE
- * kernel pool. If the coverage window is noncontiguous, the first interval is
- * used.
- */
- static SpiceOrbit*
- CreateSpiceOrbit(Hash* orbitData,
- const string& path,
- bool usePlanetUnits)
- {
- string targetBodyName;
- string originName;
- list<string> kernelList;
- if (orbitData->getValue("Kernel") != NULL)
- {
- // Kernel list is optional; a SPICE orbit may rely on kernels already loaded into
- // the kernel pool.
- if (!ParseStringList(orbitData, "Kernel", kernelList))
- {
- clog << "Kernel list for SPICE orbit is neither a string nor array of stringsn";
- return NULL;
- }
- }
- if (!orbitData->getString("Target", targetBodyName))
- {
- clog << "Target name missing from SPICE orbitn";
- return NULL;
- }
- if (!orbitData->getString("Origin", originName))
- {
- clog << "Origin name missing from SPICE orbitn";
- return NULL;
- }
- // A bounding radius for culling is required for SPICE orbits
- double boundingRadius = 0.0;
- if (!orbitData->getNumber("BoundingRadius", boundingRadius))
- {
- clog << "Bounding Radius missing from SPICE orbitn";
- return NULL;
- }
- // The period of the orbit may be specified if appropriate; a value
- // of zero for the period (the default), means that the orbit will
- // be considered aperiodic.
- double period = 0.0;
- orbitData->getNumber("Period", period);
- if (usePlanetUnits)
- {
- boundingRadius = astro::AUtoKilometers(boundingRadius);
- period = period * 365.25;
- }
- // Either a complete time interval must be specified with Beginning/Ending, or
- // else neither field can be present.
- Value* beginningDate = orbitData->getValue("Beginning");
- Value* endingDate = orbitData->getValue("Ending");
- if (beginningDate != NULL && endingDate == NULL)
- {
- clog << "Beginning specified for SPICE orbit, but ending is missing.n";
- return NULL;
- }
- if (endingDate != NULL && beginningDate == NULL)
- {
- clog << "Ending specified for SPICE orbit, but beginning is missing.n";
- return NULL;
- }
- SpiceOrbit* orbit = NULL;
- if (beginningDate != NULL && endingDate != NULL)
- {
- double beginningTDBJD = 0.0;
- if (!ParseDate(orbitData, "Beginning", beginningTDBJD))
- {
- clog << "Invalid beginning date specified for SPICE orbit.n";
- return NULL;
- }
- double endingTDBJD = 0.0;
- if (!ParseDate(orbitData, "Ending", endingTDBJD))
- {
- clog << "Invalid ending date specified for SPICE orbit.n";
- return NULL;
- }
-
- orbit = new SpiceOrbit(targetBodyName,
- originName,
- period,
- boundingRadius,
- beginningTDBJD,
- endingTDBJD);
- }
- else
- {
- // No time interval given; we'll use whatever coverage window is given
- // in the SPICE kernel.
- orbit = new SpiceOrbit(targetBodyName,
- originName,
- period,
- boundingRadius);
- }
- if (!orbit->init(path, &kernelList))
- {
- // Error using SPICE library; destroy the orbit; hopefully a
- // fallback is defined in the SSC file.
- delete orbit;
- orbit = NULL;
- }
- return orbit;
- }
- /*! Create a new rotation model based on a SPICE frame.
- *
- * SpiceRotation
- * {
- * Kernel <string|string array> # optional
- * Frame <string>
- * BaseFrame <string> # optional (defaults to ecliptic)
- * Period <number> # optional (units are hours)
- * Beginning <number> # optional
- * Ending <number> # optional
- * }
- *
- * The Kernel property specifies one or more SPICE kernel files that must be
- * loaded in order for the frame to be defined over the required range. Any
- * already loaded kernels will be used if they contain information relevant
- * for defining the frame.
- * Frame and base name are strings that give SPICE names for the frames. The
- * orientation of the SpiceRotation is the orientation of the frame relative to
- * the base frame. By default, the base frame is eclipj2000.
- * Beginning and Ending specify the valid time range of the SPICE rotation.
- * If the Beginning and Ending are omitted, the rotation model is assumed to
- * be valid at any time. It is an error to specify Beginning without Ending,
- * and vice versa.
- * Period specifies the principal rotation period; it defaults to 0 indicating
- * that the rotation is aperiodic. It is not essential to provide the rotation
- * period; it is only used by Celestia for displaying object information such
- * as sidereal day length.
- */
- static SpiceRotation*
- CreateSpiceRotation(Hash* rotationData,
- const string& path)
- {
- string frameName;
- string baseFrameName = "eclipj2000";
- list<string> kernelList;
- if (rotationData->getValue("Kernel") != NULL)
- {
- // Kernel list is optional; a SPICE rotation may rely on kernels already loaded into
- // the kernel pool.
- if (!ParseStringList(rotationData, "Kernel", kernelList))
- {
- clog << "Kernel list for SPICE rotation is neither a string nor array of stringsn";
- return NULL;
- }
- }
- if (!rotationData->getString("Frame", frameName))
- {
- clog << "Frame name missing from SPICE rotationn";
- return NULL;
- }
- rotationData->getString("BaseFrame", baseFrameName);
- // The period of the rotation may be specified if appropriate; a value
- // of zero for the period (the default), means that the rotation will
- // be considered aperiodic.
- double period = 0.0;
- rotationData->getNumber("Period", period);
- period = period / 24.0;
- // Either a complete time interval must be specified with Beginning/Ending, or
- // else neither field can be present.
- Value* beginningDate = rotationData->getValue("Beginning");
- Value* endingDate = rotationData->getValue("Ending");
- if (beginningDate != NULL && endingDate == NULL)
- {
- clog << "Beginning specified for SPICE rotation, but ending is missing.n";
- return NULL;
- }
- if (endingDate != NULL && beginningDate == NULL)
- {
- clog << "Ending specified for SPICE rotation, but beginning is missing.n";
- return NULL;
- }
- SpiceRotation* rotation = NULL;
- if (beginningDate != NULL && endingDate != NULL)
- {
- double beginningTDBJD = 0.0;
- if (!ParseDate(rotationData, "Beginning", beginningTDBJD))
- {
- clog << "Invalid beginning date specified for SPICE rotation.n";
- return NULL;
- }
- double endingTDBJD = 0.0;
- if (!ParseDate(rotationData, "Ending", endingTDBJD))
- {
- clog << "Invalid ending date specified for SPICE rotation.n";
- return NULL;
- }
-
- rotation = new SpiceRotation(frameName,
- baseFrameName,
- period,
- beginningTDBJD,
- endingTDBJD);
- }
- else
- {
- // No time interval given; rotation is valid at any time.
- rotation = new SpiceRotation(frameName,
- baseFrameName,
- period);
- }
- if (!rotation->init(path, &kernelList))
- {
- // Error using SPICE library; destroy the rotation.
- delete rotation;
- rotation = NULL;
- }
- return rotation;
- }
- #endif
- static ScriptedOrbit*
- CreateScriptedOrbit(Hash* orbitData,
- const string& path)
- {
- #if !defined(CELX)
- clog << "ScriptedOrbit not usable without scripting support.n";
- return NULL;
- #else
- // Function name is required
- string funcName;
- if (!orbitData->getString("Function", funcName))
- {
- clog << "Function name missing from script orbit definition.n";
- return NULL;
- }
- // Module name is optional
- string moduleName;
- orbitData->getString("Module", moduleName);
- string* pathCopy = new string(path);
- Value* pathValue = new Value(*pathCopy);
- orbitData->addValue("AddonPath", *pathValue);
- ScriptedOrbit* scriptedOrbit = new ScriptedOrbit();
- if (scriptedOrbit != NULL)
- {
- if (!scriptedOrbit->initialize(moduleName, funcName, orbitData))
- {
- delete scriptedOrbit;
- scriptedOrbit = NULL;
- }
- }
- return scriptedOrbit;
- #endif
- }
- Orbit*
- CreateOrbit(const Selection& centralObject,
- Hash* planetData,
- const string& path,
- bool usePlanetUnits)
- {
- Orbit* orbit = NULL;
- string customOrbitName;
- if (planetData->getString("CustomOrbit", customOrbitName))
- {
- orbit = GetCustomOrbit(customOrbitName);
- if (orbit != NULL)
- {
- return orbit;
- }
- clog << "Could not find custom orbit named '" << customOrbitName <<
- "'n";
- }
- #ifdef USE_SPICE
- Value* spiceOrbitDataValue = planetData->getValue("SpiceOrbit");
- if (spiceOrbitDataValue != NULL)
- {
- if (spiceOrbitDataValue->getType() != Value::HashType)
- {
- clog << "Object has incorrect spice orbit syntax.n";
- return NULL;
- }
- else
- {
- orbit = CreateSpiceOrbit(spiceOrbitDataValue->getHash(), path, usePlanetUnits);
- if (orbit != NULL)
- {
- return orbit;
- }
- clog << "Bad spice orbitn";
- DPRINTF(0, "Could not load SPICE orbitn");
- }
- }
- #endif
- // Trajectory calculated by Lua script
- Value* scriptedOrbitValue = planetData->getValue("ScriptedOrbit");
- if (scriptedOrbitValue != NULL)
- {
- if (scriptedOrbitValue->getType() != Value::HashType)
- {
- clog << "Object has incorrect scripted orbit syntax.n";
- return NULL;
- }
- else
- {
- orbit = CreateScriptedOrbit(scriptedOrbitValue->getHash(), path);
- if (orbit != NULL)
- return orbit;
- }
- }
- // New 1.5.0 style for sampled trajectories. Permits specification of
- // precision and interpolation type.
- Value* sampledTrajDataValue = planetData->getValue("SampledTrajectory");
- if (sampledTrajDataValue != NULL)
- {
- if (sampledTrajDataValue->getType() != Value::HashType)
- {
- clog << "Object has incorrect syntax for SampledTrajectory.n";
- return NULL;
- }
- else
- {
- return CreateSampledTrajectory(sampledTrajDataValue->getHash(), path);
- }
- }
- // Old style for sampled trajectories. Assumes cubic interpolation and
- // single precision.
- string sampOrbitFile;
- if (planetData->getString("SampledOrbit", sampOrbitFile))
- {
- DPRINTF(1, "Attempting to load sampled orbit file '%s'n",
- sampOrbitFile.c_str());
- ResourceHandle orbitHandle =
- GetTrajectoryManager()->getHandle(TrajectoryInfo(sampOrbitFile,
- path,
- TrajectoryInterpolationCubic,
- TrajectoryPrecisionSingle));
- orbit = GetTrajectoryManager()->find(orbitHandle);
- if (orbit != NULL)
- {
- return orbit;
- }
- clog << "Could not load sampled orbit file '" << sampOrbitFile << "'n";
- }
- Value* orbitDataValue = planetData->getValue("EllipticalOrbit");
- if (orbitDataValue != NULL)
- {
- if (orbitDataValue->getType() != Value::HashType)
- {
- clog << "Object has incorrect elliptical orbit syntax.n";
- return NULL;
- }
- else
- {
- return CreateEllipticalOrbit(orbitDataValue->getHash(),
- usePlanetUnits);
- }
- }
- // Create an 'orbit' that places the object at a fixed point in its
- // reference frame. There are two forms for FixedPosition: a simple
- // form with an 3-vector value, and complex form with a properlist
- // value. The simple form:
- //
- // FixedPosition [ x y z ]
- //
- // is a shorthand for:
- //
- // FixedPosition { Rectangular [ x y z ] }
- //
- // In addition to Rectangular, other coordinate types for fixed position are
- // Planetographic and Planetocentric.
- Value* fixedPositionValue = planetData->getValue("FixedPosition");
- if (fixedPositionValue != NULL)
- {
- Vec3d fixedPosition(0.0, 0.0, 0.0);
- if (planetData->getVector("FixedPosition", fixedPosition))
- {
- // Convert to Celestia's coordinate system
- fixedPosition = Vec3d(fixedPosition.x,
- fixedPosition.z,
- -fixedPosition.y);
- if (usePlanetUnits)
- fixedPosition = fixedPosition * astro::AUtoKilometers(1.0);
- return new FixedOrbit(Point3d(0.0, 0.0, 0.0) + fixedPosition);
- }
- else if (fixedPositionValue->getType() == Value::HashType)
- {
- return CreateFixedPosition(fixedPositionValue->getHash(), centralObject, usePlanetUnits);
- }
- else
- {
- clog << "Object has incorrect FixedPosition syntax.n";
- }
- }
- // LongLat will make an object fixed relative to the surface of its center
- // object. This is done by creating an orbit with a period equal to the
- // rotation rate of the parent object. A body-fixed reference frame is a
- // much better way to accomplish this.
- Vec3d longlat(0.0, 0.0, 0.0);
- if (planetData->getVector("LongLat", longlat))
- {
- Body* centralBody = centralObject.body();
- if (centralBody != NULL)
- {
- Vec3d pos = centralBody->planetocentricToCartesian(longlat.x, longlat.y, longlat.z);
- return new SynchronousOrbit(*centralBody, Point3d(pos.x, pos.y, pos.z));
- }
- else
- {
- // TODO: Allow fixing objects to the surface of stars.
- }
- return NULL;
- }
- return NULL;
- }
- static ConstantOrientation*
- CreateFixedRotationModel(double offset,
- double inclination,
- double ascendingNode)
- {
- Quatd q = Quatd::yrotation(-PI - offset) *
- Quatd::xrotation(-inclination) *
- Quatd::yrotation(-ascendingNode);
- return new ConstantOrientation(q);
- }
- static RotationModel*
- CreateUniformRotationModel(Hash* rotationData,
- double syncRotationPeriod)
- {
- // Default to synchronous rotation
- double period = syncRotationPeriod;
- if (rotationData->getNumber("Period", period))
- {
- period = period / 24.0;
- }
- float offset = 0.0f;
- if (rotationData->getNumber("MeridianAngle", offset))
- {
- offset = degToRad(offset);
- }
- double epoch = astro::J2000;
- ParseDate(rotationData, "Epoch", epoch);
- float inclination = 0.0f;
- if (rotationData->getNumber("Inclination", inclination))
- {
- inclination = degToRad(inclination);
- }
- float ascendingNode = 0.0f;
- if (rotationData->getNumber("AscendingNode", ascendingNode))
- {
- ascendingNode = degToRad(ascendingNode);
- }
- // No period was specified, and the default synchronous
- // rotation period is zero, indicating that the object
- // doesn't have a periodic orbit. Default to a constant
- // orientation instead.
- if (period == 0.0)
- {
- return CreateFixedRotationModel(offset, inclination, ascendingNode);
- }
- else
- {
- return new UniformRotationModel(period,
- offset,
- epoch,
- inclination,
- ascendingNode);
- }
- }
- static ConstantOrientation*
- CreateFixedRotationModel(Hash* rotationData)
- {
- double offset = 0.0;
- if (rotationData->getNumber("MeridianAngle", offset))
- {
- offset = degToRad(offset);
- }
- double inclination = 0.0;
- if (rotationData->getNumber("Inclination", inclination))
- {
- inclination = degToRad(inclination);
- }
- double ascendingNode = 0.0;
- if (rotationData->getNumber("AscendingNode", ascendingNode))
- {
- ascendingNode = degToRad(ascendingNode);
- }
- Quatd q = Quatd::yrotation(-PI - offset) *
- Quatd::xrotation(-inclination) *
- Quatd::yrotation(-ascendingNode);
- return new ConstantOrientation(q);
- }
- static ConstantOrientation*
- CreateFixedAttitudeRotationModel(Hash* rotationData)
- {
- double heading = 0.0;
- if (rotationData->getNumber("Heading", heading))
- {
- heading = degToRad(heading);
- }
-
- double tilt = 0.0;
- if (rotationData->getNumber("Tilt", tilt))
- {
- tilt = degToRad(tilt);
- }
-
- double roll = 0.0;
- if (rotationData->getNumber("Roll", roll))
- {
- roll = degToRad(roll);
- }
-
- Quatd q = Quatd::yrotation(-PI - heading) *
- Quatd::xrotation(-tilt) *
- Quatd::zrotation(-roll);
-
- return new ConstantOrientation(q);
- }
- static RotationModel*
- CreatePrecessingRotationModel(Hash* rotationData,
- double syncRotationPeriod)
- {
- // Default to synchronous rotation
- double period = syncRotationPeriod;
- if (rotationData->getNumber("Period", period))
- {
- period = period / 24.0;
- }
- float offset = 0.0f;
- if (rotationData->getNumber("MeridianAngle", offset))
- {
- offset = degToRad(offset);
- }
- double epoch = astro::J2000;
- ParseDate(rotationData, "Epoch", epoch);
- float inclination = 0.0f;
- if (rotationData->getNumber("Inclination", inclination))
- {
- inclination = degToRad(inclination);
- }
- float ascendingNode = 0.0f;
- if (rotationData->getNumber("AscendingNode", ascendingNode))
- {
- ascendingNode = degToRad(ascendingNode);
- }
- // The default value of 0 is handled specially, interpreted to indicate
- // that there's no precession.
- double precessionPeriod = 0.0;
- if (rotationData->getNumber("PrecessionPeriod", precessionPeriod))
- {
- // The precession period is specified in the ssc file in units
- // of years, but internally Celestia uses days.
- precessionPeriod = precessionPeriod * 365.25;
- }
- // No period was specified, and the default synchronous
- // rotation period is zero, indicating that the object
- // doesn't have a periodic orbit. Default to a constant
- // orientation instead.
- if (period == 0.0)
- {
- return CreateFixedRotationModel(offset, inclination, ascendingNode);
- }
- else
- {
- return new PrecessingRotationModel(period,
- offset,
- epoch,
- inclination,
- ascendingNode,
- precessionPeriod);
- }
- }
- static ScriptedRotation*
- CreateScriptedRotation(Hash* rotationData,
- const string& path)
- {
- #if !defined(CELX)
- clog << "ScriptedRotation not usable without scripting support.n";
- return NULL;
- #else
- // Function name is required
- string funcName;
- if (!rotationData->getString("Function", funcName))
- {
- clog << "Function name missing from scripted rotation definition.n";
- return NULL;
- }
- // Module name is optional
- string moduleName;
- rotationData->getString("Module", moduleName);
- string* pathCopy = new string(path);
- Value* pathValue = new Value(*pathCopy);
- rotationData->addValue("AddonPath", *pathValue);
- ScriptedRotation* scriptedRotation = new ScriptedRotation();
- if (scriptedRotation != NULL)
- {
- if (!scriptedRotation->initialize(moduleName, funcName, rotationData))
- {
- delete scriptedRotation;
- scriptedRotation = NULL;
- }
- }
- return scriptedRotation;
- #endif
- }
- // Parse rotation information. Unfortunately, Celestia didn't originally have
- // RotationModel objects, so information about the rotation of the object isn't
- // grouped into a single subobject--the ssc fields relevant for rotation just
- // appear in the top level structure.
- RotationModel*
- CreateRotationModel(Hash* planetData,
- const string& path,
- double syncRotationPeriod)
- {
- RotationModel* rotationModel = NULL;
- // If more than one rotation model is specified, the following precedence
- // is used to determine which one should be used:
- // CustomRotation
- // SPICE C-Kernel
- // SampledOrientation
- // PrecessingRotation
- // UniformRotation
- // legacy rotation parameters
- string customRotationModelName;
- if (planetData->getString("CustomRotation", customRotationModelName))
- {
- rotationModel = GetCustomRotationModel(customRotationModelName);
- if (rotationModel != NULL)
- {
- return rotationModel;
- }
- clog << "Could not find custom rotation model named '" <<
- customRotationModelName << "'n";
- }
- #ifdef USE_SPICE
- Value* spiceRotationDataValue = planetData->getValue("SpiceRotation");
- if (spiceRotationDataValue != NULL)
- {
- if (spiceRotationDataValue->getType() != Value::HashType)
- {
- clog << "Object has incorrect spice rotation syntax.n";
- return NULL;
- }
- else
- {
- rotationModel = CreateSpiceRotation(spiceRotationDataValue->getHash(), path);
- if (rotationModel != NULL)
- {
- return rotationModel;
- }
- clog << "Bad spice rotation modeln";
- DPRINTF(0, "Could not load SPICE rotation modeln");
- }
- }
- #endif
- Value* scriptedRotationValue = planetData->getValue("ScriptedRotation");
- if (scriptedRotationValue != NULL)
- {
- if (scriptedRotationValue->getType() != Value::HashType)
- {
- clog << "Object has incorrect scripted rotation syntax.n";
- return NULL;
- }
- else
- {
- rotationModel = CreateScriptedRotation(scriptedRotationValue->getHash(), path);
- if (rotationModel != NULL)
- return rotationModel;
- }
- }
- string sampOrientationFile;
- if (planetData->getString("SampledOrientation", sampOrientationFile))
- {
- DPRINTF(1, "Attempting to load orientation file '%s'n",
- sampOrientationFile.c_str());
- ResourceHandle orientationHandle =
- GetRotationModelManager()->getHandle(RotationModelInfo(sampOrientationFile, path));
- rotationModel = GetRotationModelManager()->find(orientationHandle);
- if (rotationModel != NULL)
- {
- return rotationModel;
- }
- clog << "Could not load rotation model file '" <<
- sampOrientationFile << "'n";
- }
- Value* precessingRotationValue = planetData->getValue("PrecessingRotation");
- if (precessingRotationValue != NULL)
- {
- if (precessingRotationValue->getType() != Value::HashType)
- {
- clog << "Object has incorrect syntax for precessing rotation.n";
- return NULL;
- }
- else
- {
- return CreatePrecessingRotationModel(precessingRotationValue->getHash(),
- syncRotationPeriod);
- }
- }
- Value* uniformRotationValue = planetData->getValue("UniformRotation");
- if (uniformRotationValue != NULL)
- {
- if (uniformRotationValue->getType() != Value::HashType)
- {
- clog << "Object has incorrect UniformRotation syntax.n";
- return NULL;
- }
- else
- {
- return CreateUniformRotationModel(uniformRotationValue->getHash(),
- syncRotationPeriod);
- }
- }
- Value* fixedRotationValue = planetData->getValue("FixedRotation");
- if (fixedRotationValue != NULL)
- {
- if (fixedRotationValue->getType() != Value::HashType)
- {
- clog << "Object has incorrect FixedRotation syntax.n";
- return NULL;
- }
- else
- {
- return CreateFixedRotationModel(fixedRotationValue->getHash());
- }
- }
-
- Value* fixedAttitudeValue = planetData->getValue("FixedAttitude");
- if (fixedAttitudeValue != NULL)
- {
- if (fixedAttitudeValue->getType() != Value::HashType)
- {
- clog << "Object has incorrect FixedAttitude syntax.n";
- return NULL;
- }
- else
- {
- return CreateFixedAttitudeRotationModel(fixedAttitudeValue->getHash());
- }
- }
- // For backward compatibility we need to support rotation parameters
- // that appear in the main block of the object definition.
- // Default to synchronous rotation
- bool specified = false;
- double period = syncRotationPeriod;
- if (planetData->getNumber("RotationPeriod", period))
- {
- specified = true;
- period = period / 24.0f;
- }
- float offset = 0.0f;
- if (planetData->getNumber("RotationOffset", offset))
- {
- specified = true;
- offset = degToRad(offset);
- }
- double epoch = astro::J2000;
- if (ParseDate(planetData, "RotationEpoch", epoch))
- {
- specified = true;
- }
- float inclination = 0.0f;
- if (planetData->getNumber("Obliquity", inclination))
- {
- specified = true;
- inclination = degToRad(inclination);
- }
- float ascendingNode = 0.0f;
- if (planetData->getNumber("EquatorAscendingNode", ascendingNode))
- {
- specified = true;
- ascendingNode = degToRad(ascendingNode);
- }
- double precessionRate = 0.0f;
- if (planetData->getNumber("PrecessionRate", precessionRate))
- {
- specified = true;
- }
- if (specified)
- {
- RotationModel* rm = NULL;
- if (period == 0.0)
- {
- // No period was specified, and the default synchronous
- // rotation period is zero, indicating that the object
- // doesn't have a periodic orbit. Default to a constant
- // orientation instead.
- rm = CreateFixedRotationModel(offset, inclination, ascendingNode);
- }
- else if (precessionRate == 0.0)
- {
- rm = new UniformRotationModel(period,
- offset,
- epoch,
- inclination,
- ascendingNode);
- }
- else
- {
- rm = new PrecessingRotationModel(period,
- offset,
- epoch,
- inclination,
- ascendingNode,
- -360.0 / precessionRate);
- }
- return rm;
- }
- else
- {
- // No rotation fields specified
- return NULL;
- }
- }
- RotationModel* CreateDefaultRotationModel(double syncRotationPeriod)
- {
- if (syncRotationPeriod == 0.0)
- {
- // If syncRotationPeriod is 0, the orbit of the object is
- // aperiodic and we'll just return a FixedRotation.
- return new ConstantOrientation(Quatd(1.0));
- }
- else
- {
- return new UniformRotationModel(syncRotationPeriod,
- 0.0f,
- astro::J2000,
- 0.0f,
- 0.0f);
- }
- }
- // Get the center object of a frame definition. Return an empty selection
- // if it's missing or refers to an object that doesn't exist.
- static Selection
- getFrameCenter(const Universe& universe, Hash* frameData, const Selection& defaultCenter)
- {
- string centerName;
- if (!frameData->getString("Center", centerName))
- {
- if (defaultCenter.empty())
- cerr << "No center specified for reference frame.n";
- return defaultCenter;
- }
- Selection centerObject = universe.findPath(centerName, NULL, 0);
- if (centerObject.empty())
- {
- cerr << "Center object '" << centerName << "' of reference frame not found.n";
- return Selection();
- }
- // Should verify that center object is a star or planet, and
- // that it is a member of the same star system as the body in which
- // the frame will be used.
- return centerObject;
- }
- static BodyFixedFrame*
- CreateBodyFixedFrame(const Universe& universe,
- Hash* frameData,
- const Selection& defaultCenter)
- {
- Selection center = getFrameCenter(universe, frameData, defaultCenter);
- if (center.empty())
- return NULL;
- else
- return new BodyFixedFrame(center, center);
- }
- static BodyMeanEquatorFrame*
- CreateMeanEquatorFrame(const Universe& universe,
- Hash* frameData,
- const Selection& defaultCenter)
- {
- Selection center = getFrameCenter(universe, frameData, defaultCenter);
- if (center.empty())
- return NULL;
- Selection obj = center;
- string objName;
- if (frameData->getString("Object", objName))
- {
- obj = universe.findPath(objName, NULL, 0);
- if (obj.empty())
- {
- clog << "Object '" << objName << "' for mean equator frame not found.n";
- return NULL;
- }
- }
- clog << "CreateMeanEquatorFrame " << center.getName() << ", " << obj.getName() << "n";
- double freezeEpoch = 0.0;
- if (ParseDate(frameData, "Freeze", freezeEpoch))
- {
- return new BodyMeanEquatorFrame(center, obj, freezeEpoch);
- }
- else
- {
- return new BodyMeanEquatorFrame(center, obj);
- }
- }
- // Convert a string to an axis label. Permitted axis labels are
- // x, y, z, -x, -y, and -z. +x, +y, and +z are allowed as synonyms for
- // x, y, z. Case is ignored.
- static int
- parseAxisLabel(const std::string& label)
- {
- if (compareIgnoringCase(label, "x") == 0 ||
- compareIgnoringCase(label, "+x") == 0)
- {
- return 1;
- }
- if (compareIgnoringCase(label, "y") == 0 ||
- compareIgnoringCase(label, "+y") == 0)
- {
- return 2;
- }
- if (compareIgnoringCase(label, "z") == 0 ||
- compareIgnoringCase(label, "+z") == 0)
- {
- return 3;
- }
- if (compareIgnoringCase(label, "-x") == 0)
- {
- return -1;
- }
- if (compareIgnoringCase(label, "-y") == 0)
- {
- return -2;
- }
- if (compareIgnoringCase(label, "-z") == 0)
- {
- return -3;
- }
- return 0;
- }
- static int
- getAxis(Hash* vectorData)
- {
- string axisLabel;
- if (!vectorData->getString("Axis", axisLabel))
- {
- DPRINTF(0, "Bad two-vector frame: missing axis label for vector.n");
- return 0;
- }
- int axis = parseAxisLabel(axisLabel);
- if (axis == 0)
- {
- DPRINTF(0, "Bad two-vector frame: vector has invalid axis label.n");
- }
- // Permute axis labels to match non-standard Celestia coordinate
- // conventions: y <- z, z <- -y
- switch (axis)
- {
- case 2:
- return -3;
- case -2:
- return 3;
- case 3:
- return 2;
- case -3:
- return -2;
- default:
- return axis;
- }
- return axis;
- }
- // Get the target object of a direction vector definition. Return an
- // empty selection if it's missing or refers to an object that doesn't exist.
- static Selection
- getVectorTarget(const Universe& universe, Hash* vectorData)
- {
- string targetName;
- if (!vectorData->getString("Target", targetName))
- {
- clog << "Bad two-vector frame: no target specified for vector.n";
- return Selection();
- }
- Selection targetObject = universe.findPath(targetName, NULL, 0);
- if (targetObject.empty())
- {
- clog << "Bad two-vector frame: target object '" << targetName << "' of vector not found.n";
- return Selection();
- }
- return targetObject;
- }
- // Get the observer object of a direction vector definition. Return an
- // empty selection if it's missing or refers to an object that doesn't exist.
- static Selection
- getVectorObserver(const Universe& universe, Hash* vectorData)
- {
- string obsName;
- if (!vectorData->getString("Observer", obsName))
- {
- // Omission of observer is permitted; it will default to the
- // frame center.
- return Selection();
- }
- Selection obsObject = universe.findPath(obsName, NULL, 0);
- if (obsObject.empty())
- {
- clog << "Bad two-vector frame: observer object '" << obsObject.getName() << "' of vector not found.n";
- return Selection();
- }
- return obsObject;
- }
- static FrameVector*
- CreateFrameVector(const Universe& universe,
- const Selection& center,
- Hash* vectorData)
- {
- Value* value = NULL;
- value = vectorData->getValue("RelativePosition");
- if (value != NULL && value->getHash() != NULL)
- {
- Hash* relPosData = value->getHash();
- Selection observer = getVectorObserver(universe, relPosData);
- Selection target = getVectorTarget(universe, relPosData);
- // Default observer is the frame center
- if (observer.empty())
- observer = center;
- if (observer.empty() || target.empty())
- return NULL;
- else
- return new FrameVector(FrameVector::createRelativePositionVector(observer, target));
- }
- value = vectorData->getValue("RelativeVelocity");
- if (value != NULL && value->getHash() != NULL)
- {
- Hash* relVData = value->getHash();
- Selection observer = getVectorObserver(universe, relVData);
- Selection target = getVectorTarget(universe, relVData);
- // Default observer is the frame center
- if (observer.empty())
- observer = center;
- if (observer.empty() || target.empty())
- return NULL;
- else
- return new FrameVector(FrameVector::createRelativeVelocityVector(observer, target));
- }
- value = vectorData->getValue("ConstantVector");
- if (value != NULL && value->getHash() != NULL)
- {
- Hash* constVecData = value->getHash();
- Vec3d vec(0.0, 0.0, 1.0);
- constVecData->getVector("Vector", vec);
- if (vec.length() == 0.0)
- {
- clog << "Bad two-vector frame: constant vector has length zeron";
- return NULL;
- }
- vec.normalize();
- vec = Vec3d(vec.x, vec.z, -vec.y);
- // The frame for the vector is optional; a NULL frame indicates
- // J2000 ecliptic.
- ReferenceFrame* f = NULL;
- Value* frameValue = constVecData->getValue("Frame");
- if (frameValue != NULL)
- {
- f = CreateReferenceFrame(universe, frameValue, center, NULL);
- if (f == NULL)
- return NULL;
- }
- return new FrameVector(FrameVector::createConstantVector(vec, f));
- }
- else
- {
- clog << "Bad two-vector frame: unknown vector typen";
- return NULL;
- }
- }
- static TwoVectorFrame*
- CreateTwoVectorFrame(const Universe& universe,
- Hash* frameData,
- const Selection& defaultCenter)
- {
- Selection center = getFrameCenter(universe, frameData, defaultCenter);
- if (center.empty())
- return NULL;
- // Primary and secondary vector definitions are required
- Value* primaryValue = frameData->getValue("Primary");
- if (!primaryValue)
- {
- clog << "Primary axis missing from two-vector frame.n";
- return NULL;
- }
- Hash* primaryData = primaryValue->getHash();
- if (!primaryData)
- {
- clog << "Bad syntax for primary axis of two-vector frame.n";
- return NULL;
- }
- Value* secondaryValue = frameData->getValue("Secondary");
- if (!secondaryValue)
- {
- clog << "Secondary axis missing from two-vector frame.n";
- return NULL;
- }
- Hash* secondaryData = secondaryValue->getHash();
- if (!secondaryData)
- {
- clog << "Bad syntax for secondary axis of two-vector frame.n";
- return NULL;
- }
- // Get and validate the axes for the direction vectors
- int primaryAxis = getAxis(primaryData);
- int secondaryAxis = getAxis(secondaryData);
- assert(abs(primaryAxis) <= 3);
- assert(abs(secondaryAxis) <= 3);
- if (primaryAxis == 0 || secondaryAxis == 0)
- {
- return NULL;
- }
- if (abs(primaryAxis) == abs(secondaryAxis))
- {
- clog << "Bad two-vector frame: axes for vectors are collinear.n";
- return NULL;
- }
- FrameVector* primaryVector = CreateFrameVector(universe,
- center,
- primaryData);
- FrameVector* secondaryVector = CreateFrameVector(universe,
- center,
- secondaryData);
- TwoVectorFrame* frame = NULL;
- if (primaryVector != NULL && secondaryVector != NULL)
- {
- frame = new TwoVectorFrame(center,
- *primaryVector, primaryAxis,
- *secondaryVector, secondaryAxis);
- }
- delete primaryVector;
- delete secondaryVector;
- return frame;
- }
- static J2000EclipticFrame*
- CreateJ2000EclipticFrame(const Universe& universe,
- Hash* frameData,
- const Selection& defaultCenter)
- {
- Selection center = getFrameCenter(universe, frameData, defaultCenter);
-
- if (center.empty())
- return NULL;
- else
- return new J2000EclipticFrame(center);
- }
- static J2000EquatorFrame*
- CreateJ2000EquatorFrame(const Universe& universe,
- Hash* frameData,
- const Selection& defaultCenter)
- {
- Selection center = getFrameCenter(universe, frameData, defaultCenter);
-
- if (center.empty())
- return NULL;
- else
- return new J2000EquatorFrame(center);
- }
- /* Helper function for CreateTopocentricFrame().
- * Creates a two-vector frame with the specified center, target, and observer.
- */
- TwoVectorFrame*
- CreateTopocentricFrame(const Selection& center,
- const Selection& target,
- const Selection& observer)
- {
- BodyMeanEquatorFrame* eqFrame = new BodyMeanEquatorFrame(target, target);
- FrameVector north = FrameVector::createConstantVector(Vec3d(0.0, 1.0, 0.0), eqFrame);
- FrameVector up = FrameVector::createRelativePositionVector(observer, target);
-
- return new TwoVectorFrame(center, up, -2, north, -3);
- }
- /* Create a new Topocentric frame. The topocentric frame is designed to make it easy
- * to place objects on the surface of a planet or moon. The z-axis will point toward
- * the observer's zenith (which here is the direction away from the center of the
- * planet.) The x-axis will point in the local north direction. The equivalent
- * two-vector frame is:
- *
- * TwoVector
- * {
- * Center <center>
- * Primary
- * {
- * Axis "z"
- * RelativePosition { Target <target> Observer <observer> }
- * }
- * Secondary
- * {
- * Axis "x"
- * ConstantVector
- * {
- * Vector [ 0 0 1]
- * Frame { BodyFixed { Center <target> } }
- * }
- * }
- * }
- *
- * Typically, the topocentric frame is used as a BodyFrame to orient an
- * object on the surface of a planet. In this situation, the observer is
- * object itself and the target object is the planet. In fact, these are
- * the defaults: when no target, observer, or center is specified, the
- * observer and center are both 'self' and the target is the parent
- * object. Thus, for a Mars rover, using a topocentric frame is as simple
- * as:
- *
- * "Rover" "Sol/Mars"
- * {
- * BodyFrame { Topocentric { } }
- * ...
- * }
- */
- static TwoVectorFrame*
- CreateTopocentricFrame(const Universe& universe,
- Hash* frameData,
- const Selection& defaultTarget,
- const Selection& defaultObserver)
- {
- Selection target;
- Selection observer;
- Selection center;
- string centerName;
- if (frameData->getString("Center", centerName))
- {
- // If a center is provided, the default observer is the center and
- // the default target is the center's parent. This gives sensible results
- // when a topocentric frame is used as an orbit frame.
- center = universe.findPath(centerName, NULL, 0);
- if (center.empty())
- {
- cerr << "Center object '" << centerName << "' for topocentric frame not found.n";
- return NULL;
- }
- observer = center;
- target = center.parent();
- }
- else
- {
- // When no center is provided, use the default observer as the center. This
- // is typical when a topocentric frame is the body frame. The default observer
- // is usually the object itself.
- target = defaultTarget;
- observer = defaultObserver;
- center = defaultObserver;
- }
- string targetName;
- if (!frameData->getString("Target", targetName))
- {
- if (target.empty())
- {
- cerr << "No target specified for topocentric frame.n";
- return NULL;
- }
- }
- else
- {
- target = universe.findPath(targetName, NULL, 0);
- if (target.empty())
- {
- cerr << "Target object '" << targetName << "' for topocentric frame not found.n";
- return NULL;
- }
- // Should verify that center object is a star or planet, and
- // that it is a member of the same star system as the body in which
- // the frame will be used.
- }
- string observerName;
- if (!frameData->getString("Observer", observerName))
- {
- if (observer.empty())
- {
- cerr << "No observer specified for topocentric frame.n";
- return NULL;
- }
- }
- else
- {
- observer = universe.findPath(observerName, NULL, 0);
- if (observer.empty())
- {
- cerr << "Observer object '" << observerName << "' for topocentric frame not found.n";
- return NULL;
- }
- }
- return CreateTopocentricFrame(center, target, observer);
- }
- static ReferenceFrame*
- CreateComplexFrame(const Universe& universe, Hash* frameData, const Selection& defaultCenter, Body* defaultObserver)
- {
- Value* value = frameData->getValue("BodyFixed");
- if (value != NULL)
- {
- if (value->getType() != Value::HashType)
- {
- clog << "Object has incorrect body-fixed frame syntax.n";
- return NULL;
- }
- else
- {
- return CreateBodyFixedFrame(universe, value->getHash(), defaultCenter);
- }
- }
- value = frameData->getValue("MeanEquator");
- if (value != NULL)
- {
- if (value->getType() != Value::HashType)
- {
- clog << "Object has incorrect mean equator frame syntax.n";
- return NULL;
- }
- else
- {
- return CreateMeanEquatorFrame(universe, value->getHash(), defaultCenter);
- }
- }
- value = frameData->getValue("TwoVector");
- if (value != NULL)
- {
- if (value->getType() != Value::HashType)
- {
- clog << "Object has incorrect two-vector frame syntax.n";
- return NULL;
- }
- else
- {
- return CreateTwoVectorFrame(universe, value->getHash(), defaultCenter);
- }
- }
- value = frameData->getValue("Topocentric");
- if (value != NULL)
- {
- if (value->getType() != Value::HashType)
- {
- clog << "Object has incorrect topocentric frame syntax.n";
- return NULL;
- }
- else
- {
- return CreateTopocentricFrame(universe, value->getHash(), defaultCenter, Selection(defaultObserver));
- }
- }
- value = frameData->getValue("EclipticJ2000");
- if (value != NULL)
- {
- if (value->getType() != Value::HashType)
- {
- clog << "Object has incorrect J2000 ecliptic frame syntax.n";
- return NULL;
- }
- else
- {
- return CreateJ2000EclipticFrame(universe, value->getHash(), defaultCenter);
- }
- }
- value = frameData->getValue("EquatorJ2000");
- if (value != NULL)
- {
- if (value->getType() != Value::HashType)
- {
- clog << "Object has incorrect J2000 equator frame syntax.n";
- return NULL;
- }
- else
- {
- return CreateJ2000EquatorFrame(universe, value->getHash(), defaultCenter);
- }
- }
- clog << "Frame definition does not have a valid frame type.n";
- return NULL;
- }
- ReferenceFrame* CreateReferenceFrame(const Universe& universe,
- Value* frameValue,
- const Selection& defaultCenter,
- Body* defaultObserver)
- {
- if (frameValue->getType() == Value::StringType)
- {
- // TODO: handle named frames
- clog << "Invalid syntax for frame definition.n";
- return NULL;
- }
- else if (frameValue->getType() == Value::HashType)
- {
- return CreateComplexFrame(universe, frameValue->getHash(), defaultCenter, defaultObserver);
- }
- else
- {
- clog << "Invalid syntax for frame definition.n";
- return NULL;
- }
- }