#pragma once #include #include // std::pair #include // rand() #include // std::sort class TimeWarp { public: struct Segment { float t_start, t_end; // входной интервал float y_start, y_end; // выходной интервал bool is_plateau; }; private: std::vector segments; public: TimeWarp(int numPlateaus, float minWidth, float maxWidth, unsigned int seed = 0) { if (seed != 0) std::srand(seed); std::vector> plateaus; int maxTries = 1000; int tries = 0; float totalPlateauWidth = 0; // Сгенерировать непересекающиеся плато while ((int)plateaus.size() < numPlateaus && tries++ < maxTries) { float width = randRange(minWidth, maxWidth); float start = randRange(0.0f, 1.0f - width); float end = start + width; bool overlaps = false; for (auto& p : plateaus) { if (!(end <= p.first || start >= p.second)) { overlaps = true; break; } } if (!overlaps) { plateaus.emplace_back(start, end); totalPlateauWidth += width; } } // Сортировать по началу std::sort(plateaus.begin(), plateaus.end()); // Собрать сегменты float currentT = 0.0f; float currentY = 0.0f; float dynamicSpan = 1.0f - totalPlateauWidth; for (auto& p : plateaus) { float start = p.first; float end = p.second; // Рамп перед плато if (start > currentT) { float t0 = currentT; float t1 = start; float ratio = (t1 - t0) / dynamicSpan; float y1 = currentY + ratio; segments.push_back({ t0, t1, currentY, y1, false }); currentY = y1; currentT = t1; } // Плато float width = end - start; segments.push_back({ start, end, currentY, currentY, true }); currentT = end; } // Рамп после последнего плато if (currentT < 1.0f) { float t0 = currentT; float t1 = 1.0f; float ratio = (t1 - t0) / dynamicSpan; float y1 = currentY + ratio; segments.push_back({ t0, t1, currentY, y1, false }); } } float apply(float t) const { for (const auto& seg : segments) { if (t >= seg.t_start && t <= seg.t_end) { if (seg.is_plateau) { return seg.y_start; } else { float localT = (t - seg.t_start) / (seg.t_end - seg.t_start); return seg.y_start + localT * (seg.y_end - seg.y_start); } } } return 1.0f; // если что-то пошло не так } private: float randRange(float minVal, float maxVal) { return minVal + (std::rand() / (float)RAND_MAX) * (maxVal - minVal); } };