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