diff --git a/Library/MeterHistogram.cpp b/Library/MeterHistogram.cpp index d5a1c04d..2b643bf1 100644 --- a/Library/MeterHistogram.cpp +++ b/Library/MeterHistogram.cpp @@ -54,7 +54,9 @@ CMeterHistogram::CMeterHistogram(CMeterWindow* meterWindow, const WCHAR* name) : m_MinPrimaryValue(), m_MaxSecondaryValue(1.0), m_MinSecondaryValue(), - m_WidthChanged(true) + m_SizeChanged(true), + m_GraphStartLeft(false), + m_GraphHorizontalOrientation(false) { } @@ -112,15 +114,16 @@ void CMeterHistogram::Initialize() if (m_PrimaryImage.IsLoaded()) { int oldW = m_W; + int oldH = m_H; Bitmap* bitmap = m_PrimaryImage.GetImage(); m_W = bitmap->GetWidth(); m_H = bitmap->GetHeight(); - if (oldW != m_W) + if (oldW != m_W || oldH != m_H) { - m_WidthChanged = true; + m_SizeChanged = true; } } } @@ -154,25 +157,24 @@ void CMeterHistogram::Initialize() { DisposeBuffer(); - m_WidthChanged = false; + m_SizeChanged = false; } - else if (m_WidthChanged) + else if (m_SizeChanged) { DisposeBuffer(); // Create buffers for values - if (m_W > 0) + if (m_W > 0 || m_H > 0) { - m_PrimaryValues = new double[m_W]; - memset(m_PrimaryValues, 0, sizeof(double) * m_W); + int maxSize = m_GraphHorizontalOrientation ? m_H : m_W; + m_PrimaryValues = new double[maxSize](); if (m_SecondaryMeasure) { - m_SecondaryValues = new double[m_W]; - memset(m_SecondaryValues, 0, sizeof(double) * m_W); + m_SecondaryValues = new double[maxSize](); } } - m_WidthChanged = false; + m_SizeChanged = false; } } @@ -251,9 +253,9 @@ void CMeterHistogram::ReadConfig(CConfigParser& parser, const WCHAR* section) { if (m_PrimaryImageName.empty()) { - if (oldW != m_W) + if (oldW != m_W || oldH != m_H) { - m_WidthChanged = true; + m_SizeChanged = true; Initialize(); // Reload the image } } @@ -278,6 +280,72 @@ void CMeterHistogram::ReadConfig(CConfigParser& parser, const WCHAR* section) } } } + + const WCHAR* graph = parser.ReadString(section, L"GraphStart", L"RIGHT").c_str(); + if (_wcsicmp(graph, L"RIGHT") == 0) + { + m_GraphStartLeft = false; + } + else if (_wcsicmp(graph, L"LEFT") == 0) + { + m_GraphStartLeft = true; + } + else + { + LogWithArgs(LOG_ERROR, L"GraphStart=%s is not valid in [%s]", graph, m_Name.c_str()); + } + + graph = parser.ReadString(section, L"GraphOrientation", L"VERTICAL").c_str(); + if (_wcsicmp(graph, L"VERTICAL") == 0) + { + // Restart graph + if (m_GraphHorizontalOrientation) + { + m_GraphHorizontalOrientation = false; + DisposeBuffer(); + + // Create buffers for values + if (m_W > 0) + { + m_PrimaryValues = new double[m_W](); + if (m_SecondaryMeasure) + { + m_SecondaryValues = new double[m_W](); + } + } + } + else + { + m_GraphHorizontalOrientation = false; + } + } + else if (_wcsicmp(graph, L"HORIZONTAL") == 0) + { + // Restart graph + if (!m_GraphHorizontalOrientation) + { + m_GraphHorizontalOrientation = true; + DisposeBuffer(); + + // Create buffers for values + if (m_H > 0) + { + m_PrimaryValues = new double[m_H](); + if (m_SecondaryMeasure) + { + m_SecondaryValues = new double[m_H](); + } + } + } + else + { + m_GraphHorizontalOrientation = true; + } + } + else + { + LogWithArgs(LOG_ERROR, L"GraphOrientation=%s is not valid in [%s]", graph, m_Name.c_str()); + } } /* @@ -297,8 +365,9 @@ bool CMeterHistogram::Update() } ++m_MeterPos; - m_MeterPos %= m_W; - + int maxSize = m_GraphHorizontalOrientation ? m_H : m_W; + m_MeterPos %= maxSize; + m_MaxPrimaryValue = m_Measure->GetMaxValue(); m_MinPrimaryValue = m_Measure->GetMinValue(); m_MaxSecondaryValue = 0.0; @@ -314,7 +383,7 @@ bool CMeterHistogram::Update() // Go through all values and find the max double newValue = 0.0; - for (int i = 0; i < m_W; ++i) + for (int i = 0; i < maxSize; ++i) { newValue = max(newValue, m_PrimaryValues[i]); } @@ -335,7 +404,7 @@ bool CMeterHistogram::Update() if (m_SecondaryMeasure && m_SecondaryValues) { - for (int i = 0; i < m_W; ++i) + for (int i = 0; i < maxSize; ++i) { newValue = max(newValue, m_SecondaryValues[i]); } @@ -381,63 +450,157 @@ bool CMeterHistogram::Draw(Graphics& graphics) int x = GetX(); int y = GetY(); - for (int i = 0; i < m_W; ++i) + // Default values (GraphStart=Right, GraphOrientation=Vertical) + int i; + int startValue = 0; + int* endValueLHS = &i; + int* endValueRHS = &m_W; + int step = 1; + int endValue = -1; //(should be 0, but need to simulate <=) + + // GraphStart=Left, GraphOrientation=Vertical + if (m_GraphStartLeft && !m_GraphHorizontalOrientation) { - double value = (m_MaxPrimaryValue == 0.0) ? - 0.0 - : m_PrimaryValues[(i + m_MeterPos) % m_W] / m_MaxPrimaryValue; - value -= m_MinPrimaryValue; - int primaryBarHeight = (int)(m_H * value); - primaryBarHeight = min(m_H, primaryBarHeight); - primaryBarHeight = max(0, primaryBarHeight); + startValue = m_W - 1; + endValueLHS = &endValue; + endValueRHS = &i; + step = -1; + } + else if (m_GraphHorizontalOrientation && !m_Flip) + { + endValueRHS = &m_H; + } + else if (m_GraphHorizontalOrientation && m_Flip) + { + startValue = m_H - 1; + endValueLHS = &endValue; + endValueRHS = &i; + step = -1; + } - if (m_SecondaryMeasure) + // Horizontal or Vertical graph + if (m_GraphHorizontalOrientation) + { + for (i = startValue; *endValueLHS < *endValueRHS; i += step) { - value = (m_MaxSecondaryValue == 0.0) ? + double value = (m_MaxPrimaryValue == 0.0) ? 0.0 - : m_SecondaryValues[(i + m_MeterPos) % m_W] / m_MaxSecondaryValue; - value -= m_MinSecondaryValue; - int secondaryBarHeight = (int)(m_H * value); - secondaryBarHeight = min(m_H, secondaryBarHeight); - secondaryBarHeight = max(0, secondaryBarHeight); + : m_PrimaryValues[(i + (m_MeterPos % m_H)) % m_H] / m_MaxPrimaryValue; + value -= m_MinPrimaryValue; + int primaryBarHeight = (int)(m_W * value); + primaryBarHeight = min(m_W, primaryBarHeight); + primaryBarHeight = max(0, primaryBarHeight); - // Check which measured value is higher - int bothBarHeight = min(primaryBarHeight, secondaryBarHeight); - - // Cache image/color rectangle for the both lines + if (m_SecondaryMeasure) { - Rect& r = (m_Flip) ? - Rect(x + i, y, 1, bothBarHeight) - : Rect(x + i, y + m_H - bothBarHeight, 1, bothBarHeight); + value = (m_MaxSecondaryValue == 0.0) ? + 0.0 + : m_SecondaryValues[(i + m_MeterPos) % m_H] / m_MaxSecondaryValue; + value -= m_MinSecondaryValue; + int secondaryBarHeight = (int)(m_W * value); + secondaryBarHeight = min(m_W, secondaryBarHeight); + secondaryBarHeight = max(0, secondaryBarHeight); - bothPath.AddRectangle(r); // cache - } + // Check which measured value is higher + int bothBarHeight = min(primaryBarHeight, secondaryBarHeight); - // Cache the image/color rectangle for the rest - if (secondaryBarHeight > primaryBarHeight) - { - Rect& r = (m_Flip) ? - Rect(x + i, y + bothBarHeight, 1, secondaryBarHeight - bothBarHeight) - : Rect(x + i, y + m_H - secondaryBarHeight, 1, secondaryBarHeight - bothBarHeight); + // Cache image/color rectangle for the both lines + { + Rect& r = m_GraphStartLeft ? + Rect(x, y + startValue + (step * i), bothBarHeight, 1) + : Rect(x + m_W - bothBarHeight, y + startValue + (step * i), bothBarHeight, 1); - secondaryPath.AddRectangle(r); // cache + bothPath.AddRectangle(r); // cache + } + + // Cache the image/color rectangle for the rest + if (secondaryBarHeight > primaryBarHeight) + { + Rect& r = m_GraphStartLeft ? + Rect(x + bothBarHeight, y + startValue + (step * i), secondaryBarHeight - bothBarHeight, 1) + : Rect(x + m_W - secondaryBarHeight, y + startValue + (step * i), secondaryBarHeight - bothBarHeight, 1); + + secondaryPath.AddRectangle(r); // cache + } + else + { + Rect& r = m_GraphStartLeft ? + Rect(x + bothBarHeight, y + startValue + (step * i), primaryBarHeight - bothBarHeight, 1) + : Rect(x + m_W - primaryBarHeight, y + startValue + (step * i), primaryBarHeight - bothBarHeight, 1); + + primaryPath.AddRectangle(r); // cache + } } else { - Rect& r = (m_Flip) ? - Rect(x + i, y + bothBarHeight, 1, primaryBarHeight - bothBarHeight) - : Rect(x + i, y + m_H - primaryBarHeight, 1, primaryBarHeight - bothBarHeight); + Rect& r = m_GraphStartLeft ? + Rect(x, y + startValue + (step * i), primaryBarHeight, 1) + : Rect(x + m_W - primaryBarHeight, y + startValue + (step * i), primaryBarHeight, 1); primaryPath.AddRectangle(r); // cache } } - else + } + else // GraphOrientation=Vertical + { + for (i = startValue; *endValueLHS < *endValueRHS; i += step) { - Rect& r = (m_Flip) ? - Rect(x + i, y, 1, primaryBarHeight) - : Rect(x + i, y + m_H - primaryBarHeight, 1, primaryBarHeight); + double value = (m_MaxPrimaryValue == 0.0) ? + 0.0 + : m_PrimaryValues[(i + m_MeterPos) % m_W] / m_MaxPrimaryValue; + value -= m_MinPrimaryValue; + int primaryBarHeight = (int)(m_H * value); + primaryBarHeight = min(m_H, primaryBarHeight); + primaryBarHeight = max(0, primaryBarHeight); - primaryPath.AddRectangle(r); // cache + if (m_SecondaryMeasure) + { + value = (m_MaxSecondaryValue == 0.0) ? + 0.0 + : m_SecondaryValues[(i + m_MeterPos) % m_W] / m_MaxSecondaryValue; + value -= m_MinSecondaryValue; + int secondaryBarHeight = (int)(m_H * value); + secondaryBarHeight = min(m_H, secondaryBarHeight); + secondaryBarHeight = max(0, secondaryBarHeight); + + // Check which measured value is higher + int bothBarHeight = min(primaryBarHeight, secondaryBarHeight); + + // Cache image/color rectangle for the both lines + { + Rect& r = m_Flip ? + Rect(x + startValue + (step * i), y, 1, bothBarHeight) + : Rect(x + startValue + (step * i), y + m_H - bothBarHeight, 1, bothBarHeight); + + bothPath.AddRectangle(r); // cache + } + + // Cache the image/color rectangle for the rest + if (secondaryBarHeight > primaryBarHeight) + { + Rect& r = m_Flip ? + Rect(x + startValue + (step * i), y + bothBarHeight, 1, secondaryBarHeight - bothBarHeight) + : Rect(x + startValue + (step * i), y + m_H - secondaryBarHeight, 1, secondaryBarHeight - bothBarHeight); + + secondaryPath.AddRectangle(r); // cache + } + else + { + Rect& r = m_Flip ? + Rect(x + startValue + (step * i), y + bothBarHeight, 1, primaryBarHeight - bothBarHeight) + : Rect(x + startValue + (step * i), y + m_H - primaryBarHeight, 1, primaryBarHeight - bothBarHeight); + + primaryPath.AddRectangle(r); // cache + } + } + else + { + Rect& r = m_Flip ? + Rect(x + startValue + (step * i), y, 1, primaryBarHeight) + : Rect(x + startValue + (step * i), y + m_H - primaryBarHeight, 1, primaryBarHeight); + + primaryPath.AddRectangle(r); // cache + } } } diff --git a/Library/MeterHistogram.h b/Library/MeterHistogram.h index 37b714ec..0e6c57d0 100644 --- a/Library/MeterHistogram.h +++ b/Library/MeterHistogram.h @@ -42,18 +42,18 @@ private: void DisposeBuffer(); std::wstring m_SecondaryMeasureName; // Name of the secondary measure - CMeasure* m_SecondaryMeasure; // Pointer ot the secondary measure + CMeasure* m_SecondaryMeasure; // Pointer ot the secondary measure Gdiplus::Color m_PrimaryColor; // Color of the primary histogram - Gdiplus::Color m_SecondaryColor; // Color of the secondary histogram + Gdiplus::Color m_SecondaryColor; // Color of the secondary histogram Gdiplus::Color m_BothColor; // Color when the both histograms overlap - int m_MeterPos; // Position of the meter (i.e. where the new value should be placed) + int m_MeterPos; // Position of the meter (i.e. where the new value should be placed) bool m_Autoscale; bool m_Flip; std::wstring m_PrimaryImageName; // Name of the primary image for bitmap histograms - std::wstring m_SecondaryImageName; // Name of the secondary image for bitmap histograms - std::wstring m_BothImageName; // Name of the image for overlapping histograms + std::wstring m_SecondaryImageName; // Name of the secondary image for bitmap histograms + std::wstring m_BothImageName; // Name of the image for overlapping histograms CTintedImage m_PrimaryImage; // The primary bitmap CTintedImage m_SecondaryImage; // The secondary bitmap @@ -71,7 +71,10 @@ private: double m_MaxSecondaryValue; double m_MinSecondaryValue; - bool m_WidthChanged; + bool m_SizeChanged; + + bool m_GraphStartLeft; // Start graph to the Left or Right(default) + bool m_GraphHorizontalOrientation; // Horizontal or Vertical(default) static const WCHAR* c_PrimaryConfigArray[CTintedImage::ConfigCount]; static const WCHAR* c_SecondaryConfigArray[CTintedImage::ConfigCount]; diff --git a/Library/MeterLine.cpp b/Library/MeterLine.cpp index 4d55bf13..9be0651e 100644 --- a/Library/MeterLine.cpp +++ b/Library/MeterLine.cpp @@ -33,7 +33,9 @@ CMeterLine::CMeterLine(CMeterWindow* meterWindow, const WCHAR* name) : CMeter(me m_Flip(false), m_LineWidth(1.0), m_HorizontalColor(Color::Black), - m_CurrentPos() + m_CurrentPos(), + m_GraphStartLeft(false), + m_GraphHorizontalOrientation(false) { } @@ -56,6 +58,7 @@ void CMeterLine::Initialize() size_t colorsSize = m_Colors.size(); size_t allValuesSize = m_AllValues.size(); size_t num = (allValuesSize > 0) ? m_AllValues[0].size() : 0; + int maxSize = m_GraphHorizontalOrientation ? m_H : m_W; if (colorsSize != allValuesSize) { @@ -65,9 +68,9 @@ void CMeterLine::Initialize() { m_AllValues.push_back(std::vector()); - if (m_W > 0) + if (maxSize > 0) { - m_AllValues.back().assign(m_W, 0.0); + m_AllValues.back().assign(maxSize, 0.0); } } } @@ -77,11 +80,11 @@ void CMeterLine::Initialize() } } - if (m_W < 0 || num != (size_t)m_W) + if (maxSize < 0 || num != (size_t)maxSize) { - if (m_CurrentPos >= m_W) m_CurrentPos = 0; + if (m_CurrentPos >= maxSize) m_CurrentPos = 0; - num = (m_W < 0) ? 0 : m_W; + num = (maxSize < 0) ? 0 : maxSize; for (size_t i = 0; i < allValuesSize; ++i) { if (num != m_AllValues[i].size()) @@ -154,11 +157,60 @@ void CMeterLine::ReadConfig(CConfigParser& parser, const WCHAR* section) m_HorizontalColor = parser.ReadColor(section, L"HorizontalLineColor", color); // This is what it should be if (m_Initialized && - (oldLineCount != lineCount || - oldW != m_W)) + (oldLineCount != lineCount || oldW != m_W)) { Initialize(); } + + const WCHAR* graph = parser.ReadString(section, L"GraphStart", L"RIGHT").c_str(); + if (_wcsicmp(graph, L"RIGHT") == 0) + { + m_GraphStartLeft = false; + } + else if (_wcsicmp(graph, L"LEFT") == 0) + { + m_GraphStartLeft = true; + } + else + { + LogWithArgs(LOG_ERROR, L"StartFrom=%s is not valid in [%s]", graph, m_Name.c_str()); + } + + graph = parser.ReadString(section, L"GraphOrientation", L"VERTICAL").c_str(); + if (_wcsicmp(graph, L"VERTICAL") == 0) + { + // Restart graph + if (m_GraphHorizontalOrientation) + { + m_GraphHorizontalOrientation = false; + m_AllValues.clear(); + Initialize(); + m_CurrentPos = 0; + } + else + { + m_GraphHorizontalOrientation = false; + } + } + else if (_wcsicmp(graph, L"HORIZONTAL") == 0) + { + // Restart graph + if (!m_GraphHorizontalOrientation) + { + m_GraphHorizontalOrientation = true; + m_AllValues.clear(); + Initialize(); + m_CurrentPos = 0; + } + else + { + m_GraphHorizontalOrientation = true; + } + } + else + { + LogWithArgs(LOG_ERROR, L"GraphOrientation=%s is not valid in [%s]", graph, m_Name.c_str()); + } } /* @@ -169,7 +221,9 @@ bool CMeterLine::Update() { if (CMeter::Update() && m_Measure) { - if (m_W > 0) + int maxSize = m_GraphHorizontalOrientation ? m_H : m_W; + + if (maxSize > 0) { // Collect the values if (!m_Measure->IsDisabled()) @@ -190,7 +244,7 @@ bool CMeterLine::Update() } ++m_CurrentPos; - if (m_CurrentPos >= m_W) m_CurrentPos = 0; + if (m_CurrentPos >= maxSize) m_CurrentPos = 0; } return true; } @@ -203,7 +257,8 @@ bool CMeterLine::Update() */ bool CMeterLine::Draw(Graphics& graphics) { - if (!CMeter::Draw(graphics) || m_W <= 0) return false; + int maxSize = m_GraphHorizontalOrientation ? m_H : m_W; + if (!CMeter::Draw(graphics) || maxSize <= 0) return false; double maxValue = 0.0; int counter = 0; @@ -291,46 +346,136 @@ bool CMeterLine::Draw(Graphics& graphics) } // Draw all the lines - const REAL H = m_H - 1.0f; - counter = 0; - std::vector< std::vector >::const_iterator i = m_AllValues.begin(); - for (; i != m_AllValues.end(); ++i) + + if (m_GraphHorizontalOrientation) { - // Draw a line - REAL Y, oldY; - - const double scale = m_ScaleValues[counter] * H / maxValue; - - int pos = m_CurrentPos; - - oldY = (REAL)((*i)[pos] * scale); - oldY = min(oldY, H); - oldY = max(oldY, 0.0f); - oldY = y + ((m_Flip) ? oldY : H - oldY); - - // Cache all lines - GraphicsPath path; - for (int j = x + 1, R = x + m_W; j < R; ++j) + const REAL W = m_W - 1.0f; + counter = 0; + std::vector< std::vector >::const_iterator i = m_AllValues.begin(); + for (; i != m_AllValues.end(); ++i) { - ++pos; - if (pos >= m_W) pos = 0; + // Draw a line + REAL X, oldX; - Y = (REAL)((*i)[pos] * scale); - Y = min(Y, H); - Y = max(Y, 0.0f); - Y = y + ((m_Flip) ? Y : H - Y); + const double scale = m_ScaleValues[counter] * W / maxValue; - path.AddLine((REAL)(j - 1), oldY, (REAL)j, Y); + int pos = m_CurrentPos; - oldY = Y; + oldX = (REAL)((*i)[pos] * scale); + oldX = min(oldX, W); + oldX = max(oldX, 0.0f); + oldX = x + (m_GraphStartLeft ? oldX : W - oldX); + + // Cache all lines + GraphicsPath path; + + if (!m_Flip) + { + for (int j = y + 1, R = y + m_H; j < R; ++j) + { + ++pos; + if (pos >= m_H) pos = 0; + + X = (REAL)((*i)[pos] * scale); + X = min(X, W); + X = max(X, 0.0f); + X = x + (m_GraphStartLeft ? X : W - X); + + path.AddLine(oldX, (REAL)(j - 1), X, (REAL)j); + + oldX = X; + } + } + else + { + for (int j = y + m_H, R = y + 1; j > R; --j) + { + ++pos; + if (pos >= m_H) pos = 0; + + X = (REAL)((*i)[pos] * scale); + X = min(X, W); + X = max(X, 0.0f); + X = x + (m_GraphStartLeft ? X : W - X); + + path.AddLine(oldX, (REAL)(j - 1), X, (REAL)(j - 2)); + + oldX = X; + } + } + + // Draw cached lines + Pen pen(m_Colors[counter], (REAL)m_LineWidth); + pen.SetLineJoin(LineJoinBevel); + graphics.DrawPath(&pen, &path); + + ++counter; } + } + else + { + const REAL H = m_H - 1.0f; + counter = 0; + std::vector< std::vector >::const_iterator i = m_AllValues.begin(); + for (; i != m_AllValues.end(); ++i) + { + // Draw a line + REAL Y, oldY; - // Draw cached lines - Pen pen(m_Colors[counter], (REAL)m_LineWidth); - pen.SetLineJoin(LineJoinBevel); - graphics.DrawPath(&pen, &path); + const double scale = m_ScaleValues[counter] * H / maxValue; - ++counter; + int pos = m_CurrentPos; + + oldY = (REAL)((*i)[pos] * scale); + oldY = min(oldY, H); + oldY = max(oldY, 0.0f); + oldY = y + (m_Flip ? oldY : H - oldY); + + // Cache all lines + GraphicsPath path; + + if (!m_GraphStartLeft) + { + for (int j = x + 1, R = x + m_W; j < R; ++j) + { + ++pos; + if (pos >= m_W) pos = 0; + + Y = (REAL)((*i)[pos] * scale); + Y = min(Y, H); + Y = max(Y, 0.0f); + Y = y + (m_Flip ? Y : H - Y); + + path.AddLine((REAL)(j - 1), oldY, (REAL)j, Y); + + oldY = Y; + } + } + else + { + for (int j = x + m_W, R = x + 1; j > R; --j) + { + ++pos; + if (pos >= m_W) pos = 0; + + Y = (REAL)((*i)[pos] * scale); + Y = min(Y, H); + Y = max(Y, 0.0f); + Y = y + (m_Flip ? Y : H - Y); + + path.AddLine((REAL)(j - 1), oldY, (REAL)(j - 2), Y); + + oldY = Y; + } + } + + // Draw cached lines + Pen pen(m_Colors[counter], (REAL)m_LineWidth); + pen.SetLineJoin(LineJoinBevel); + graphics.DrawPath(&pen, &path); + + ++counter; + } } return true; diff --git a/Library/MeterLine.h b/Library/MeterLine.h index 4bd52d83..a3511df3 100644 --- a/Library/MeterLine.h +++ b/Library/MeterLine.h @@ -48,10 +48,13 @@ private: bool m_HorizontalLines; // If true, horizontal lines will ba drawn on the meter bool m_Flip; double m_LineWidth; - Gdiplus::Color m_HorizontalColor; // Color of the horizontal lines + Gdiplus::Color m_HorizontalColor; // Color of the horizontal lines - std::vector< std::vector > m_AllValues; // All the values to be drawn - int m_CurrentPos; // Place of the current value + std::vector< std::vector > m_AllValues; // All the values to be drawn + int m_CurrentPos; // Place of the current value + + bool m_GraphStartLeft; // Start graph to the Left or Right(default) + bool m_GraphHorizontalOrientation; // Horizontal or Vertical(default) }; #endif