diff --git a/src/entities/PathComponent.cpp b/src/entities/PathComponent.cpp index fea66a1d..2d6f25c8 100644 --- a/src/entities/PathComponent.cpp +++ b/src/entities/PathComponent.cpp @@ -62,14 +62,54 @@ void PathComponent::Begin() { animationPath_->clear(); - const std::vector& steps = timeStep_->GetSteps(); + // 优先使用路径文件的时间列;若不存在则按当前时间范围等间距生成时间点 + std::vector mappedSteps; + const bool hasPathTimes = transformPath_ && !transformPath_->GetTimes().empty(); + double rangeMin = 0.0, rangeMax = 0.0, rangeStep = 0.0; + timeStep_->GetRange(rangeMin, rangeMax, rangeStep); + if (hasPathTimes) { + const auto& times = transformPath_->GetTimes(); + if (timeStep_->HasManualRange()) { + // 将路径文件时间列线性映射到用户指定的手动区间,保证相对时间关系不变 + double srcMin = times.front(); + double srcMax = times.back(); + double dstMin = timeStep_->GetManualStart(); + double dstMax = timeStep_->GetManualEnd(); + double srcSpan = std::max(1e-9, srcMax - srcMin); + mappedSteps.reserve(times.size()); + for (double t : times) { + double alpha = (t - srcMin) / srcSpan; + mappedSteps.push_back(dstMin + alpha * (dstMax - dstMin)); + } + } else { + mappedSteps = times; + } + } else { + const std::vector& transforms = transformPath_->GetTransforms(); + int num = (int)transforms.size(); + if (num > 0) { + mappedSteps.resize(num); + double start = timeStep_->HasManualRange() ? timeStep_->GetManualStart() : rangeMin; + double end = timeStep_->HasManualRange() ? timeStep_->GetManualEnd() : rangeMax; + double step = rangeStep; + // 若步长无效,则回退为均匀分布 + if (step <= 0.0) { + step = (num > 1) ? std::max(0.0, (end - start)) / double(num - 1) : 0.0; + } + for (int i = 0; i < num; ++i) { + double t = start + step * double(i); + if (t > end) t = end; + mappedSteps[i] = t; + } + } + } const std::vector& transforms = transformPath_->GetTransforms(); - int num = std::min(steps.size(), transforms.size()); + int num = std::min(mappedSteps.size(), transforms.size()); for (int index = 0; index < num; ++index) { const Transform& transform = transforms[index]; osg::AnimationPath::ControlPoint controlPoint(transform.GetLocation() /*+ osg::Vec3(index * 10, 0, 0)*/, OsgUtils::HPRToQuat(transform.GetRotation()), transform.GetScale()); - animationPath_->insert(steps[index], controlPoint); + animationPath_->insert(mappedSteps[index], controlPoint); } deltaTime_ = 0; diff --git a/src/ui/Menu/PlayManagerMenu.cpp b/src/ui/Menu/PlayManagerMenu.cpp index c79ae567..77fe6401 100644 --- a/src/ui/Menu/PlayManagerMenu.cpp +++ b/src/ui/Menu/PlayManagerMenu.cpp @@ -139,6 +139,16 @@ void PlayManagerMenu::OnTimestepChanged(Timestep* timestep) { ui->lbUp->setText(QString("x%1").arg(step)); } ); + // 当时间范围更新(例如用户在属性面板设置了起止时间)时,刷新显示与滑条范围 + connect(timestep, &Timestep::RangeChanged, [this](double minTime, double maxTime, double step) { + minTime_ = minTime; + maxTime_ = maxTime; + ui->lbtime->setText(QString::number(minTime_, 'f', 3)); + ui->lbtimeTotal->setText(QString("/%1(s)").arg(maxTime_)); + ui->lbUp->setText(QString("x%1").arg(step)); + ui->horizontalSlider->setRange((int)(minTime_ * 1000), (int)(maxTime_ * 1000)); + ui->horizontalSlider->setValue((int)(minTime_ * 1000)); + }); connect(timestep, &Timestep::StatusChanged, [this](int statue) { Timestep::PlayStatus state = static_cast(statue); diff --git a/src/ui/PropertyBrowser/qtpropertymanager.cpp b/src/ui/PropertyBrowser/qtpropertymanager.cpp index fccaf477..b2c485bd 100644 --- a/src/ui/PropertyBrowser/qtpropertymanager.cpp +++ b/src/ui/PropertyBrowser/qtpropertymanager.cpp @@ -7916,7 +7916,6 @@ public: QMap m_properyToName; QMap m_properyToDescription; - QMap m_properyToTimestep; QMap m_properyToSimMatlab; QMap m_properyToMatlabParam; QMap m_properyToWavePath; @@ -7926,7 +7925,6 @@ public: QMap m_nameToPropery; QMap m_descriptionToPropery; - QMap m_timestepToPropery; QMap m_simMatlabToPropery; QMap m_matlabParamToPropery; QMap m_wavePathToPropery; @@ -7951,6 +7949,16 @@ public: QMap m_properyToHomeRange; QMap m_homeRangeToPropery; + // Simulation Time group (manual range) + QMap m_properyToSimTimeGroup; + QMap m_simTimeGroupToPropery; + QMap m_properyToSimStart; + QMap m_simStartToPropery; + QMap m_properyToSimEnd; + QMap m_simEndToPropery; + QMap m_properyToSimStep; + QMap m_simStepToPropery; + // Grouped file entries: Curve QMap m_properyToCurveGroup; QMap m_curveGroupToPropery; @@ -8015,10 +8023,6 @@ void QtWorkspacePropertyManagerPrivate::slotStringChanged(QtProperty* property, QWorkspaceAttribute c = m_values[prop]; c.SetDescription(value); q_ptr->setValue(prop, c); - } else if (QtProperty* prop = m_timestepToPropery.value(property, 0)) { - QWorkspaceAttribute c = m_values[prop]; - c.SetTimeStep(value); - q_ptr->setValue(prop, c); } else if (QtProperty* prop = m_commondPathToPropery.value(property, 0)) { QWorkspaceAttribute c = m_values[prop]; c.SetCommondFilePath(value); @@ -8145,6 +8149,18 @@ void QtWorkspacePropertyManagerPrivate::slotDoubleChanged(QtProperty* property, QWorkspaceAttribute c = m_values[prop]; c.SetHomeViewpointRange(value); q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_simStartToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + c.SetSimulationStart(value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_simEndToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + c.SetSimulationEnd(value); + q_ptr->setValue(prop, c); + } else if (QtProperty* prop = m_simStepToPropery.value(property, 0)) { + QWorkspaceAttribute c = m_values[prop]; + c.SetSimulationStep(value); + q_ptr->setValue(prop, c); } } @@ -8159,10 +8175,7 @@ void QtWorkspacePropertyManagerPrivate::slotPropertyDestroyed(QtProperty* proper m_descriptionToPropery.remove(property); } - if (QtProperty* subProp = m_timestepToPropery.value(property, nullptr)) { - m_timestepToPropery[subProp] = 0; - m_timestepToPropery.remove(property); - } + // Removed: timestep property mapping cleanup (no longer tracked) if (QtProperty* subProp = m_simMatlabToPropery.value(property, nullptr)) { m_simMatlabToPropery[subProp] = 0; @@ -8184,6 +8197,44 @@ void QtWorkspacePropertyManagerPrivate::slotPropertyDestroyed(QtProperty* proper m_rdPathToPropery[subProp] = 0; m_rdPathToPropery.remove(property); } + // HomeViewpoint numeric children + if (QtProperty* subProp = m_homeLonToPropery.value(property, nullptr)) { + m_homeLonToPropery[subProp] = 0; + m_homeLonToPropery.remove(property); + } + if (QtProperty* subProp = m_homeLatToPropery.value(property, nullptr)) { + m_homeLatToPropery[subProp] = 0; + m_homeLatToPropery.remove(property); + } + if (QtProperty* subProp = m_homeAltToPropery.value(property, nullptr)) { + m_homeAltToPropery[subProp] = 0; + m_homeAltToPropery.remove(property); + } + if (QtProperty* subProp = m_homeHeadingToPropery.value(property, nullptr)) { + m_homeHeadingToPropery[subProp] = 0; + m_homeHeadingToPropery.remove(property); + } + if (QtProperty* subProp = m_homePitchToPropery.value(property, nullptr)) { + m_homePitchToPropery[subProp] = 0; + m_homePitchToPropery.remove(property); + } + if (QtProperty* subProp = m_homeRangeToPropery.value(property, nullptr)) { + m_homeRangeToPropery[subProp] = 0; + m_homeRangeToPropery.remove(property); + } + // Simulation Time children + if (QtProperty* subProp = m_simStartToPropery.value(property, nullptr)) { + m_simStartToPropery[subProp] = 0; + m_simStartToPropery.remove(property); + } + if (QtProperty* subProp = m_simEndToPropery.value(property, nullptr)) { + m_simEndToPropery[subProp] = 0; + m_simEndToPropery.remove(property); + } + if (QtProperty* subProp = m_simStepToPropery.value(property, nullptr)) { + m_simStepToPropery[subProp] = 0; + m_simStepToPropery.remove(property); + } } QtWorkspacePropertyManager::QtWorkspacePropertyManager(QObject* parent) @@ -8286,7 +8337,6 @@ void QtWorkspacePropertyManager::setValue(QtProperty* property, const QWorkspace d_ptr->m_stringProperyManager->setValue(d_ptr->m_properyToName[property], value.GetName()); d_ptr->m_stringProperyManager->setValue(d_ptr->m_properyToDescription[property], value.GetDescription()); - d_ptr->m_filesProperyManager->setValue(d_ptr->m_properyToTimestep[property], value.GetTimeStep()); d_ptr->m_filesProperyManager->setValue(d_ptr->m_properyToCommondPath[property], value.GetCommondFilePath()); // Sync HomeViewpoint numeric fields if (QtProperty* p = d_ptr->m_properyToHomeLon.value(property, nullptr)) @@ -8301,6 +8351,13 @@ void QtWorkspacePropertyManager::setValue(QtProperty* property, const QWorkspace d_ptr->m_doubleProperyManager->setValueOnly(p, value.GetHomeViewpointPitch()); if (QtProperty* p = d_ptr->m_properyToHomeRange.value(property, nullptr)) d_ptr->m_doubleProperyManager->setValueOnly(p, value.GetHomeViewpointRange()); + // Sync Simulation Time Start/End + if (QtProperty* p = d_ptr->m_properyToSimStart.value(property, nullptr)) + d_ptr->m_doubleProperyManager->setValueOnly(p, value.GetSimulationStart()); + if (QtProperty* p = d_ptr->m_properyToSimEnd.value(property, nullptr)) + d_ptr->m_doubleProperyManager->setValueOnly(p, value.GetSimulationEnd()); + if (QtProperty* p = d_ptr->m_properyToSimStep.value(property, nullptr)) + d_ptr->m_doubleProperyManager->setValueOnly(p, value.GetSimulationStep()); auto syncGroup = [&](FileEntryType type, QMap& propToGroup, @@ -8383,12 +8440,7 @@ void QtWorkspacePropertyManager::initializeProperty(QtProperty* property) { d_ptr->m_descriptionToPropery[prop] = property; property->addSubProperty(prop); - prop = d_ptr->m_filesProperyManager->addProperty(); - prop->setPropertyName(tr("Timestep")); - d_ptr->m_filesProperyManager->setValueOnly(prop, val.GetTimeStep()); - d_ptr->m_properyToTimestep[property] = prop; - d_ptr->m_timestepToPropery[prop] = property; - property->addSubProperty(prop); + // Removed: Timestep file path property (no longer file-based) // Deprecated properties (SimMatlab/MatlabParam/WavePath/ReportPath/RDPath) removed from UI @@ -8435,6 +8487,41 @@ void QtWorkspacePropertyManager::initializeProperty(QtProperty* property) { addDouble(tr("Range"), val.GetHomeViewpointRange(), d_ptr->m_properyToHomeRange, d_ptr->m_homeRangeToPropery, 0.0, 1000000.0, 2, 10.0); + // Simulation Time group (English labels) + QtProperty* simGroup = d_ptr->m_groupProperyManager->addProperty(); + simGroup->setPropertyName(tr("Simulation Time")); + d_ptr->m_properyToSimTimeGroup[property] = simGroup; + d_ptr->m_simTimeGroupToPropery[simGroup] = property; + property->addSubProperty(simGroup); + + auto addSimDouble = [&](const QString& name, double init, + QMap& propToChild, + QMap& childToProp) { + QtProperty* dp = d_ptr->m_doubleProperyManager->addProperty(); + dp->setPropertyName(name); + d_ptr->m_doubleProperyManager->setValueOnly(dp, init); + d_ptr->m_doubleProperyManager->setRange(dp, -1e12, 1e12); + d_ptr->m_doubleProperyManager->setDecimals(dp, 6); + d_ptr->m_doubleProperyManager->setSingleStep(dp, 0.1); + simGroup->addSubProperty(dp); + propToChild[property] = dp; + childToProp[dp] = property; + }; + addSimDouble(tr("Simulation Start"), val.GetSimulationStart(), d_ptr->m_properyToSimStart, d_ptr->m_simStartToPropery); + addSimDouble(tr("Simulation End"), val.GetSimulationEnd(), d_ptr->m_properyToSimEnd, d_ptr->m_simEndToPropery); + // Simulation Step (playback speed multiplier) + { + QtProperty* dp = d_ptr->m_doubleProperyManager->addProperty(); + dp->setPropertyName(tr("Simulation Step")); + d_ptr->m_doubleProperyManager->setValueOnly(dp, val.GetSimulationStep()); + d_ptr->m_doubleProperyManager->setRange(dp, 0.25, 8.0); + d_ptr->m_doubleProperyManager->setDecimals(dp, 2); + d_ptr->m_doubleProperyManager->setSingleStep(dp, 0.25); + simGroup->addSubProperty(dp); + d_ptr->m_properyToSimStep[property] = dp; + d_ptr->m_simStepToPropery[dp] = property; + } + // Add grouped file sections #if 0 // Hide resource groups (Curves/Surfaces/Tables/Lights/Polars/Images) in workspace property box auto addGroup = [&](FileEntryType type, const QString& groupName, @@ -8523,12 +8610,7 @@ void QtWorkspacePropertyManager::uninitializeProperty(QtProperty* property) { } d_ptr->m_properyToDescription.remove(property); - prop = d_ptr->m_timestepToPropery[property]; - if (prop) { - d_ptr->m_timestepToPropery.remove(prop); - delete prop; - } - d_ptr->m_properyToTimestep.remove(property); + // Removed: Timestep file path cleanup prop = d_ptr->m_simMatlabToPropery[property]; if (prop) { @@ -8597,6 +8679,26 @@ void QtWorkspacePropertyManager::uninitializeProperty(QtProperty* property) { } d_ptr->m_properyToHomeViewGroup.remove(property); + // Simulation Time cleanup + auto removeSimChild = [&](QMap& propToChild, + QMap& childToProp) { + QtProperty* child = propToChild.value(property, nullptr); + if (child) { + childToProp.remove(child); + delete child; + } + propToChild.remove(property); + }; + removeSimChild(d_ptr->m_properyToSimStart, d_ptr->m_simStartToPropery); + removeSimChild(d_ptr->m_properyToSimEnd, d_ptr->m_simEndToPropery); + removeSimChild(d_ptr->m_properyToSimStep, d_ptr->m_simStepToPropery); + QtProperty* simGroup = d_ptr->m_properyToSimTimeGroup.value(property, nullptr); + if (simGroup) { + d_ptr->m_simTimeGroupToPropery.remove(simGroup); + delete simGroup; + } + d_ptr->m_properyToSimTimeGroup.remove(property); + // Cleanup grouped file properties auto cleanupGroup = [&](QMap& propToGroup, QMap& groupToProp, diff --git a/src/ui/PropertyBrowser/qtworkspaceattribute.cpp b/src/ui/PropertyBrowser/qtworkspaceattribute.cpp index e133146f..7be4d3b4 100644 --- a/src/ui/PropertyBrowser/qtworkspaceattribute.cpp +++ b/src/ui/PropertyBrowser/qtworkspaceattribute.cpp @@ -62,35 +62,65 @@ const QString QWorkspaceAttribute::GetDescription() const { } -void QWorkspaceAttribute::SetTimeStep(const QString& timestep) { - if (nullptr == workspace_) { - return; - } +// 已移除:时间步长文件路径设置/获取(不再从文件读取) - Timestep* obj = workspace_->GetTimestep(); - if (nullptr == obj) { - workspace_->SetTimestepPath(timestep); - return; - } - - const QString& path = obj->GetPath(); - if (path == timestep) { - return; - } - - workspace_->SetTimestepPath(timestep); +void QWorkspaceAttribute::SetSimulationStart(double start) { + if (!workspace_) return; + Timestep* t = workspace_->GetTimestep(); + if (!t) return; + double minTime = 0.0, maxTime = 0.0, step = 0.0; + t->GetRange(minTime, maxTime, step); + t->SetManualRange(start, maxTime); } -const QString QWorkspaceAttribute::GetTimeStep() const { - if (nullptr == workspace_) { - return ""; - } +double QWorkspaceAttribute::GetSimulationStart() const { + if (!workspace_) return 0.0; + Timestep* t = workspace_->GetTimestep(); + if (!t) return 0.0; + double minTime = 0.0, maxTime = 0.0, step = 0.0; + t->GetRange(minTime, maxTime, step); + return minTime; +} - Timestep* timestep = workspace_->GetTimestep(); - if (nullptr == timestep) { - return ""; - } - return timestep->GetPath(); +void QWorkspaceAttribute::SetSimulationEnd(double end) { + if (!workspace_) return; + Timestep* t = workspace_->GetTimestep(); + if (!t) return; + double minTime = 0.0, maxTime = 0.0, step = 0.0; + t->GetRange(minTime, maxTime, step); + t->SetManualRange(minTime, end); +} + +double QWorkspaceAttribute::GetSimulationEnd() const { + if (!workspace_) return 0.0; + Timestep* t = workspace_->GetTimestep(); + if (!t) return 0.0; + double minTime = 0.0, maxTime = 0.0, step = 0.0; + t->GetRange(minTime, maxTime, step); + return maxTime; +} + +void QWorkspaceAttribute::SetSimulationRange(double start, double end) { + if (!workspace_) return; + Timestep* t = workspace_->GetTimestep(); + if (!t) return; + t->SetManualRange(start, end); +} + +void QWorkspaceAttribute::SetSimulationStep(double step) { + if (!workspace_) return; + Timestep* t = workspace_->GetTimestep(); + if (!t) return; + t->SetSpeed(step); +} + +double QWorkspaceAttribute::GetSimulationStep() const { + if (!workspace_) return 1.0; + Timestep* t = workspace_->GetTimestep(); + if (!t) return 1.0; + double minTime = 0.0, maxTime = 0.0, step = 0.0; + t->GetRange(minTime, maxTime, step); + return step; } // Deprecated workspace path setters/getters removed diff --git a/src/ui/PropertyBrowser/qtworkspaceattribute.h b/src/ui/PropertyBrowser/qtworkspaceattribute.h index 66d697f9..a97996e0 100644 --- a/src/ui/PropertyBrowser/qtworkspaceattribute.h +++ b/src/ui/PropertyBrowser/qtworkspaceattribute.h @@ -62,8 +62,17 @@ public: void SetDescription(const QString& desc); const QString GetDescription() const; - void SetTimeStep(const QString& timestep); - const QString GetTimeStep() const; + // 已移除:时间步长文件路径(不再从文件读取) + + // Simulation time (manual range) for playback + void SetSimulationStart(double start); + double GetSimulationStart() const; + void SetSimulationEnd(double end); + double GetSimulationEnd() const; + void SetSimulationRange(double start, double end); + // Playback speed (step multiplier) + void SetSimulationStep(double step); + double GetSimulationStep() const; // Deprecated fields removed: SimMatlab/MatlabParam/WavePath/ReportPath/RDPath diff --git a/src/utils/TransformPath.cpp b/src/utils/TransformPath.cpp index 46cb4540..336cad0d 100644 --- a/src/utils/TransformPath.cpp +++ b/src/utils/TransformPath.cpp @@ -36,6 +36,8 @@ TransformPath* TransformPath::LoadFromFile(const QString& path, QObject* parent) std::vector transforms; + std::vector times; + while (!in.atEnd()) { QString line = in.readLine(); if (line.trimmed().isEmpty()) { @@ -67,11 +69,14 @@ TransformPath* TransformPath::LoadFromFile(const QString& path, QObject* parent) transform.GetRotation().set(pitch, roll, heading); // 当前 PathComponent 使用 WorkSpace 的 Timestep 作为时间轴,这里暂不保存 time 字段 - // 若未来需要按文件时间驱动,可扩展 TransformPath 结构以记录时间。 + // 现在记录时间以支持按路径文件驱动回放 + times.push_back(time); transforms.push_back(transform); } file.close(); - return new TransformPath(std::move(transforms), parent); + TransformPath* tp = new TransformPath(std::move(transforms), parent); + tp->SetTimes(std::move(times)); + return tp; } diff --git a/src/utils/TransformPath.h b/src/utils/TransformPath.h index 3084e812..90dc2d6d 100644 --- a/src/utils/TransformPath.h +++ b/src/utils/TransformPath.h @@ -21,6 +21,15 @@ public: return transforms_; } + const std::vector& GetTimes() const { + return times_; + } + + void SetTimes(std::vector times) { + times_ = std::move(times); + } + private: std::vector transforms_; + std::vector times_; }; \ No newline at end of file diff --git a/src/workspace/Timestep.cpp b/src/workspace/Timestep.cpp index 9e135ecb..fff2408c 100644 --- a/src/workspace/Timestep.cpp +++ b/src/workspace/Timestep.cpp @@ -1,6 +1,8 @@ #include "workspace/Timestep.h" #include +#include +#include #include #include @@ -10,51 +12,47 @@ #include "common/RecourceHelper.h" #include "common/SpdLogger.h" -Timestep::Timestep(const std::vector& steps, const QString& path, WorkSpace* parent /*= nullptr*/) noexcept +Timestep::Timestep(WorkSpace* parent /*= nullptr*/) noexcept : QObject((QObject*)parent) - , steps_(steps) - , path_(path) , workSpace_(parent) { - maxTime_ = *steps_.rbegin(); + // 默认最大时间为 0,由数据或手动区间设置 // 初始化为 1.0x,如果列表中没有 1.0,则取最接近中间的默认索引 auto it = std::find(speedLevels_.begin(), speedLevels_.end(), 1.0); speedIndex_ = it != speedLevels_.end() ? int(std::distance(speedLevels_.begin(), it)) : speedIndex_; currentStep_ = speedLevels_[speedIndex_]; } -Timestep* Timestep::Load(const QString& path, WorkSpace* workSpace) { - const QString filePath = QString("%1/%2").arg(workSpace->GetDir()).arg(path); - LOG_INFO("Load timestep: {}", filePath.toStdString()); +void Timestep::SetManualRange(double start, double end) { + hasManualRange_ = true; + manualStart_ = start; + manualEnd_ = end; + // 当设置手动区间时,更新最大时间使播放边界正确 + maxTime_ = end; + // 通知 UI 更新范围与步进 + double minTime = 0.0, maxTime = 0.0, step = 0.0; + GetRange(minTime, maxTime, step); + emit RangeChanged(minTime, maxTime, step); +} - QFile file(filePath); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - LOG_WARN("Cannot open file for reading: {}", file.errorString().toLocal8Bit().constData()); - return nullptr; - } - - QTextStream in(&file); - std::vector numbers; - - while (!in.atEnd()) { - QString line = in.readLine(); - bool ok; - double value = line.toDouble(&ok); - if (ok) { - numbers.push_back(value); - } else { - LOG_WARN("Cannot open file for reading: {}", line.toStdString()); - } - } - - file.close(); - - Timestep* timestep = new Timestep(numbers, path, workSpace); - return timestep; +void Timestep::ClearManualRange() { + hasManualRange_ = false; + manualStart_ = 0.0; + manualEnd_ = 0.0; + // 恢复为数据驱动的最大时间,保留当前 maxTime_ + // 通知 UI 更新范围与步进 + double minTime = 0.0, maxTime = 0.0, step = 0.0; + GetRange(minTime, maxTime, step); + emit RangeChanged(minTime, maxTime, step); } void Timestep::GetRange(double& minTime, double& maxTime, double& step) { - minTime = *steps_.begin(); - maxTime = maxTime_; + if (hasManualRange_) { + minTime = manualStart_; + maxTime = manualEnd_; + } else { + minTime = 0.0; + maxTime = maxTime_; + } step = currentStep_; } @@ -90,7 +88,7 @@ bool Timestep::IsStoped() { void Timestep::Start() { LOG_INFO("enter"); playStatus_ = PlayStatus::PS_Started; - current_ = 0.0; + current_ = hasManualRange_ ? manualStart_ : 0.0; if (nullptr == workSpace_) { LOG_WARN("workSpace_ is nullptr"); @@ -127,7 +125,7 @@ void Timestep::Pause() { void Timestep::Stop() { LOG_INFO("enter"); - current_ = maxTime_; + current_ = hasManualRange_ ? manualEnd_ : maxTime_; playStatus_ = PlayStatus::PS_Stoped; if (nullptr == workSpace_) { LOG_WARN("workSpace_ is nullptr"); @@ -151,6 +149,13 @@ void Timestep::Up() { emit StepChanged(currentStep_); } +void Timestep::SetDataMaxTime(double end) { + maxTime_ = end; + double minTime = 0.0, maxTime = 0.0, step = 0.0; + GetRange(minTime, maxTime, step); + emit RangeChanged(minTime, maxTime, step); +} + void Timestep::Down() { // 降到上一个倍率(保底) if (speedIndex_ > 0) { @@ -159,3 +164,21 @@ void Timestep::Down() { currentStep_ = speedLevels_[speedIndex_]; emit StepChanged(currentStep_); } + +void Timestep::SetSpeed(double speed) { + if (speedLevels_.empty()) { + return; + } + int bestIdx = 0; + double bestDiff = std::numeric_limits::max(); + for (int i = 0; i < static_cast(speedLevels_.size()); ++i) { + double diff = std::fabs(speedLevels_[i] - speed); + if (diff < bestDiff) { + bestDiff = diff; + bestIdx = i; + } + } + speedIndex_ = bestIdx; + currentStep_ = speedLevels_[speedIndex_]; + emit StepChanged(currentStep_); +} diff --git a/src/workspace/Timestep.h b/src/workspace/Timestep.h index 1e85fa8d..31e680ff 100644 --- a/src/workspace/Timestep.h +++ b/src/workspace/Timestep.h @@ -8,7 +8,7 @@ class WorkSpace; class Timestep : public QObject { - Q_OBJECT + Q_OBJECT public: enum class PlayStatus : uint8_t { PS_Started, @@ -17,23 +17,14 @@ public: }; public: - explicit Timestep(const std::vector& steps, const QString& path, WorkSpace* parent = nullptr) noexcept; + explicit Timestep(WorkSpace* parent = nullptr) noexcept; ~Timestep() override = default; - static Timestep* Load(const QString& path, WorkSpace* workSpace = nullptr); - - const std::vector& GetSteps() const { - return steps_; - } - const QString& GetPath() const { - return path_; - } - - void GetRange(double& minTime, double& maxTime, double& step); - void Update(double dt); - double GetCurrent() const { - return current_; - } + void GetRange(double& minTime, double& maxTime, double& step); + void Update(double dt); + double GetCurrent() const { + return current_; + } bool IsPause(); bool IsStoped(); @@ -42,22 +33,34 @@ public: void Pause(); void Stop(); - void Up(); + void Up(); void Down(); + // 设置播放速度(倍率),会选取最接近的预设倍率并触发 StepChanged + void SetSpeed(double speed); + + // 设置数据驱动的默认最大时间(非手动区间),用于没有文件步长时的播放边界 + void SetDataMaxTime(double end); WorkSpace* GetWorkSpace() const { return workSpace_; } + // 手动时间区间(可选):允许用户指定起止时间,替代或重映射步骤 + void SetManualRange(double start, double end); + void ClearManualRange(); + bool HasManualRange() const { return hasManualRange_; } + double GetManualStart() const { return manualStart_; } + double GetManualEnd() const { return manualEnd_; } + Q_SIGNALS: - void StatusChanged(int); - void StepChanged(double); - void TimeChanged(double); + void StatusChanged(int); + void StepChanged(double); + void TimeChanged(double); + // 当时间范围或步进倍率更新时通知 UI(例如设置手动区间或清除) + void RangeChanged(double minTime, double maxTime, double step); private: - std::vector steps_; - QString path_; double current_{ 0.0 }; double maxTime_{ 0.0 }; // 播放速度(倍率),与 UI 显示一致,比如 0.5x、1x、2x @@ -66,8 +69,12 @@ private: std::vector speedLevels_{ 0.25, 0.5, 1.0, 2.0, 4.0, 8.0 }; int speedIndex_{ 2 }; // 默认指向 1.0x - - PlayStatus playStatus_{ PlayStatus::PS_Stoped }; - WorkSpace* workSpace_{ nullptr }; + + PlayStatus playStatus_{ PlayStatus::PS_Stoped }; + WorkSpace* workSpace_{ nullptr }; + + bool hasManualRange_{ false }; + double manualStart_{ 0.0 }; + double manualEnd_{ 0.0 }; }; diff --git a/src/workspace/WorkSpace.cpp b/src/workspace/WorkSpace.cpp index d6497053..b3088522 100644 --- a/src/workspace/WorkSpace.cpp +++ b/src/workspace/WorkSpace.cpp @@ -263,24 +263,11 @@ void WorkSpace::SetActiveScene(OEScene* scene) { //homeViewpoint_ = vp; } -bool WorkSpace::SetTimestep(Timestep* timestep) { - if (!timestep) { - return false; +void WorkSpace::EnsureTimestep() { + if (nullptr == timestep_) { + timestep_ = new Timestep(this); + emit TimestepChanged(timestep_); } - - if (nullptr != timestep_ && timestep_ != timestep) { - timestep_->deleteLater(); - } - - timestep_ = timestep; - emit TimestepChanged(timestep_); - return true; -} - -bool WorkSpace::SetTimestepPath(const QString& path) { - Timestep* timestep = Timestep::Load(path, this); - - return SetTimestep(timestep); } bool WorkSpace::SetLampStatus(class LampStatus* lampStatus) { @@ -368,9 +355,7 @@ void WorkSpace::OnLoaded() { if (nullptr != lampStatus_) { emit LampStatusChanged(lampStatus_); } - if (nullptr != timestep_) { - emit TimestepChanged(timestep_); - } + EnsureTimestep(); // Execute commands configured for onLoad ExecuteCommands(CommandWhen::OnLoad); } diff --git a/src/workspace/WorkSpace.h b/src/workspace/WorkSpace.h index e5d3444d..48c95e01 100644 --- a/src/workspace/WorkSpace.h +++ b/src/workspace/WorkSpace.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include @@ -100,8 +100,7 @@ public: dyt_check(nullptr != scene_); return scene_; } - bool SetTimestep(class Timestep* timestep); - bool SetTimestepPath(const QString& path); + // Timestep lifecycle is managed internally; external setting removed class Timestep* GetTimestep() const { return timestep_; } @@ -132,8 +131,9 @@ public: void OnLoaded(); - // 通知:某类型的文件条目属性已更新(不改变数量,仅触发刷新) void NotifyFileEntryUpdated(FileEntryType type, std::shared_ptr fileEntry = nullptr); + // Ensure a timestep exists; create if missing and emit change + void EnsureTimestep(); Q_SIGNALS: void EntityAdded(class Entity* entity); @@ -171,3 +171,5 @@ public: friend class WorkSpaceXMLParse; }; + + diff --git a/src/workspace/WorkSpaceXMLParse.cpp b/src/workspace/WorkSpaceXMLParse.cpp index 207fb69e..c5b27ad2 100644 --- a/src/workspace/WorkSpaceXMLParse.cpp +++ b/src/workspace/WorkSpaceXMLParse.cpp @@ -2,6 +2,7 @@ #include "workspace/WorkSpace.h" #include "entities/EntitiesManager.h" +#include "workspace/Timestep.h" #include "common/SpdLogger.h" #include "workspace/WorkSpaceManager.h" @@ -59,14 +60,35 @@ bool WorkSpaceXMLParse::ParseTimestep(const tinyxml2::XMLElement* element) { LOG_WARN("element is nullptr"); return false; } - - const char* path = element->Attribute("path"); - if (nullptr == path) { - LOG_WARN("element not has path"); + // 确保存在 Timestep 实例 + workSpace_->EnsureTimestep(); + Timestep* t = workSpace_->GetTimestep(); + if (!t) { return false; } - - return workSpace_->SetTimestepPath(path); + + // 解析属性:manual(0/1)、start、end、step + int manualFlag = 0; + element->QueryIntAttribute("manual", &manualFlag); + double start = 0.0; + double end = 0.0; + double step = 0.0; + bool hasStart = (element->QueryDoubleAttribute("start", &start) == tinyxml2::XML_SUCCESS); + bool hasEnd = (element->QueryDoubleAttribute("end", &end) == tinyxml2::XML_SUCCESS); + bool hasStep = (element->QueryDoubleAttribute("step", &step) == tinyxml2::XML_SUCCESS); + + if (hasStep) { + t->SetSpeed(step); + } + + // 若标记为手动或具有完整的 start/end,则设置手动范围;否则按数据最大时间设置边界 + if (manualFlag != 0 || (hasStart && hasEnd)) { + t->SetManualRange(hasStart ? start : 0.0, hasEnd ? end : 0.0); + } else if (hasEnd) { + t->SetDataMaxTime(end); + } + + return true; } bool WorkSpaceXMLParse::ParseLamp(const tinyxml2::XMLElement* element) { diff --git a/src/workspace/WorkSpaceXMLWrite.cpp b/src/workspace/WorkSpaceXMLWrite.cpp index 1a6170fa..17c754db 100644 --- a/src/workspace/WorkSpaceXMLWrite.cpp +++ b/src/workspace/WorkSpaceXMLWrite.cpp @@ -34,9 +34,9 @@ bool WorkSpaceXMLWrite::Save(const QString& path) { } doc.LinkEndChild(scene); + SaveTimeStep(scene); SaveChart(scene, &doc); - SaveTimeStep(scene); SaveLamp(scene); SaveCommond(scene); SaveFiles(scene, &doc); @@ -66,13 +66,31 @@ bool WorkSpaceXMLWrite::SaveScene(tinyxml2::XMLElement* scene) { return true; } +// Removed: SaveTimeStep — timestep is no longer file-based +// 新增:保存 Timestep 的手动范围与步长倍率 bool WorkSpaceXMLWrite::SaveTimeStep(tinyxml2::XMLElement* scene) { - Timestep* timestep = workSpace_->GetTimestep(); - if (nullptr == timestep) { + if (nullptr == workSpace_) { return false; } - tinyxml2::XMLElement* timestepXml = scene->InsertNewChildElement("timestep"); - timestepXml->SetAttribute("path", timestep->GetPath().toStdString().c_str()); + auto* t = workSpace_->GetTimestep(); + if (!t) { + // 若尚未创建,确保存在一个默认实例,以保证文件的一致性 + workSpace_->EnsureTimestep(); + t = workSpace_->GetTimestep(); + } + if (!t) { + return false; + } + + double start = 0.0, end = 0.0, step = 1.0; + t->GetRange(start, end, step); + + tinyxml2::XMLElement* timestep = scene->InsertNewChildElement("timestep"); + timestep->SetAttribute("manual", t->HasManualRange() ? 1 : 0); + timestep->SetAttribute("start", start); + timestep->SetAttribute("end", end); + timestep->SetAttribute("step", step); + return true; }