#include "Data/DataSeriesUtils.h" #include Q_LOGGING_CATEGORY(LOG_DataSeriesUtils, "DataSeriesUtils") void DataSeriesUtils::fillDataHoles(std::vector &xAxisData, std::vector &valuesData, double resolution, double fillValue, double minBound, double maxBound) { if (resolution == 0. || std::isnan(resolution)) { qCWarning(LOG_DataSeriesUtils()) << "Can't fill data holes with a null resolution, no changes will be made"; return; } if (xAxisData.empty()) { qCWarning(LOG_DataSeriesUtils()) << "Can't fill data holes for empty data, no changes will be made"; return; } // Gets the number of values per x-axis data auto nbComponents = valuesData.size() / xAxisData.size(); // Generates fill values that will be used to complete values data std::vector fillValues(nbComponents, fillValue); // Checks if there are data holes on the beginning of the data and generates the hole at the // extremity if it's the case auto minXAxisData = xAxisData.front(); if (!std::isnan(minBound) && minBound < minXAxisData) { auto holeSize = static_cast((minXAxisData - minBound) / resolution); if (holeSize > 0) { xAxisData.insert(xAxisData.begin(), minXAxisData - holeSize * resolution); valuesData.insert(valuesData.begin(), fillValues.begin(), fillValues.end()); } } // Same for the end of the data auto maxXAxisData = xAxisData.back(); if (!std::isnan(maxBound) && maxBound > maxXAxisData) { auto holeSize = static_cast((maxBound - maxXAxisData) / resolution); if (holeSize > 0) { xAxisData.insert(xAxisData.end(), maxXAxisData + holeSize * resolution); valuesData.insert(valuesData.end(), fillValues.begin(), fillValues.end()); } } // Generates other data holes auto xAxisIt = xAxisData.begin(); while (xAxisIt != xAxisData.end()) { // Stops at first value which has a gap greater than resolution with the value next to it xAxisIt = std::adjacent_find( xAxisIt, xAxisData.end(), [resolution](const auto &a, const auto &b) { return (b - a) > resolution; }); if (xAxisIt != xAxisData.end()) { auto nextXAxisIt = xAxisIt + 1; // Gets the values that has a gap greater than resolution between them auto lowValue = *xAxisIt; auto highValue = *nextXAxisIt; // Completes holes between the two values by creating new values (according to the // resolution) for (auto i = lowValue + resolution; i < highValue; i += resolution) { // Gets the iterator of values data from which to insert fill values auto nextValuesIt = valuesData.begin() + std::distance(xAxisData.begin(), nextXAxisIt) * nbComponents; // New value is inserted before nextXAxisIt nextXAxisIt = xAxisData.insert(nextXAxisIt, i) + 1; // New values are inserted before nextValuesIt valuesData.insert(nextValuesIt, fillValues.begin(), fillValues.end()); } // Moves to the next value to continue loop on the x-axis data xAxisIt = nextXAxisIt; } } } DataSeriesUtils::Mesh DataSeriesUtils::regularMesh(DataSeriesIterator begin, DataSeriesIterator end, Resolution xResolution, Resolution yResolution) { // Checks preconditions if (xResolution.m_Val == 0. || std::isnan(xResolution.m_Val) || yResolution.m_Val == 0. || std::isnan(yResolution.m_Val)) { qCWarning(LOG_DataSeriesUtils()) << "Can't generate mesh with a null resolution"; return Mesh{}; } if (xResolution.m_Logarithmic) { qCWarning(LOG_DataSeriesUtils()) << "Can't generate mesh with a logarithmic x-axis resolution"; return Mesh{}; } if (std::distance(begin, end) == 0) { qCWarning(LOG_DataSeriesUtils()) << "Can't generate mesh for empty data"; return Mesh{}; } auto yData = begin->y(); if (yData.empty()) { qCWarning(LOG_DataSeriesUtils()) << "Can't generate mesh for data with no y-axis"; return Mesh{}; } // Converts y-axis and its resolution to logarithmic values if (yResolution.m_Logarithmic) { std::for_each(yData.begin(), yData.end(), [](auto &val) { val = std::log10(val); }); } }