1 | #include "envelope.h" |
2 | |
3 | #include <algorithm> |
4 | #include <chrono> |
5 | #include <limits> |
6 | |
7 | using namespace std::chrono_literals; |
8 | |
9 | CEnvelope::CEnvelopePointAccess::CEnvelopePointAccess(std::vector<CEnvPoint_runtime> *pvPoints) |
10 | { |
11 | m_pvPoints = pvPoints; |
12 | } |
13 | |
14 | int CEnvelope::CEnvelopePointAccess::NumPoints() const |
15 | { |
16 | return m_pvPoints->size(); |
17 | } |
18 | |
19 | const 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 | |
26 | const 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 | |
33 | CEnvelope::CEnvelope(EType Type) : |
34 | m_Type(Type), m_PointsAccess(&m_vPoints) {} |
35 | |
36 | CEnvelope::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 | |
55 | void CEnvelope::Resort() |
56 | { |
57 | std::sort(first: m_vPoints.begin(), last: m_vPoints.end()); |
58 | } |
59 | |
60 | std::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 | |
101 | void 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 | |
107 | void 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 | |
127 | float CEnvelope::EndTime() const |
128 | { |
129 | if(m_vPoints.empty()) |
130 | return 0.0f; |
131 | return m_vPoints.back().m_Time / 1000.0f; |
132 | } |
133 | |
134 | int 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 | |