diff --git a/source/EngineInterface/DataPointCollection.cpp b/source/EngineInterface/DataPointCollection.cpp index 4ddbf6066..afd080ff7 100644 --- a/source/EngineInterface/DataPointCollection.cpp +++ b/source/EngineInterface/DataPointCollection.cpp @@ -24,6 +24,7 @@ DataPointCollection DataPointCollection::operator+(DataPointCollection const& ot { DataPointCollection result; result.time = time + other.time; + result.systemClock = systemClock + other.systemClock; result.numCells = numCells + other.numCells; result.numSelfReplicators = numSelfReplicators + other.numSelfReplicators; result.numColonies = numColonies + other.numColonies; @@ -56,6 +57,7 @@ DataPointCollection DataPointCollection::operator/(double divisor) const { DataPointCollection result; result.time = time / divisor; + result.systemClock = systemClock / divisor; result.numCells = numCells / divisor; result.numSelfReplicators = numSelfReplicators / divisor; result.numColonies = numColonies / divisor; diff --git a/source/EngineInterface/DataPointCollection.h b/source/EngineInterface/DataPointCollection.h index c41537185..b2bf9689d 100644 --- a/source/EngineInterface/DataPointCollection.h +++ b/source/EngineInterface/DataPointCollection.h @@ -14,6 +14,7 @@ struct DataPoint struct DataPointCollection { double time; //could be a time step or real-time + double systemClock = 0; DataPoint numCells; DataPoint numSelfReplicators; diff --git a/source/EngineInterface/SerializerService.cpp b/source/EngineInterface/SerializerService.cpp index 2bea8547a..176304359 100644 --- a/source/EngineInterface/SerializerService.cpp +++ b/source/EngineInterface/SerializerService.cpp @@ -1256,6 +1256,7 @@ namespace loadSave(task, serializedData, 1 + 22 * 8, dataPoints.averageGenomeComplexity); loadSave(task, serializedData, 1 + 23 * 8, dataPoints.maxGenomeComplexityOfColonies); loadSave(task, serializedData, 1 + 24 * 8, dataPoints.varianceGenomeComplexity); + loadSave(task, serializedData, 1 + 25 * 8, dataPoints.systemClock); } } @@ -1294,6 +1295,7 @@ void SerializerService::serializeStatistics(StatisticsHistoryData const& statist writeLabelAllColors("Genome complexity average"); writeLabelAllColors("Genome complexity Maximum"); writeLabelAllColors("Genome complexity variance"); + writeLabelAllColors("System clock"); stream << std::endl; //content diff --git a/source/EngineInterface/StatisticsConverterService.cpp b/source/EngineInterface/StatisticsConverterService.cpp index 622048c21..77a6b4d44 100644 --- a/source/EngineInterface/StatisticsConverterService.cpp +++ b/source/EngineInterface/StatisticsConverterService.cpp @@ -1,5 +1,7 @@ #include "StatisticsConverterService.h" +#include + #include "Base/Definitions.h" namespace @@ -80,6 +82,11 @@ DataPointCollection StatisticsConverterService::convert( { DataPointCollection result; result.time = time; + + auto now = std::chrono::system_clock::now(); + auto unixEpoch = std::chrono::time_point(); + result.systemClock = toDouble(std::chrono::duration_cast(now - unixEpoch).count()); + result.numCells = getDataPointBySummation(data.timestep.numCells); result.numSelfReplicators = getDataPointBySummation(data.timestep.numSelfReplicators); result.numColonies = getDataPointBySummation(data.timestep.numColonies); diff --git a/source/Gui/StatisticsWindow.cpp b/source/Gui/StatisticsWindow.cpp index fa9dc93dd..7bc667b27 100644 --- a/source/Gui/StatisticsWindow.cpp +++ b/source/Gui/StatisticsWindow.cpp @@ -491,16 +491,17 @@ void _StatisticsWindow::processPlot(int row, DataPoint DataPointCollection::*val auto endTime = _mode == 0 ? dataPointCollectionHistory.back().time : longtermStatistics->back().time; auto values = _mode == 0 ? &(dataPointCollectionHistory[0].*valuesPtr) : &((*longtermStatistics)[0].*valuesPtr); auto timePoints = _mode == 0 ? &dataPointCollectionHistory[0].time : &(*longtermStatistics)[0].time; + auto systemClock = _mode == 0 ? nullptr : &(*longtermStatistics)[0].systemClock; switch (_plotType) { case 0: - plotSumColorsIntern(row, values, timePoints, count, startTime, endTime, fracPartDecimals); + plotSumColorsIntern(row, values, timePoints, systemClock, count, startTime, endTime, fracPartDecimals); break; case 1: plotByColorIntern(row, values, timePoints, count, startTime, endTime, fracPartDecimals); break; default: - plotForColorIntern(row, values, _plotType - 2, timePoints, count, startTime, endTime, fracPartDecimals); + plotForColorIntern(row, values, _plotType - 2, timePoints, systemClock, count, startTime, endTime, fracPartDecimals); break; } ImGui::Spacing(); @@ -538,6 +539,7 @@ void _StatisticsWindow::plotSumColorsIntern( int row, DataPoint const* dataPoints, double const* timePoints, + double const* systemClock, int count, double startTime, double endTime, @@ -573,7 +575,7 @@ void _StatisticsWindow::plotSumColorsIntern( ImPlot::PopStyleColor(); } if (ImGui::GetStyle().Alpha == 1.0f && ImPlot::IsPlotHovered() && count > 0) { - drawValuesAtMouseCursor(plotDataY, timePoints, count, startTime, endTime, upperBound, fracPartDecimals); + drawValuesAtMouseCursor(plotDataY, timePoints, systemClock, count, startTime, endTime, upperBound, fracPartDecimals); } ImPlot::EndPlot(); } @@ -633,6 +635,7 @@ void _StatisticsWindow::plotForColorIntern( DataPoint const* values, int colorIndex, double const* timePoints, + double const* systemClock, int count, double startTime, double endTime, @@ -668,7 +671,7 @@ void _StatisticsWindow::plotForColorIntern( ImPlot::PopStyleVar(); ImPlot::PopStyleColor(); if (ImGui::GetStyle().Alpha == 1.0f && ImPlot::IsPlotHovered()) { - drawValuesAtMouseCursor(valuesForColor, timePoints, count, startTime, endTime, upperBound, fracPartDecimals); + drawValuesAtMouseCursor(valuesForColor, timePoints, systemClock, count, startTime, endTime, upperBound, fracPartDecimals); } } ImPlot::EndPlot(); @@ -681,23 +684,42 @@ void _StatisticsWindow::plotForColorIntern( void _StatisticsWindow::drawValuesAtMouseCursor( double const* dataPoints, double const* timePoints, + double const* systemClock, int count, double startTime, double endTime, double upperBound, int fracPartDecimals) { + auto constexpr stride = sizeof(DataPointCollection) / sizeof(double); + auto mousePos = ImPlot::GetPlotMousePos(); mousePos.x = std::max(startTime, std::min(endTime, mousePos.x)); mousePos.y = dataPoints[0]; - auto constexpr stride = sizeof(DataPointCollection) / sizeof(double); - for (int i = 1; i < count; ++i) { - if (timePoints[i * stride] > mousePos.x) { - mousePos.y = dataPoints[i * stride]; - break; + + auto dateTimeString = + [&] { + if (systemClock == nullptr) { + for (int i = 1; i < count; ++i) { + if (timePoints[i * stride] > mousePos.x) { + mousePos.y = dataPoints[i * stride]; + break; + } + } + return std::string(); } - } - mousePos.y = std::max(0.0, std::min(upperBound, mousePos.y)); + auto systemClockPoint = systemClock[0]; + for (int i = 1; i < count; ++i) { + if (timePoints[i * stride] > mousePos.x) { + mousePos.y = dataPoints[i * stride]; + systemClockPoint = systemClock[i * stride]; + break; + } + } + mousePos.y = std::max(0.0, std::min(upperBound, mousePos.y)); + auto timePoint = std::chrono::floor(std::chrono::system_clock::from_time_t(static_cast(systemClockPoint))); + return systemClockPoint != 0 ? std::format("{:%Y-%m-%d %H:%M:%S}", timePoint) : std::string("-"); + }(); ImPlot::PushStyleColor(ImPlotCol_InlayText, ImColor::HSV(0.0f, 0.0f, 1.0f).Value); ImPlot::PlotText(ICON_FA_GENDERLESS, mousePos.x, mousePos.y, false, {scale(1.0f), scale(2.0f)}); @@ -709,9 +731,23 @@ void _StatisticsWindow::drawValuesAtMouseCursor( char label[256]; auto leftSideFactor = mousePos.x > (startTime + endTime) / 2 ? -1.0f : 1.0f; - snprintf( - label, sizeof(label), "Time: %s\nValue: %s", StringHelper::format(mousePos.x, 0).c_str(), StringHelper::format(mousePos.y, fracPartDecimals).c_str()); - ImPlot::PlotText(label, mousePos.x, upperBound, false, {leftSideFactor * (scale(5.0f) + ImGui::CalcTextSize(label).x / 2), scale(20.0f)}); + if (!dateTimeString.empty()) { + snprintf( + label, + sizeof(label), + "Time step: %s\nReal time: %s\nValue: %s", + StringHelper::format(mousePos.x, 0).c_str(), + dateTimeString.c_str(), + StringHelper::format(mousePos.y, fracPartDecimals).c_str()); + } else { + snprintf( + label, + sizeof(label), + "Relative time: %s\nValue: %s", + StringHelper::format(mousePos.x, 0).c_str(), + StringHelper::format(mousePos.y, fracPartDecimals).c_str()); + } + ImPlot::PlotText(label, mousePos.x, upperBound, false, {leftSideFactor * (scale(5.0f) + ImGui::CalcTextSize(label).x / 2), scale(28.0f)}); } void _StatisticsWindow::validationAndCorrection() diff --git a/source/Gui/StatisticsWindow.h b/source/Gui/StatisticsWindow.h index c2444ab2e..89d6b7a7c 100644 --- a/source/Gui/StatisticsWindow.h +++ b/source/Gui/StatisticsWindow.h @@ -32,13 +32,31 @@ class _StatisticsWindow : public _AlienWindow void processBackground() override; - void plotSumColorsIntern(int row, DataPoint const* dataPoints, double const* timePoints, int count, double startTime, double endTime, int fracPartDecimals); + void plotSumColorsIntern( + int row, + DataPoint const* dataPoints, + double const* timePoints, + double const* systemClock, + int count, + double startTime, + double endTime, + int fracPartDecimals); void plotByColorIntern(int row, DataPoint const* values, double const* timePoints, int count, double startTime, double endTime, int fracPartDecimals); - void plotForColorIntern(int row, DataPoint const* values, int colorIndex, double const* timePoints, int count, double startTime, double endTime, int fracPartDecimals); + void plotForColorIntern( + int row, + DataPoint const* values, + int colorIndex, + double const* timePoints, + double const* systemClock, + int count, + double startTime, + double endTime, + int fracPartDecimals); void drawValuesAtMouseCursor( double const* dataPoints, double const* timePoints, + double const* systemClock, int count, double startTime, double endTime,