1#include "envelope_extrema.h"
2
3CEnvelopeExtrema::CEnvelopeExtrema(IMap *pMap) :
4 m_EnvelopePoints(pMap)
5{
6 m_pMap = pMap;
7
8 m_EnvelopeExtremaItemNone.m_Available = true;
9 m_EnvelopeExtremaItemNone.m_Rotating = false;
10 m_EnvelopeExtremaItemNone.m_Minima = ivec2(0, 0);
11 m_EnvelopeExtremaItemNone.m_Maxima = ivec2(0, 0);
12
13 m_EnvelopeExtremaItemInvalid.m_Available = false;
14 m_EnvelopeExtremaItemInvalid.m_Rotating = false;
15 m_EnvelopeExtremaItemInvalid.m_Minima = ivec2(0, 0);
16 m_EnvelopeExtremaItemInvalid.m_Maxima = ivec2(0, 0);
17
18 CalculateExtrema();
19}
20
21void CEnvelopeExtrema::CalculateEnvelope(const CMapItemEnvelope *pEnvelopeItem, int EnvId)
22{
23 CEnvelopeExtremaItem &EnvExt = m_vEnvelopeExtrema[EnvId];
24
25 // setup default values
26 for(int Channel = 0; Channel < 2; ++Channel)
27 {
28 EnvExt.m_Minima[Channel] = std::numeric_limits<int>::max(); // minimum of channel
29 EnvExt.m_Maxima[Channel] = std::numeric_limits<int>::min(); // maximum of channel
30 }
31 EnvExt.m_Available = false;
32 EnvExt.m_Rotating = false;
33
34 // check if the envelope is a valid position envelope
35 if(!pEnvelopeItem || pEnvelopeItem->m_Channels != 3)
36 return;
37
38 for(int PointId = pEnvelopeItem->m_StartPoint; PointId < pEnvelopeItem->m_StartPoint + pEnvelopeItem->m_NumPoints; ++PointId)
39 {
40 const CEnvPoint *pEnvPoint = m_EnvelopePoints.GetPoint(Index: PointId);
41 if(!pEnvPoint)
42 return;
43
44 // check if quad is rotating
45 if(pEnvPoint->m_aValues[2] != 0)
46 EnvExt.m_Rotating = true;
47
48 for(int Channel = 0; Channel < 2; ++Channel)
49 {
50 EnvExt.m_Minima[Channel] = std::min(a: pEnvPoint->m_aValues[Channel], b: EnvExt.m_Minima[Channel]);
51 EnvExt.m_Maxima[Channel] = std::max(a: pEnvPoint->m_aValues[Channel], b: EnvExt.m_Maxima[Channel]);
52
53 // bezier curves can have offsets beyond the fixed points
54 // using the bezier position is just an estimate, but clipping like this is good enough
55 if(PointId < pEnvelopeItem->m_StartPoint + pEnvelopeItem->m_NumPoints - 1 && pEnvPoint->m_Curvetype == CURVETYPE_BEZIER)
56 {
57 const CEnvPointBezier *pEnvPointBezier = m_EnvelopePoints.GetBezier(Index: PointId);
58 if(!pEnvPointBezier)
59 return;
60
61 // we are only interested in the height not in the time, meaning we only need delta Y
62 EnvExt.m_Minima[Channel] = std::min(a: pEnvPoint->m_aValues[Channel] + pEnvPointBezier->m_aOutTangentDeltaY[Channel], b: EnvExt.m_Minima[Channel]);
63 EnvExt.m_Maxima[Channel] = std::max(a: pEnvPoint->m_aValues[Channel] + pEnvPointBezier->m_aOutTangentDeltaY[Channel], b: EnvExt.m_Maxima[Channel]);
64 }
65
66 if(PointId > 0 && m_EnvelopePoints.GetPoint(Index: PointId - 1)->m_Curvetype == CURVETYPE_BEZIER)
67 {
68 const CEnvPointBezier *pEnvPointBezier = m_EnvelopePoints.GetBezier(Index: PointId);
69 if(!pEnvPointBezier)
70 return;
71
72 // we are only interested in the height not in the time, meaning we only need delta Y
73 EnvExt.m_Minima[Channel] = std::min(a: pEnvPoint->m_aValues[Channel] + pEnvPointBezier->m_aInTangentDeltaY[Channel], b: EnvExt.m_Minima[Channel]);
74 EnvExt.m_Maxima[Channel] = std::max(a: pEnvPoint->m_aValues[Channel] + pEnvPointBezier->m_aInTangentDeltaY[Channel], b: EnvExt.m_Maxima[Channel]);
75 }
76 }
77 }
78
79 EnvExt.m_Available = true;
80}
81
82void CEnvelopeExtrema::CalculateExtrema()
83{
84 int EnvStart, EnvNum;
85 m_pMap->GetType(Type: MAPITEMTYPE_ENVELOPE, pStart: &EnvStart, pNum: &EnvNum);
86 m_vEnvelopeExtrema.resize(new_size: EnvNum);
87 for(int EnvId = 0; EnvId < EnvNum; ++EnvId)
88 {
89 const CMapItemEnvelope *pItem = static_cast<const CMapItemEnvelope *>(m_pMap->GetItem(Index: EnvStart + EnvId));
90 CalculateEnvelope(pEnvelopeItem: pItem, EnvId);
91 }
92}
93
94const CEnvelopeExtrema::CEnvelopeExtremaItem &CEnvelopeExtrema::GetExtrema(int Env) const
95{
96 if(Env == -1)
97 return m_EnvelopeExtremaItemNone; // No envelope just means no movement
98 else if(Env < -1 || Env >= (int)m_vEnvelopeExtrema.size())
99 return m_EnvelopeExtremaItemInvalid;
100 return m_vEnvelopeExtrema[Env];
101}
102