Phosphor
Qt6 / Wayland library suite for window-management tools
 
Loading...
Searching...
No Matches
AnimatedValue_geometric.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 fuddlesworth
2// SPDX-License-Identifier: LGPL-2.1-or-later
3
4#pragma once
5
6// Geometric query helpers for AnimatedValue<T, Space>. Included from
7// AnimatedValue.h — do not include directly.
8
9namespace PhosphorAnimation {
10
12template<typename T, ColorSpace Space>
14 requires detail::PositionalGeometric<T>
15{
16 return boundsImpl();
17}
18
20template<typename T, ColorSpace Space>
21QRectF AnimatedValue<T, Space>::boundsAt(QPointF anchor) const
23{
24 return QRectF(anchor, sweptSizeImpl().second);
25}
26
28template<typename T, ColorSpace Space>
29std::pair<QSizeF, QSizeF> AnimatedValue<T, Space>::sweptSize() const
30 requires detail::SizeGeometric<T>
31{
32 return sweptSizeImpl();
33}
34
36template<typename T, ColorSpace Space>
37bool AnimatedValue<T, Space>::hasSizeChange(qreal epsilonPx) const
38 requires std::same_as<T, QRectF>
39{
40 return qAbs(m_current.width() - m_to.width()) > epsilonPx || qAbs(m_current.height() - m_to.height()) > epsilonPx;
41}
42
44template<typename T, ColorSpace Space>
46 requires detail::ScalarValue<T>
47{
48 return sweptRangeImpl();
49}
50
51// ─── Private impl helpers ───
52
53// Uses explicit min/max reduction instead of QRectF::united() because
54// Qt treats zero-width/zero-height rects as "empty" and drops them.
55template<typename T, ColorSpace Space>
57 requires detail::PositionalGeometric<T>
58{
59 qreal minX, minY, maxX, maxY;
60 if constexpr (std::same_as<T, QPointF>) {
61 minX = std::min(m_from.x(), m_to.x());
62 maxX = std::max(m_from.x(), m_to.x());
63 minY = std::min(m_from.y(), m_to.y());
64 maxY = std::max(m_from.y(), m_to.y());
65 sampleOvershoots(minX, minY, maxX, maxY, [this](qreal p) {
66 const QPointF s = Interpolate<QPointF>::lerp(m_from, m_to, p);
67 return std::tuple<qreal, qreal, qreal, qreal>{s.x(), s.y(), s.x(), s.y()};
68 });
69 } else {
70 minX = std::min(m_from.left(), m_to.left());
71 minY = std::min(m_from.top(), m_to.top());
72 maxX = std::max(m_from.right(), m_to.right());
73 maxY = std::max(m_from.bottom(), m_to.bottom());
74 sampleOvershoots(minX, minY, maxX, maxY, [this](qreal p) {
75 const QRectF s = Interpolate<QRectF>::lerp(m_from, m_to, p);
76 return std::tuple<qreal, qreal, qreal, qreal>{s.left(), s.top(), s.right(), s.bottom()};
77 });
78 }
79 return QRectF(minX, minY, maxX - minX, maxY - minY);
80}
81
82template<typename T, ColorSpace Space>
83std::pair<QSizeF, QSizeF> AnimatedValue<T, Space>::sweptSizeImpl() const
84 requires detail::SizeGeometric<T>
85{
86 qreal minW = std::min(m_from.width(), m_to.width());
87 qreal maxW = std::max(m_from.width(), m_to.width());
88 qreal minH = std::min(m_from.height(), m_to.height());
89 qreal maxH = std::max(m_from.height(), m_to.height());
90 const auto curve = effectiveCurve();
91 if (curve && curve->overshoots()) {
92 for (int i = 1; i < kOvershootSamples; ++i) {
93 const qreal p = curve->evaluate(qreal(i) / kOvershootSamples);
94 const QSizeF sampled = Interpolate<QSizeF>::lerp(m_from, m_to, p);
95 minW = std::min(minW, sampled.width());
96 maxW = std::max(maxW, sampled.width());
97 minH = std::min(minH, sampled.height());
98 maxH = std::max(maxH, sampled.height());
99 }
100 }
101 return {QSizeF(minW, minH), QSizeF(maxW, maxH)};
102}
103
104template<typename T, ColorSpace Space>
105template<typename Sampler>
106void AnimatedValue<T, Space>::sampleOvershoots(qreal& minX, qreal& minY, qreal& maxX, qreal& maxY,
107 const Sampler& sampleAt) const
108{
109 const auto curve = effectiveCurve();
110 if (!curve || !curve->overshoots()) {
111 return;
112 }
113 for (int i = 1; i < kOvershootSamples; ++i) {
114 const qreal p = curve->evaluate(qreal(i) / kOvershootSamples);
115 const auto [x1, y1, x2, y2] = sampleAt(p);
116 minX = std::min(minX, x1);
117 minY = std::min(minY, y1);
118 maxX = std::max(maxX, x2);
119 maxY = std::max(maxY, y2);
120 }
121}
122
123template<typename T, ColorSpace Space>
124std::pair<T, T> AnimatedValue<T, Space>::sweptRangeImpl() const
125{
126 T lo = std::min(m_from, m_to);
127 T hi = std::max(m_from, m_to);
128 const auto curve = effectiveCurve();
129 if (curve && curve->overshoots()) {
130 for (int i = 1; i < kOvershootSamples; ++i) {
131 const qreal p = curve->evaluate(qreal(i) / kOvershootSamples);
132 const T sampled = Interpolate<T>::lerp(m_from, m_to, p);
133 lo = std::min(lo, sampled);
134 hi = std::max(hi, sampled);
135 }
136 }
137 return {lo, hi};
138}
139
140} // namespace PhosphorAnimation
Unified motion primitive: one value of type T transitioning from start to target over time,...
Definition AnimatedValue.h:44
bool hasSizeChange(qreal epsilonPx=kRectSizeEpsilonPx) const
True when current size diverges from target by > epsilonPx on either axis.
Definition AnimatedValue_geometric.h:37
std::pair< T, T > sweptRange() const
(lo, hi) of a scalar animation's swept range, with overshoot.
Definition AnimatedValue_geometric.h:45
QRectF bounds() const
Bounding rectangle of the swept path including curve overshoot.
Definition AnimatedValue_geometric.h:13
QRectF boundsAt(QPointF anchor) const
Damage rect anchored at anchor — QSizeF specialisation only.
Definition AnimatedValue_geometric.h:21
std::pair< QSizeF, QSizeF > sweptSize() const
(minSize, maxSize) the animation sweeps through, including overshoot.
Definition AnimatedValue_geometric.h:29
Size-only T — caller must supply an anchor for damage rect.
Definition Interpolate.h:400
Definition AnimatedValue.h:31
Type-specific linear interpolation and path-distance for AnimatedValue<T>.
Definition Interpolate.h:36