diff --git a/data/scene/osirisrex.scene b/data/scene/osirisrex.scene index 8801be1d0e..cf5a8cd9f9 100644 --- a/data/scene/osirisrex.scene +++ b/data/scene/osirisrex.scene @@ -169,7 +169,7 @@ function postInitialization() openspace.setPropertyValue("OsirisRex.renderable.modelrotation", {90.0, 0.0, 0.0}) openspace.printInfo("Done setting default values") - openspace.loadMission("${OPENSPACE_DATA}/scene/osirisrex/osirisrex/osirisrex.mission") + openspace.loadMission("${OPENSPACE_DATA}/scene/osirisrex/osirisrex/spice_kernel_times.mission") openspace.resetCameraDirection() end @@ -184,16 +184,16 @@ return { }, Modules = { "sun", - "mercury", - "venus", + --"mercury", + --"venus", "lodearth", - "mars", - "saturn", - "uranus", - "neptune", - "stars", + --"mars", + --"saturn", + --"uranus", + --"neptune", + --"stars", -- "stars-denver", - "milkyway", + --"milkyway", -- "milkyway-eso", --"imageplane", "osirisrex", diff --git a/data/scene/osirisrex/osirisrex/spice_kernel_times.mission b/data/scene/osirisrex/osirisrex/spice_kernel_times.mission new file mode 100644 index 0000000000..4032141bd6 --- /dev/null +++ b/data/scene/osirisrex/osirisrex/spice_kernel_times.mission @@ -0,0 +1,95 @@ +return +{ Name = "Nominal_Observations_Science", Phases = { + { Name = "03_Approach", Phases = { + { Name = "DustSearch_v1", Phases = { + { Name = "Phase03_AP_DustSearch_1.bc", TimeRange = { Start = "2018-SEP-11 21:31:01.183", End = "2018-SEP-12 02:18:41.183" }}, + }}, + { Name = "LightCurve_v1", Phases = { + { Name = "Phase03_AP_LightCurve_1.bc", TimeRange = { Start = "2018-OCT-09 21:50:48.182", End = "2018-OCT-10 02:33:16.183" }}, + { Name = "Phase03_AP_LightCurve_2.bc", TimeRange = { Start = "2018-OCT-10 21:50:48.182", End = "2018-OCT-11 02:33:16.183" }}, + }}, + { Name = "NatSatSearch_v1", Phases = { + { Name = "Phase03_AP_SatSearch_1.bc", TimeRange = { Start = "2018-OCT-26 19:38:30.183", End = "2018-OCT-27 00:22:34.183" }}, + { Name = "Phase03_AP_SatSearch_2.bc", TimeRange = { Start = "2018-NOV-05 17:10:20.183", End = "2018-NOV-05 21:59:48.183" }}, + }}, + { Name = "PhaseFunction_v1", Phases = { + { Name = "Phase03_AP_PhaseFunction_1.bc", TimeRange = { Start = "2018-OCT-12 21:42:26.183", End = "2018-OCT-13 02:24:54.183" }}, + }}, + { Name = "ShapeModel_v1", Phases = { + { Name = "Phase03_AP_ShapeModel_1.bc", TimeRange = { Start = "2018-NOV-09 11:02:59.183", End = "2018-NOV-09 15:52:27.183" }}, + { Name = "Phase03_AP_ShapeModel_2.bc", TimeRange = { Start = "2018-NOV-10 11:02:59.183", End = "2018-NOV-10 15:52:27.183" }}, + { Name = "Phase03_AP_ShapeModel_3.bc", TimeRange = { Start = "2018-NOV-11 11:02:59.183", End = "2018-NOV-11 15:52:27.183" }}, + { Name = "Phase03_AP_ShapeModel_4.bc", TimeRange = { Start = "2018-NOV-12 11:02:59.183", End = "2018-NOV-12 15:52:27.183" }}, + { Name = "Phase03_AP_ShapeModel_5.bc", TimeRange = { Start = "2018-NOV-13 11:02:59.183", End = "2018-NOV-13 15:52:27.183" }}, + { Name = "Phase03_AP_ShapeModel_6.bc", TimeRange = { Start = "2018-NOV-14 11:03:53.183", End = "2018-NOV-14 15:51:33.183" }}, + { Name = "Phase03_AP_ShapeModel_7.bc", TimeRange = { Start = "2018-NOV-15 11:03:53.183", End = "2018-NOV-15 15:51:33.183" }}, + { Name = "Phase03_AP_ShapeModel_8.bc", TimeRange = { Start = "2018-NOV-16 11:03:53.183", End = "2018-NOV-16 15:51:33.183" }}, + { Name = "Phase03_AP_ShapeModel_9_Forced4x4.bc", TimeRange = { Start = "2018-NOV-17 11:03:54.183", End = "2018-NOV-17 15:51:34.183" }}, + }}, + { Name = "SpectraMap_v1", Phases = { + { Name = "Phase03_AP_SpectraMap_1.bc", TimeRange = { Start = "2018-OCT-30 20:44:53.183", End = "2018-OCT-31 01:34:21.183" }}, + }}, + }}, + { Name = "04_PrelimSurvey", Phases = { + { Name = "MapCamOLA_v1", Phases = { + { Name = "Phase04_PS_MC_1_v1_1a.bc", TimeRange = { Start = "2018-NOV-20 01:13:12.183", End = "2018-NOV-20 06:13:04.183" }}, + { Name = "Phase04_PS_MC_2_v1_1a.bc", TimeRange = { Start = "2018-NOV-28 01:13:12.183", End = "2018-NOV-28 06:13:04.183" }}, + }}, + { Name = "OLA_v1", Phases = { + { Name = "Phase04_PS_OLA_Nominal_1.bc", TimeRange = { Start = "2018-NOV-19 22:30:00.184", End = "2018-NOV-19 23:19:28.183" }}, + { Name = "Phase04_PS_OLA_Nominal_2.bc", TimeRange = { Start = "2018-NOV-23 22:19:34.184", End = "2018-NOV-23 23:19:26.183" }}, + { Name = "Phase04_PS_OLA_Nominal_3.bc", TimeRange = { Start = "2018-NOV-24 00:48:38.184", End = "2018-NOV-24 01:38:06.184" }}, + { Name = "Phase04_PS_OLA_Nominal_4.bc", TimeRange = { Start = "2018-NOV-27 22:29:58.184", End = "2018-NOV-27 23:19:26.183" }}, + }}, + { Name = "PolyCam_v1", Phases = { + { Name = "Phase04_PS_PolyCam_1.bc", TimeRange = { Start = "2018-NOV-19 12:00:33.183", End = "2018-NOV-19 16:46:25.183" }}, + { Name = "Phase04_PS_PolyCam_2.bc", TimeRange = { Start = "2018-NOV-20 07:10:26.183", End = "2018-NOV-20 12:10:18.183" }}, + { Name = "Phase04_PS_PolyCam_3.bc", TimeRange = { Start = "2018-NOV-23 11:51:29.184", End = "2018-NOV-23 16:51:21.184" }}, + { Name = "Phase04_PS_PolyCam_4.bc", TimeRange = { Start = "2018-NOV-24 07:17:39.184", End = "2018-NOV-24 12:03:31.184" }}, + { Name = "Phase04_PS_PolyCam_5.bc", TimeRange = { Start = "2018-NOV-27 12:00:20.184", End = "2018-NOV-27 16:46:12.184" }}, + { Name = "Phase04_PS_PolyCam_6.bc", TimeRange = { Start = "2018-NOV-28 07:10:35.183", End = "2018-NOV-28 12:10:27.183" }}, + }}, + }}, + { Name = "06_DetailedSurvey", Phases = { + { Name = "BaseballDiamond_v2", Phases = { + { Name = "atl_19013_18_BBD1_v2.bc", TimeRange = { Start = "2019-JAN-13 18:59:31.195", End = "2019-JAN-13 23:59:29.179" }}, + { Name = "atl_19014_16_BBD2_v2.bc", TimeRange = { Start = "2019-JAN-14 16:56:01.185", End = "2019-JAN-14 21:55:58.219" }}, + { Name = "atl_19020_18_BBD3_v2.bc", TimeRange = { Start = "2019-JAN-20 18:59:15.211", End = "2019-JAN-20 23:59:13.195" }}, + { Name = "atl_19021_19_BBD4_v2.bc", TimeRange = { Start = "2019-JAN-21 19:26:47.179", End = "2019-JAN-22 00:26:44.213" }}, + }}, + { Name = "EquatorialStations_v1", Phases = { + { Name = "Phase06_DS_Equatorial_Stations_1.bc", TimeRange = { Start = "2019-JAN-27 10:36:24.185", End = "2019-JAN-27 15:20:28.185" }}, + { Name = "Phase06_DS_Equatorial_Stations_2.bc", TimeRange = { Start = "2019-FEB-03 10:35:30.185", End = "2019-FEB-03 15:21:22.185" }}, + { Name = "Phase06_DS_Equatorial_Stations_3.bc", TimeRange = { Start = "2019-FEB-10 10:51:50.185", End = "2019-FEB-10 15:51:42.185" }}, + { Name = "Phase06_DS_Equatorial_Stations_4.bc", TimeRange = { Start = "2019-FEB-17 10:29:11.186", End = "2019-FEB-17 15:29:03.186" }}, + { Name = "Phase06_DS_Equatorial_Stations_5.bc", TimeRange = { Start = "2019-FEB-24 10:08:28.186", End = "2019-FEB-24 15:08:20.185" }}, + { Name = "Phase06_DS_Equatorial_Stations_6.bc", TimeRange = { Start = "2019-MAR-03 09:52:58.186", End = "2019-MAR-03 14:42:26.186" }}, + { Name = "Phase06_DS_Equatorial_Stations_7.bc", TimeRange = { Start = "2019-MAR-10 09:57:47.186", End = "2019-MAR-10 14:36:33.186" }}, + }}, + { Name = "PlumeSearch_v1", Phases = { + { Name = "Phase06_DS_Plume_Search_1.bc", TimeRange = { Start = "2019-JAN-28 10:34:36.185", End = "2019-JAN-28 15:22:16.185" }}, + { Name = "Phase06_DS_Plume_Search_2.bc", TimeRange = { Start = "2019-FEB-18 10:29:11.186", End = "2019-FEB-18 15:29:03.186" }}, + }}, + }}, + { Name = "07_OrbitalB", Phases = { + { Name = "CandidateSampleSite_v1", Phases = { + { Name = "Phase07_OB_CSS_Mapping_1.bc", TimeRange = { Start = "2019-APR-08 10:35:27.186", End = "2019-APR-08 15:22:06.186" }}, + { Name = "Phase07_OB_CSS_Mapping_2.bc", TimeRange = { Start = "2019-APR-08 16:16:06.186", End = "2019-APR-11 10:38:58.186" }}, + { Name = "Phase07_OB_CSS_Mapping_3.bc", TimeRange = { Start = "2019-APR-22 17:51:23.186", End = "2019-APR-29 19:41:03.186" }}, + }}, + }}, + { Name = "08_Recon", Phases = { + { Name = "225m_Sortie_v2", Phases = { + { Name = "Recon_225mSortie_Case02_0Latitude.bc", TimeRange = { Start = "2019-MAY-25 03:50:31.195", End = "2019-MAY-25 04:32:17.227" }}, + { Name = "Recon_225mSortie_Case05_20negLatitude.bc", TimeRange = { Start = "2019-MAY-25 03:50:48.216", End = "2019-MAY-25 04:37:10.209" }}, + { Name = "Recon_225mSortie_Case08_40negLatitude.bc", TimeRange = { Start = "2019-MAY-25 04:02:43.176", End = "2019-MAY-25 04:54:41.179" }}, + { Name = "Recon_225mSortie_Case11_60negLatitude.bc", TimeRange = { Start = "2019-MAY-25 04:21:46.161", End = "2019-MAY-25 05:18:44.232" }}, + }}, + { Name = "525m_Sortie_v2", Phases = { + { Name = "Recon_525mSortie_Case02_0Latitude.bc", TimeRange = { Start = "2019-MAY-25 04:06:39.220", End = "2019-MAY-25 04:44:17.198" }}, + { Name = "Recon_525mSortie_Case05_20negLatitude.bc", TimeRange = { Start = "2019-MAY-25 04:11:39.201", End = "2019-MAY-25 04:49:37.224" }}, + { Name = "Recon_525mSortie_Case05_NominalProfile.bc", TimeRange = { Start = "2019-MAY-25 03:01:50.184", End = "2019-MAY-25 06:38:50.232" }}, + { Name = "Recon_525mSortie_Case08_NominalProfile.bc", TimeRange = { Start = "2019-MAY-25 03:01:50.184", End = "2019-MAY-25 06:38:50.232" }}, + }}, + }}, +}} \ No newline at end of file diff --git a/modules/newhorizons/util/missionmanager.cpp b/modules/newhorizons/util/missionmanager.cpp index 7ff1f855d3..8b0b4af31c 100644 --- a/modules/newhorizons/util/missionmanager.cpp +++ b/modules/newhorizons/util/missionmanager.cpp @@ -98,7 +98,33 @@ MissionPhase::MissionPhase(const ghoul::Dictionary& dict) { } }; +std::list MissionPhase::phaseTrace(double time, int maxDepth) const { + std::list trace; + if (_timeRange.includes(time)) { + trace.push_back(this); + phaseTrace(time, trace, maxDepth); + } + return std::move(trace); +} +bool MissionPhase::phaseTrace(double time, std::list& trace, int maxDepth) const { + if (maxDepth == 0) { + return false; + } + + for (int i = 0; i < _subphases.size(); ++i) { + if (_subphases[i]._timeRange.includes(time)) { + trace.push_back(&_subphases[i]); + _subphases[i].phaseTrace(time, trace, maxDepth - 1); + return true; // only add the first one + } + // Since time ranges are sorted we can do early termination + else if (_subphases[i]._timeRange.start > time) { + return false; + } + } + return true; +} diff --git a/modules/newhorizons/util/missionmanager.h b/modules/newhorizons/util/missionmanager.h index 4f0fd29736..037a3aa465 100644 --- a/modules/newhorizons/util/missionmanager.h +++ b/modules/newhorizons/util/missionmanager.h @@ -27,6 +27,7 @@ #include +#include #include #include #include @@ -64,8 +65,13 @@ public: */ const MissionPhase& phase(size_t i) const { return _subphases[i]; } + std::list phaseTrace(double time, int maxDepth = -1) const; + protected: + bool phaseTrace(double time, std::list& trace, int maxDepth) const; + + std::string _name; std::string _description; TimeRange _timeRange; diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index ffacbd360b..4e327694c5 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -1267,12 +1267,12 @@ RenderEngine::RendererImplementation RenderEngine::rendererFromString(const std: std::string RenderEngine::progressToStr(int size, double t) { std::string progress = "|"; - int g = static_cast((t * 24) + 1); + int g = static_cast((t * (size - 1)) + 1); g = std::max(g, 0); for (int i = 0; i < g; i++) progress.append("-"); progress.append(">"); - for (int i = 0; i < 25 - g; i++) + for (int i = 0; i < size - g; i++) progress.append(" "); progress.append("|"); return progress; @@ -1370,60 +1370,75 @@ void RenderEngine::renderInformation() { const Mission& mission = MissionManager::ref().currentMission(); if (mission.phases().size() > 0) { - glm::vec4 inactiveColor(0.3, 0.3, 0.3, 1); - glm::vec4 nextMissionColor(0.3, 0.4, 0.7, 1); - glm::vec4 activeMissionColor(0.3, 0.6, 1.0, 1); + static const glm::vec4 nextMissionColor(0.7, 0.3, 0.3, 1); + //static const glm::vec4 missionProgressColor(0.4, 1.0, 1.0, 1); + static const glm::vec4 currentMissionColor(0.0, 0.5, 0.5, 1); + static const glm::vec4 missionProgressColor = currentMissionColor;// (0.4, 1.0, 1.0, 1); + static const glm::vec4 currentLeafMissionColor = missionProgressColor; + static const glm::vec4 nonCurrentMissionColor(0.3, 0.3, 0.3, 1); - size_t nextMissionPhaseIndex = -1; - size_t currentMissionPhaseIndex = -1; - - for (size_t i = 0; i < mission.phases().size(); i++) { - if (mission.phase(i).timeRange().includes(currentTime)) { - RenderFontCr(*_fontInfo, penPosition, activeMissionColor, "Current Mission Phase Progress:" ); - double remaining = mission.phase(i).timeRange().end - currentTime; - float t = static_cast(1.0 - remaining / mission.phase(i).timeRange().duration()); - std::string progress = progressToStr(25, t); - RenderFontCr(*_fontInfo, - penPosition, - activeMissionColor, - "%s %.1f %%", - progress.c_str(), t * 100 - ); - currentMissionPhaseIndex = i; - break; - } - else if (mission.phase(i).timeRange().start > currentTime) { - RenderFontCr(*_fontInfo, penPosition, nextMissionColor, "Next Mission Phase:"); - if (i > 0) { - double remaining = mission.phase(i).timeRange().start - currentTime; - double duration = mission.phase(i).timeRange().start - mission.phase(i - 1).timeRange().end; - float t = static_cast(1.0 - remaining / duration); - std::string progress = progressToStr(25, t); - RenderFontCr(*_fontInfo, - penPosition, - nextMissionColor, - "%s %.1f %%", - progress.c_str(), t * 100 - ); - } - nextMissionPhaseIndex = i; - break; - } + int maxDepth = 2; + std::list phaseTrace = mission.phaseTrace(currentTime, maxDepth); + + if (phaseTrace.size()) { + std::string title = "Current Mission Phase: " + phaseTrace.back()->name(); + RenderFontCr(*_fontInfo, penPosition, missionProgressColor, title.c_str()); + double remaining = phaseTrace.back()->timeRange().end - currentTime; + float t = static_cast(1.0 - remaining / phaseTrace.back()->timeRange().duration()); + std::string progress = progressToStr(25, t); + //RenderFontCr(*_fontInfo, penPosition, missionProgressColor, + // "%.0f s %s %.1f %%", remaining, progress.c_str(), t * 100); } + else { + RenderFontCr(*_fontInfo, penPosition, nextMissionColor, "Next Mission:"); + double remaining = mission.timeRange().start - currentTime; + RenderFontCr(*_fontInfo, penPosition, nextMissionColor, + "%.0f s", remaining); + } + + bool showAllPhases = false; - for (size_t i = 0; i < mission.phases().size(); i++) { - if (i == currentMissionPhaseIndex || i == nextMissionPhaseIndex) { - RenderFontCr(*_fontInfo, - penPosition, - currentMissionPhaseIndex != -1 ? activeMissionColor : nextMissionColor, - mission.phase(i).name().c_str() + typedef std::pair PhaseWithDepth; + std::stack S; + int pixelIndentation = 20; + S.push({ &mission, 0 }); + while (!S.empty()) { + const MissionPhase* phase = S.top().first; + int depth = S.top().second; + S.pop(); + + bool isCurrentPhase = phase->timeRange().includes(currentTime); + //bool isCurrentLeafPhase = phaseTrace.size() && phase == phaseTrace.back(); + glm::vec4 color = /*isCurrentLeafPhase ? currentLeafMissionColor :*/ isCurrentPhase ? currentMissionColor : nonCurrentMissionColor; + + + penPosition.x += depth * pixelIndentation; + if (isCurrentPhase) { + double remaining = phase->timeRange().end - currentTime; + float t = static_cast(1.0 - remaining / phase->timeRange().duration()); + std::string progress = progressToStr(25, t); + RenderFontCr(*_fontInfo, penPosition, color, + "%s %s %.1f %%", + phase->name().c_str(), + progress.c_str(), + t * 100 ); } else { - RenderFontCr(*_fontInfo, penPosition, inactiveColor, mission.phase(i).name().c_str()); + RenderFontCr(*_fontInfo, penPosition, color, phase->name().c_str()); + } + penPosition.x -= depth * pixelIndentation; + + if((isCurrentPhase || showAllPhases) && depth < maxDepth){ + // phases are sorted increasingly by start time, and will be popped + // last-in-first-out from the stack, so add them in reversed order. + int indexLastPhase = phase->phases().size() - 1; + for (int i = indexLastPhase; 0 <= i; --i) { + S.push({ &phase->phase(i), depth + 1 }); + } } } - RenderFontCr(*_fontInfo, penPosition, inactiveColor, " " ); + RenderFontCr(*_fontInfo, penPosition, nonCurrentMissionColor, " " ); } double remaining = openspace::ImageSequencer::ref().getNextCaptureTime() - currentTime; diff --git a/support/mission/ckbrief2mission.js b/support/mission/ckbrief2mission.js new file mode 100644 index 0000000000..20cf10add4 --- /dev/null +++ b/support/mission/ckbrief2mission.js @@ -0,0 +1,123 @@ +var USAGE_TEXT = '\n\ +USAGE:\n\ +node spice2mission.js [,] \n\ +\n\ +PURPOSE:\n\ +This script takes a root directory containing SPICE files (*.bc) \n\ +and prints lua code representing the contents for a .mission-file\n\ +to std out. The directory structure is used to create the hierarchy\n\ +of mission phases.\ +'; + +const exec = require('child_process').exec; + +var KEY_START = 'start'; +var KEY_END = 'end'; +var FILE_START = 'Summary for: '; +var TIME_START = 'Begin ET: '; +var SPACES = ' '; + +var inputDir = process.argv[2]; +var additionalArguments = process.argv.splice(3); + + +run(); + +function run(){ + if(process.argv.length == 2){ + console.log('Error: expected at least one (1) argument'); + console.log(USAGE_TEXT); + return; + } + + execute('find ' + inputDir + ' -type f -name *.bc*', (files) => { + files = files.split('\n').join(' '); + //console.log(files); + var cmd = "ckbrief.exe " + additionalArguments.join(" ") + " " + files + '-g'; + execute(cmd, handleCkbriefOutput); + }); +} + +function onSuccess(cb){ + return (error, stdout, stderr) => { + if(error) return console.error('exec error: ' + error); + if(stderr) return console.error(stderr); + cb(stdout); + } +} + +function execute(cmd, cb){ + //console.log('executing: ' + cmd); + exec(cmd, onSuccess(cb)); +} + +/** +* Handles the output from the shell command +* $ find -type f -name *.bc | xargs ckbrief.exe -g +*/ +function handleCkbriefOutput(output){ + var mission = {}; + var currentLeaf = null; + var lines = output.split('\n'); + for (var i = 0; i < lines.length; i++) { + var line = lines[i].trim(); + if(line.startsWith(FILE_START)){ + var pathToFile = line.substr(FILE_START.length); + //console.log(currentLeaf); + currentLeaf = addPathToTree(mission, pathToFile); + } + else if(line.startsWith(TIME_START)){ + var startTime = line.substr(TIME_START.length + 0, TIME_START.length + 14); + var endTime = line.substr(TIME_START.length + 34).trim(); + if(currentLeaf[KEY_START] === undefined) { + currentLeaf[KEY_START] = startTime; + } + currentLeaf[KEY_END] = endTime; + } + } + //console.log(JSON.stringify(mission, null, 2)); + + var luaStr = toLuaTable(mission, 0); + console.log(luaStr); +} + +function toLuaTable(mission, d){ + if(d === undefined) d = 0; + if(mission === undefined) return ''; + //if(d > 2) return ''; + var str = d ? "" : "return "; + + var indent = SPACES.substr(0, d*2); + for (var phaseName in mission) { + if (mission.hasOwnProperty(phaseName)) { + var phase = mission[phaseName]; + str += '\n' + indent + '{ Name = "' + phaseName + '", '; + if(phase[KEY_START] !== undefined && phase[KEY_END] !== undefined){ + str += 'TimeRange = { Start = "' + phase[KEY_START] + '", End = "' + phase[KEY_END] + '" }'; + } + else { + str += 'Phases = {' + toLuaTable(phase, d + 1) + '\n' + indent + '}'; + } + str += '},'; + } + } + + // strip out last ','. + return str.substr(0, str.length-1); +} + +function addPathToTree(obj, components){ + if(typeof components === 'string') { + components = components.split('/'); + } + + var o = obj; + var key; + for (var i = 0; i < components.length; i++) { + key = components[i]; + if(o[key] === undefined) o[key] = {}; + o = o[key]; + } + + return o; // returning the leaf component +} \ No newline at end of file