You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
308 lines
9.3 KiB
308 lines
9.3 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//=============================================================================// |
|
|
|
#include <math.h> |
|
|
|
#include <vgui_controls/GraphPanel.h> |
|
#include <vgui/IScheme.h> |
|
#include <vgui/ISurface.h> |
|
#include <vgui/IVGui.h> |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include <tier0/memdbgon.h> |
|
|
|
using namespace vgui; |
|
|
|
DECLARE_BUILD_FACTORY( GraphPanel ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
GraphPanel::GraphPanel(Panel *parent, const char *name) : BaseClass(parent, name) |
|
{ |
|
m_flDomainSize = 100.0f; |
|
m_flLowRange = 0.0f; |
|
m_flHighRange = 1.0f; |
|
m_bUseDynamicRange = true; |
|
m_flMinDomainSize = 0.0f; |
|
m_flMaxDomainSize = 0.0f; |
|
m_bMaxDomainSizeSet = false; |
|
|
|
// rendering, need to pull these from scheme/res file |
|
m_iGraphBarWidth = 2; |
|
m_iGraphBarGapWidth = 2; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: domain settings (x-axis settings) |
|
//----------------------------------------------------------------------------- |
|
void GraphPanel::SetDisplayDomainSize(float size) |
|
{ |
|
m_flDomainSize = size; |
|
|
|
// set the max domain size if it hasn't been set yet |
|
if (!m_bMaxDomainSizeSet) |
|
{ |
|
SetMaxDomainSize(size); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: sets the smallest domain that will be displayed |
|
//----------------------------------------------------------------------------- |
|
void GraphPanel::SetMinDomainSize(float size) |
|
{ |
|
m_flMinDomainSize = size; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: sets the samples to keep |
|
//----------------------------------------------------------------------------- |
|
void GraphPanel::SetMaxDomainSize(float size) |
|
{ |
|
m_flMaxDomainSize = size; |
|
m_bMaxDomainSizeSet = true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: range settings (y-axis settings) |
|
//----------------------------------------------------------------------------- |
|
void GraphPanel::SetUseFixedRange(float lowRange, float highRange) |
|
{ |
|
m_bUseDynamicRange = false; |
|
m_flLowRange = lowRange; |
|
m_flHighRange = highRange; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sets the graph to dynamically determine the range |
|
//----------------------------------------------------------------------------- |
|
void GraphPanel::SetUseDynamicRange(float *rangeList, int numRanges) |
|
{ |
|
m_bUseDynamicRange = true; |
|
m_RangeList.CopyArray(rangeList, numRanges); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Gets the currently displayed range |
|
//----------------------------------------------------------------------------- |
|
void GraphPanel::GetDisplayedRange(float &lowRange, float &highRange) |
|
{ |
|
lowRange = m_flLowRange; |
|
highRange = m_flHighRange; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: adds an item to the end of the list |
|
//----------------------------------------------------------------------------- |
|
void GraphPanel::AddItem(float sampleEnd, float sampleValue) |
|
{ |
|
if (m_Samples.Count() && m_Samples[m_Samples.Tail()].value == sampleValue) |
|
{ |
|
// collapse identical samples |
|
m_Samples[m_Samples.Tail()].sampleEnd = sampleEnd; |
|
} |
|
else |
|
{ |
|
// add to the end of the samples list |
|
Sample_t item; |
|
item.value = sampleValue; |
|
item.sampleEnd = sampleEnd; |
|
m_Samples.AddToTail(item); |
|
} |
|
|
|
// see if this frees up any samples past the end |
|
if (m_bMaxDomainSizeSet) |
|
{ |
|
float freePoint = sampleEnd - m_flMaxDomainSize; |
|
while (m_Samples[m_Samples.Head()].sampleEnd < freePoint) |
|
{ |
|
m_Samples.Remove(m_Samples.Head()); |
|
} |
|
} |
|
|
|
/* |
|
// see the max number of samples necessary to display this information reasonably precisely |
|
static const int MAX_LIKELY_GRAPH_WIDTH = 800; |
|
int maxSamplesNeeded = 2 * MAX_LIKELY_GRAPH_WIDTH / (m_iGraphBarWidth + m_iGraphBarGapWidth); |
|
if (m_Samples.Count() > 2) |
|
{ |
|
// see if we can collapse some items |
|
float highestSample = m_Samples[m_Samples.Tail()].sampleEnd; |
|
|
|
// iterate the items |
|
// always keep the head around so we have something to go against |
|
int sampleIndex = m_Samples.Next(m_Samples.Head()); |
|
int nextSampleIndex = m_Samples.Next(sampleIndex); |
|
|
|
while (m_Samples.IsInList(nextSampleIndex)) |
|
{ |
|
// calculate what sampling precision is actually needed to display this data |
|
float distanceFromEnd = highestSample - m_Samples[sampleIndex].sampleEnd; |
|
|
|
// if (distanceFromEnd < m_flDomainSize) |
|
// break; |
|
|
|
//!! this calculation is very incorrect |
|
float minNeededSampleSize = distanceFromEnd / (m_flMinDomainSize * maxSamplesNeeded); |
|
float sampleSize = m_Samples[nextSampleIndex].sampleEnd - m_Samples[sampleIndex].sampleEnd; |
|
|
|
if (sampleSize < minNeededSampleSize) |
|
{ |
|
// collapse the item into the next index |
|
m_Samples[nextSampleIndex].value = 0.5f * (m_Samples[nextSampleIndex].value + m_Samples[sampleIndex].value); |
|
|
|
// remove the item from the list |
|
m_Samples.Remove(sampleIndex); |
|
|
|
// move to the next item |
|
sampleIndex = nextSampleIndex; |
|
nextSampleIndex = m_Samples.Next(sampleIndex); |
|
} |
|
else |
|
{ |
|
// this item didn't need collapsing, so assume the next item won't |
|
break; |
|
} |
|
} |
|
} |
|
*/ |
|
|
|
InvalidateLayout(); |
|
Repaint(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: returns number of items that can be displayed |
|
//----------------------------------------------------------------------------- |
|
int GraphPanel::GetVisibleItemCount() |
|
{ |
|
return GetWide() / (m_iGraphBarWidth + m_iGraphBarGapWidth); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: lays out the graph |
|
//----------------------------------------------------------------------------- |
|
void GraphPanel::PerformLayout() |
|
{ |
|
BaseClass::PerformLayout(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: draws the graph |
|
//----------------------------------------------------------------------------- |
|
void GraphPanel::Paint() |
|
{ |
|
if (!m_Samples.Count()) |
|
return; |
|
|
|
// walk from right to left drawing the resampled data |
|
int sampleIndex = m_Samples.Tail(); |
|
int x = GetWide() - (m_iGraphBarWidth + m_iGraphBarGapWidth); |
|
|
|
// calculate how big each sample should be |
|
float sampleSize = m_flDomainSize / GetVisibleItemCount(); |
|
|
|
// calculate where in the domain we start resampling |
|
float resampleStart = m_Samples[sampleIndex].sampleEnd - sampleSize; |
|
// always resample from a sample point that is a multiple of the sampleSize |
|
resampleStart -= (float)fmod(resampleStart, sampleSize); |
|
|
|
// bar size multiplier |
|
float barSizeMultiplier = GetTall() / (m_flHighRange - m_flLowRange); |
|
|
|
// set render color |
|
surface()->DrawSetColor(GetFgColor()); |
|
|
|
// recalculate the sample range for dynamic resizing |
|
float flMinValue = m_Samples[m_Samples.Head()].value; |
|
float flMaxValue = m_Samples[m_Samples.Head()].value; |
|
|
|
// iterate the bars to draw |
|
while (x > 0 && m_Samples.IsInList(sampleIndex)) |
|
{ |
|
// move back the drawing point |
|
x -= (m_iGraphBarWidth + m_iGraphBarGapWidth); |
|
|
|
// collect the samples |
|
float value = 0.0f; |
|
float maxValue = 0.0f; |
|
int samplesTouched = 0; |
|
int prevSampleIndex = m_Samples.Previous(sampleIndex); |
|
while (m_Samples.IsInList(prevSampleIndex)) |
|
{ |
|
// take the value |
|
value += m_Samples[sampleIndex].value; |
|
samplesTouched++; |
|
|
|
// do some work to calculate the sample range |
|
if (m_Samples[sampleIndex].value < flMinValue) |
|
{ |
|
flMinValue = m_Samples[sampleIndex].value; |
|
} |
|
if (m_Samples[sampleIndex].value > flMaxValue) |
|
{ |
|
flMaxValue = m_Samples[sampleIndex].value; |
|
} |
|
if (m_Samples[sampleIndex].value > maxValue) |
|
{ |
|
maxValue = m_Samples[sampleIndex].value; |
|
} |
|
|
|
if (resampleStart < m_Samples[prevSampleIndex].sampleEnd) |
|
{ |
|
// we're out of the sampling range, we need to move on to the next sample |
|
sampleIndex = prevSampleIndex; |
|
prevSampleIndex = m_Samples.Previous(sampleIndex); |
|
} |
|
else |
|
{ |
|
// we're done with this resample |
|
// move back the resample start |
|
resampleStart -= sampleSize; |
|
// draw the current item |
|
break; |
|
} |
|
} |
|
|
|
// draw the item |
|
// show the max value in the sample, not the average |
|
int size = (int)(maxValue * barSizeMultiplier); |
|
// int size = (int)((value * barSizeMultiplier) / samplesTouched); |
|
surface()->DrawFilledRect(x, GetTall() - size, x + m_iGraphBarWidth, GetTall()); |
|
} |
|
|
|
// calculate our final range (for use next frame) |
|
if (m_bUseDynamicRange) |
|
{ |
|
flMinValue = 0; |
|
|
|
// find the range that fits |
|
for (int i = 0; i < m_RangeList.Count(); i++) |
|
{ |
|
if (m_RangeList[i] > flMaxValue) |
|
{ |
|
flMaxValue = m_RangeList[i]; |
|
break; |
|
} |
|
} |
|
|
|
m_flLowRange = flMinValue; |
|
m_flHighRange = flMaxValue; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: sets up colors |
|
//----------------------------------------------------------------------------- |
|
void GraphPanel::ApplySchemeSettings(IScheme *pScheme) |
|
{ |
|
BaseClass::ApplySchemeSettings(pScheme); |
|
|
|
SetFgColor(GetSchemeColor("GraphPanel.FgColor", pScheme)); |
|
SetBgColor(GetSchemeColor("GraphPanel.BgColor", pScheme)); |
|
SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); |
|
}
|
|
|