1#include "envelope.h"
2
3#include <base/system.h>
4
5#include <algorithm>
6#include <chrono>
7#include <limits>
8
9using namespace std::chrono_literals;
10
11CEnvelope::CEnvelopePointAccess::CEnvelopePointAccess(std::vector<CEnvPoint_runtime> *pvPoints)
12{
13 m_pvPoints = pvPoints;
14}
15
16int CEnvelope::CEnvelopePointAccess::NumPoints() const
17{
18 return m_pvPoints->size();
19}
20
21const CEnvPoint *CEnvelope::CEnvelopePointAccess::GetPoint(int Index) const
22{
23 if(Index < 0 || (size_t)Index >= m_pvPoints->size())
24 return nullptr;
25 return &m_pvPoints->at(n: Index);
26}
27
28const CEnvPointBezier *CEnvelope::CEnvelopePointAccess::GetBezier(int Index) const
29{
30 if(Index < 0 || (size_t)Index >= m_pvPoints->size())
31 return nullptr;
32 return &m_pvPoints->at(n: Index).m_Bezier;
33}
34
35CEnvelope::CEnvelope(EType Type) :
36 m_Type(Type), m_PointsAccess(&m_vPoints) {}
37
38CEnvelope::CEnvelope(int NumChannels) :
39 m_PointsAccess(&m_vPoints)
40{
41 switch(NumChannels)
42 {
43 case 1:
44 m_Type = EType::SOUND;
45 break;
46 case 3:
47 m_Type = EType::POSITION;
48 break;
49 case 4:
50 m_Type = EType::COLOR;
51 break;
52 default:
53 dbg_assert_failed("invalid number of channels for envelope");
54 }
55}
56
57void CEnvelope::Resort()
58{
59 std::sort(first: m_vPoints.begin(), last: m_vPoints.end());
60}
61
62std::pair<float, float> CEnvelope::GetValueRange(int ChannelMask)
63{
64 float Top = -std::numeric_limits<float>::infinity();
65 float Bottom = std::numeric_limits<float>::infinity();
66 for(size_t PointIndex = 0; PointIndex < m_vPoints.size(); ++PointIndex)
67 {
68 const auto &Point = m_vPoints[PointIndex];
69 for(int c = 0; c < GetChannels(); c++)
70 {
71 if(ChannelMask & (1 << c))
72 {
73 {
74 // value handle
75 const float v = fx2f(v: Point.m_aValues[c]);
76 Top = maximum(a: Top, b: v);
77 Bottom = minimum(a: Bottom, b: v);
78 }
79
80 if(PointIndex < m_vPoints.size() - 1 && Point.m_Curvetype == CURVETYPE_BEZIER)
81 {
82 // out-tangent handle
83 const float v = fx2f(v: Point.m_aValues[c] + Point.m_Bezier.m_aOutTangentDeltaY[c]);
84 Top = maximum(a: Top, b: v);
85 Bottom = minimum(a: Bottom, b: v);
86 }
87
88 if(PointIndex > 0 && m_vPoints[PointIndex - 1].m_Curvetype == CURVETYPE_BEZIER)
89 {
90 // in-tangent handle
91 const float v = fx2f(v: Point.m_aValues[c] + Point.m_Bezier.m_aInTangentDeltaY[c]);
92 Top = maximum(a: Top, b: v);
93 Bottom = minimum(a: Bottom, b: v);
94 }
95 }
96 }
97 }
98 return {Bottom, Top};
99}
100
101void CEnvelope::Eval(float Time, ColorRGBA &Result, size_t Channels)
102{
103 Channels = minimum<size_t>(a: Channels, b: GetChannels(), c: CEnvPoint::MAX_CHANNELS);
104 CRenderMap::RenderEvalEnvelope(pPoints: &m_PointsAccess, TimeNanos: std::chrono::nanoseconds((int64_t)((double)Time * (double)std::chrono::nanoseconds(1s).count())), Result, Channels);
105}
106
107void CEnvelope::AddPoint(CFixedTime Time, std::array<int, CEnvPoint::MAX_CHANNELS> aValues)
108{
109 CEnvPoint_runtime Point;
110 Point.m_Time = Time;
111 Point.m_Curvetype = CURVETYPE_LINEAR;
112 std::copy_n(first: aValues.begin(), n: std::size(Point.m_aValues), result: Point.m_aValues);
113 std::fill(first: std::begin(arr&: Point.m_Bezier.m_aInTangentDeltaX), last: std::end(arr&: Point.m_Bezier.m_aInTangentDeltaX), value: CFixedTime(0));
114 std::fill(first: std::begin(arr&: Point.m_Bezier.m_aInTangentDeltaY), last: std::end(arr&: Point.m_Bezier.m_aInTangentDeltaY), value: 0);
115 std::fill(first: std::begin(arr&: Point.m_Bezier.m_aOutTangentDeltaX), last: std::end(arr&: Point.m_Bezier.m_aOutTangentDeltaX), value: CFixedTime(0));
116 std::fill(first: std::begin(arr&: Point.m_Bezier.m_aOutTangentDeltaY), last: std::end(arr&: Point.m_Bezier.m_aOutTangentDeltaY), value: 0);
117 m_vPoints.emplace_back(args&: Point);
118 Resort();
119}
120
121float CEnvelope::EndTime() const
122{
123 if(m_vPoints.empty())
124 return 0.0f;
125 return m_vPoints.back().m_Time.AsSeconds();
126}
127
128int CEnvelope::FindPointIndex(CFixedTime Time) const
129{
130 return m_PointsAccess.FindPointIndex(Time);
131}
132
133int CEnvelope::GetChannels() const
134{
135 switch(m_Type)
136 {
137 case EType::POSITION:
138 return 3;
139 case EType::COLOR:
140 return 4;
141 case EType::SOUND:
142 return 1;
143 default:
144 dbg_assert_failed("unknown envelope type");
145 }
146}
147