From 2ee081261b43b5730de911d9c248575d07af2498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Sat, 2 Sep 2023 14:33:39 -0400 Subject: [PATCH] [magnetism] Allow processes to have magnetism too --- .../score-lib-process/Process/Process.cpp | 6 ++ .../score-lib-process/Process/Process.hpp | 7 ++ .../Color/GradientModel.cpp | 10 +++ .../Color/GradientModel.hpp | 3 + .../FullView/FullViewIntervalPresenter.cpp | 74 +++++++++++++++++++ .../Document/Interval/IntervalModel.cpp | 6 +- .../Document/Interval/IntervalModel.hpp | 2 +- .../Scenario/Process/ScenarioModel.cpp | 9 +++ .../Scenario/Process/ScenarioModel.hpp | 2 + 9 files changed, 115 insertions(+), 4 deletions(-) diff --git a/src/plugins/score-lib-process/Process/Process.cpp b/src/plugins/score-lib-process/Process/Process.cpp index 03d54e6d86..a5ab17a83f 100644 --- a/src/plugins/score-lib-process/Process/Process.cpp +++ b/src/plugins/score-lib-process/Process/Process.cpp @@ -320,6 +320,12 @@ void ProcessModel::setSlotHeight(double v) noexcept slotHeightChanged(v); } +std::optional +ProcessModel::magneticPosition(const QObject* o, const TimeVal t) const noexcept +{ + return {}; +} + ProcessModel* parentProcess(QObject* obj) noexcept { if(obj) diff --git a/src/plugins/score-lib-process/Process/Process.hpp b/src/plugins/score-lib-process/Process/Process.hpp index 33181a05a9..99ab07db34 100644 --- a/src/plugins/score-lib-process/Process/Process.hpp +++ b/src/plugins/score-lib-process/Process/Process.hpp @@ -5,6 +5,8 @@ #include #include +#include + #include #include #include @@ -22,6 +24,7 @@ #include #include +#include #include #include @@ -133,6 +136,10 @@ class SCORE_LIB_PROCESS_EXPORT ProcessModel virtual void forEachControl( smallfun::function) const; + // Magnetism + virtual std::optional + magneticPosition(const QObject* o, const TimeVal t) const noexcept; + // Clip duration things bool loops() const noexcept { return m_loops; } void setLoops(bool b); diff --git a/src/plugins/score-plugin-automation/Color/GradientModel.cpp b/src/plugins/score-plugin-automation/Color/GradientModel.cpp index d84fb9ddfe..e770d25720 100644 --- a/src/plugins/score-plugin-automation/Color/GradientModel.cpp +++ b/src/plugins/score-plugin-automation/Color/GradientModel.cpp @@ -167,6 +167,16 @@ Process::Preset ProcessModel::savePreset() const noexcept return p; } +std::optional +ProcessModel::magneticPosition(const QObject* o, const TimeVal t) const noexcept +{ + double pos = t.impl / double(this->duration().impl); + + auto it = m_colors.lower_bound(pos); + if(it != m_colors.end()) + return Process::MagneticInfo{TimeVal(it->first * this->duration().impl), true}; + return {}; +} } template <> diff --git a/src/plugins/score-plugin-automation/Color/GradientModel.hpp b/src/plugins/score-plugin-automation/Color/GradientModel.hpp index 27c701129e..2db537f47b 100644 --- a/src/plugins/score-plugin-automation/Color/GradientModel.hpp +++ b/src/plugins/score-plugin-automation/Color/GradientModel.hpp @@ -63,6 +63,9 @@ class SCORE_PLUGIN_AUTOMATION_EXPORT ProcessModel final : public Process::Proces void loadPreset(const Process::Preset& preset) override; Process::Preset savePreset() const noexcept override; + std::optional + magneticPosition(const QObject* o, const TimeVal t) const noexcept override; + void setDurationAndScale(const TimeVal& newDuration) noexcept override; void setDurationAndGrow(const TimeVal& newDuration) noexcept override; void setDurationAndShrink(const TimeVal& newDuration) noexcept override; diff --git a/src/plugins/score-plugin-scenario/Scenario/Document/Interval/FullView/FullViewIntervalPresenter.cpp b/src/plugins/score-plugin-scenario/Scenario/Document/Interval/FullView/FullViewIntervalPresenter.cpp index 3e91948d53..4d0cf9da7d 100644 --- a/src/plugins/score-plugin-scenario/Scenario/Document/Interval/FullView/FullViewIntervalPresenter.cpp +++ b/src/plugins/score-plugin-scenario/Scenario/Document/Interval/FullView/FullViewIntervalPresenter.cpp @@ -620,6 +620,10 @@ Process::MagneticInfo FullViewIntervalPresenter::magneticPosition( TimeVal scenarioT = t; TimeVal closestTimeSyncT = TimeVal::fromMsecs(std::numeric_limits::max()); bool snapToScenario{}; + bool foundMagnetism = false; + + // Priority order: + // 1. In the object's parent process if(auto given_ts = qobject_cast(o)) { if(auto scenario = qobject_cast(given_ts->parent())) @@ -639,10 +643,80 @@ Process::MagneticInfo FullViewIntervalPresenter::magneticPosition( { scenarioT = closestTimeSyncT; snapToScenario = true; + foundMagnetism = true; + } + } + } + + auto is_parent = [](QObject* const parent, const QObject* child) { + while((child = child->parent())) + { + if(child == parent) + return true; + } + return false; + }; + + // 2. In the processes around + if(!foundMagnetism) + { + for(auto& proc : this->model().processes) + { + if(!is_parent(&proc, o)) + { + if(auto pos = proc.magneticPosition(o, t)) + { + if(std::abs(pos->time.impl - t.impl) + < std::abs(closestTimeSyncT.impl - t.impl)) + closestTimeSyncT = pos->time; + } + } + } + + double delta = std::abs((closestTimeSyncT - t).toPixels(m_zoomRatio)); + if(delta < 10) + { + scenarioT = closestTimeSyncT; + snapToScenario = true; + foundMagnetism = true; + } + } + + // 3. In the child processes around the closest scenario to the moved object + + if(!foundMagnetism) + { + auto parentScenar = Scenario::closestParentScenario(o); + if(parentScenar) + { + for(auto& itv : parentScenar->intervals) + { + for(auto& proc : itv.processes) + { + if(!is_parent(&proc, o)) + { + if(auto pos = proc.magneticPosition(o, t)) + { + if(std::abs(pos->time.impl - t.impl) + < std::abs(closestTimeSyncT.impl - t.impl)) + closestTimeSyncT = pos->time; + } + } + } } } + + double delta = std::abs((closestTimeSyncT - t).toPixels(m_zoomRatio)); + if(delta < 10) + { + scenarioT = closestTimeSyncT; + snapToScenario = true; + foundMagnetism = true; + } } + // 4. That's all, don't search deeper + if(!m_settings.getMagneticMeasures() || !m_settings.getMeasureBars()) return {scenarioT, snapToScenario}; diff --git a/src/plugins/score-plugin-scenario/Scenario/Document/Interval/IntervalModel.cpp b/src/plugins/score-plugin-scenario/Scenario/Document/Interval/IntervalModel.cpp index 5837595940..f8c5f60e61 100644 --- a/src/plugins/score-plugin-scenario/Scenario/Document/Interval/IntervalModel.cpp +++ b/src/plugins/score-plugin-scenario/Scenario/Document/Interval/IntervalModel.cpp @@ -1057,13 +1057,13 @@ QPointF newProcessPosition(const IntervalModel& cst) noexcept } // TODO refactor by grepping for _cast.*IntervalModel -IntervalModel* closestParentInterval(QObject* parentObj) noexcept +IntervalModel* closestParentInterval(const QObject* parentObj) noexcept { - while(parentObj && !qobject_cast(parentObj)) + while(parentObj && !qobject_cast(parentObj)) { parentObj = parentObj->parent(); } - return static_cast(parentObj); + return static_cast(const_cast(parentObj)); } TimeVal timeDelta(const IntervalModel* self, const IntervalModel* parent) diff --git a/src/plugins/score-plugin-scenario/Scenario/Document/Interval/IntervalModel.hpp b/src/plugins/score-plugin-scenario/Scenario/Document/Interval/IntervalModel.hpp index 7ad9366bd5..e3916fcbf4 100644 --- a/src/plugins/score-plugin-scenario/Scenario/Document/Interval/IntervalModel.hpp +++ b/src/plugins/score-plugin-scenario/Scenario/Document/Interval/IntervalModel.hpp @@ -323,7 +323,7 @@ struct ParentTimeInfo }; SCORE_PLUGIN_SCENARIO_EXPORT -IntervalModel* closestParentInterval(QObject*) noexcept; +IntervalModel* closestParentInterval(const QObject*) noexcept; SCORE_PLUGIN_SCENARIO_EXPORT ParentTimeInfo closestParentWithMusicalMetrics(const IntervalModel*); diff --git a/src/plugins/score-plugin-scenario/Scenario/Process/ScenarioModel.cpp b/src/plugins/score-plugin-scenario/Scenario/Process/ScenarioModel.cpp index cb023ee9d6..de65139ab3 100644 --- a/src/plugins/score-plugin-scenario/Scenario/Process/ScenarioModel.cpp +++ b/src/plugins/score-plugin-scenario/Scenario/Process/ScenarioModel.cpp @@ -297,4 +297,13 @@ Process::Preset ProcessModel::savePreset() const noexcept p.data = r.toByteArray(); return p; } + +Scenario::ProcessModel* closestParentScenario(const QObject* parentObj) noexcept +{ + while(parentObj && !qobject_cast(parentObj)) + { + parentObj = parentObj->parent(); + } + return static_cast(const_cast(parentObj)); +} } diff --git a/src/plugins/score-plugin-scenario/Scenario/Process/ScenarioModel.hpp b/src/plugins/score-plugin-scenario/Scenario/Process/ScenarioModel.hpp index e515492c4c..e62a124c01 100644 --- a/src/plugins/score-plugin-scenario/Scenario/Process/ScenarioModel.hpp +++ b/src/plugins/score-plugin-scenario/Scenario/Process/ScenarioModel.hpp @@ -203,6 +203,8 @@ class SCORE_PLUGIN_SCENARIO_EXPORT ProcessModel final std::unique_ptr m_graph; }; + +Scenario::ProcessModel* closestParentScenario(const QObject* parentObj) noexcept; } // TODO this ought to go in Selection.hpp ? template