Skip to content

Stream browser dialog DMM support #811

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
277 changes: 277 additions & 0 deletions src/ngscopeclient/Dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -424,3 +424,280 @@ bool Dialog::UnitInputWithExplicitApply(
ImGui::EndGroup();
return changed;
}

/**
@brief Segment on/off state for each of the 10 digits + "L" (needed for OL / Overload)
0b01000000 : Top h segment
0b00100000 : Top right v seglent
0b00010000 : Bottom right v segment
0b00001000 : Bottom h segment
0b00000100 : Bottom left v segment
0b00000010 : Top left v segment
0b00000001 : Center h segment
*/
static char SEGMENTS[] =
{
0x7E, // 0
0x30, // 1
0x6D, // 2
0x79, // 3
0x33, // 4
0x5B, // 5
0x5F, // 6
0x70, // 7
0x7F, // 8
0x7B, // 9
0x0E, // L
};

/**
@brief Render a single digit in 7 segment display style

@param drawList the drawList used for rendering
@param digit the digit to render
@param size the size of the digit
@param position the position of the digit
@param thickness the thickness of a segment
@param colorOn the color for an "on" segment
@param colorOff the color for an "off" segment
*/
void Dialog::Render7SegmentDigit(ImDrawList* drawList, uint8_t digit, ImVec2 size, ImVec2 position, float thickness, ImU32 colorOn, ImU32 colorOff)
{
// Inspired by https://github.com/ocornut/imgui/issues/3606#issuecomment-736855952
if(digit > 10)
digit = 10; // 10 is for L of OL (Overload)
size.y += thickness;
ImVec2 halfSize(size.x/2,size.y/2);
ImVec2 centerPosition(position.x+halfSize.x,position.y+halfSize.y);
float w = thickness;
float h = thickness/2;
float segmentSpec[7][4] =
{
{-1, -1, h, h}, // Top h segment
{ 1, -1, -h, h}, // Top right v seglent
{ 1, 0, -h, -h}, // Bottom right v segment
{-1, 1, h, -w * 1.5f},// Bottom h segment
{-1, 0, h, -h}, // Bottom left v segment
{-1, -1, h, h}, // Top left v segment
{-1, 0, h, -h}, // Center h segment
};
for(int i = 0; i < 7; i++)
{
ImVec2 topLeft, bottomRight;
if(i % 3 == 0)
{
// Horizontal segment
topLeft = ImVec2(centerPosition.x + segmentSpec[i][0] * halfSize.x + segmentSpec[i][2], centerPosition.y + segmentSpec[i][1] * halfSize.y + segmentSpec[i][3] - h);
bottomRight = ImVec2(topLeft.x + size.x - w, topLeft.y + w);
}
else
{
// Vertical segment
topLeft = ImVec2(centerPosition.x + segmentSpec[i][0] * halfSize.x + segmentSpec[i][2] - h, centerPosition.y + segmentSpec[i][1] * halfSize.y + segmentSpec[i][3]);
bottomRight = ImVec2(topLeft.x + w, topLeft.y + halfSize.y - w);
}
ImVec2 segmentSize = bottomRight - topLeft;
float space = w * 0.6;
float u = space - h;
if(segmentSize.x > segmentSize.y)
{
// Horizontal segment
ImVec2 points[] =
{
{topLeft.x + u, topLeft.y + segmentSize.y * .5f},
{topLeft.x + space, topLeft.y},
{bottomRight.x - space, topLeft.y},
{bottomRight.x - u, topLeft.y + segmentSize.y * .5f},
{bottomRight.x - space, bottomRight.y},
{topLeft.x + space, bottomRight.y}
};
drawList->AddConvexPolyFilled(points, 6, (SEGMENTS[digit] >> (6 - i)) & 1 ? colorOn : colorOff);
}
else
{
// Vertical segment
ImVec2 points[] = {
{topLeft.x + segmentSize.x * .5f, topLeft.y + u},
{bottomRight.x, topLeft.y + space},
{bottomRight.x, bottomRight.y - space},
{bottomRight.x - segmentSize.x * .5f, bottomRight.y - u},
{topLeft.x, bottomRight.y - space},
{topLeft.x, topLeft.y + space}};
drawList->AddConvexPolyFilled(points, 6, (SEGMENTS[digit] >> (6 - i)) & 1 ? colorOn : colorOff);
}
}
}

// @brief ratio between unit font size and digit size
#define UNIT_SCALE 0.80f

// @brief ratio between digit width and height
#define DIGIT_WIDTH_RATIO 0.50f

/**
@brief Render a numeric value with a 7 segment display style

@param value the string representation of the value to display (may include the unit)
@param color the color to use
@param digitHeight the height of a digit
*/
void Dialog::Render7SegmentValue(const std::string& value, ImVec4 color, float digitHeight)
{
bool ignoredClicked, ignoredHovered;
Render7SegmentValue(value,color,digitHeight,ignoredClicked,ignoredHovered,false);
}

/**
@brief Render a numeric value with a 7 segment display style

@param value the string representation of the value to display (may include the unit)
@param color the color to use
@param digitHeight the height of a digit
@param clicked output value for clicked state
@param hovered output value for hovered state
@param clickable true (default) if the displayed value should be clickable
*/
void Dialog::Render7SegmentValue(const std::string& value, ImVec4 color, float digitHeight, bool &clicked, bool &hovered, bool clickable)
{
ImDrawList* draw_list = ImGui::GetWindowDrawList();

// Compute digit width according th height
float digitWidth = digitHeight*DIGIT_WIDTH_RATIO;

// Compute front and back color
float bgmul = 0.15;
auto bcolor = ImGui::ColorConvertFloat4ToU32(ImVec4(color.x*bgmul, color.y*bgmul, color.z*bgmul, color.w));
auto fcolor = ImGui::ColorConvertFloat4ToU32(color);


// Parse value string to get integer and fractional part + unit
bool inIntPart = true;
bool inFractPart = false;
vector<uint8_t> intPart;
vector<uint8_t> fractPart;
string unit;

if(value == UNIT_OVERLOAD_LABEL)
{
// Overload
intPart.push_back(0);
intPart.push_back(10); // 10 is for L
unit = "Inf.";
}
else
{
// Iterate on each char of the value string
for(const char c : value)
{
if(c >= '0' && c <='9')
{
// This is a numeric digit
if(inIntPart)
intPart.push_back((uint8_t)(c-'0'));
else if(inFractPart)
fractPart.push_back((uint8_t)(c-'0'));
else
unit += c;
}
else if(c == '.' || c == std::use_facet<std::numpunct<char> >(std::locale()).decimal_point() || c == ',')
{
// This is the decimal separator
if(inIntPart)
{
inFractPart = true;
inIntPart = false;
}
else
LogWarning("Unexpected decimal separator '%c' in value '%s'.\n",c,value.c_str());
}
else if(isspace(c) || c == std::use_facet< std::numpunct<char> >(std::locale()).thousands_sep())
{
// We ingore spaces (except in unit part)
if(inIntPart || inFractPart) {} // Ignore
else
unit += c;
}
else // Anything else
{
// This is the unit
inFractPart = false;
inIntPart = false;
unit += c;
}
}
// Trim the unit string
unit = Trim(unit);

// Fill fractional part with 2 zeros if it's empty
if(fractPart.empty())
{
fractPart.push_back(0);
fractPart.push_back(0);
}
}

// Segment thickness
float thickness = digitHeight/10;

// Space between digits
float spacing = 0.08 * digitWidth;

// Size of decimal separator
float dotSize = 2*thickness;

// Size of unit font and unit text
float unitSize = digitHeight*UNIT_SCALE;
float unitTextWidth = ImGui::GetFont()->CalcTextSizeA(unitSize,FLT_MAX, 0.0f,unit.c_str()).x;

ImVec2 size(digitWidth*(intPart.size()+fractPart.size())+dotSize+2*spacing+unitTextWidth+thickness, digitHeight);

if(clickable)
{
bgmul = 0.0f;
ImVec4 buttonColor = ImVec4(color.x*bgmul, color.y*bgmul, color.z*bgmul, 0);
bgmul = 0.2f;
ImVec4 buttonColorHovered = ImVec4(color.x*bgmul, color.y*bgmul, color.z*bgmul, color.w);
bgmul = 0.3f;
ImVec4 buttonColorActive = ImVec4(color.x*bgmul, color.y*bgmul, color.z*bgmul, color.w);
ImGui::PushStyleColor(ImGuiCol_Button, buttonColor);
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, buttonColorHovered);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, buttonColorActive);
clicked |= ImGui::Button(" ",size);
hovered |= ImGui::IsItemHovered();
ImGui::PopStyleColor(3);
if(hovered)
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
}
else
ImGui::InvisibleButton("seven", size, ImGuiButtonFlags_EnableNav);

ImVec2 position = ImGui::GetItemRectMin();

// Actual digit width (without space)
float digitActualWidth = digitWidth - spacing;
// Current x position
float x = 0;

// Integer part
for(size_t i = 0; i < intPart.size(); i++)
{
Render7SegmentDigit(draw_list, intPart[i], ImVec2(digitActualWidth, digitHeight), ImVec2(position.x + x, position.y),thickness,fcolor,bcolor);
x += digitWidth;
}
// Decimal separator
x+= spacing;
draw_list->AddCircleFilled(ImVec2(position.x+x+dotSize/2-spacing/2,position.y+digitHeight-dotSize/2),dotSize/2,fcolor);
x+= dotSize;
x+= spacing;
// Factional part
for(size_t i = 0; i < fractPart.size(); i++)
{
Render7SegmentDigit(draw_list, fractPart[i], ImVec2(digitActualWidth, digitHeight), ImVec2(position.x + x, position.y),thickness,fcolor,bcolor);
x += digitWidth;
}
// Unit
draw_list->AddText(NULL,unitSize,
ImVec2(position.x + x + thickness, position.y),
fcolor,
unit.c_str());
}
5 changes: 5 additions & 0 deletions src/ngscopeclient/Dialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ class Dialog
void RenderErrorPopup();
void ShowErrorPopup(const std::string& title, const std::string& msg);

void Render7SegmentDigit(ImDrawList* drawList, uint8_t digit, ImVec2 size, ImVec2 position, float thikness, ImU32 colorOn, ImU32 colorOff);
void Render7SegmentValue(const std::string& value, ImVec4 color, float digitHeight);
void Render7SegmentValue(const std::string& value, ImVec4 color, float digitHeight, bool &clicked, bool &hovered, bool clickable = true);


bool m_open;
std::string m_id;
std::string m_title;
Expand Down
8 changes: 8 additions & 0 deletions src/ngscopeclient/PreferenceSchema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ void PreferenceManager::InitializeDefaults()
.Description("Color for icon captions"));

auto& stream = appearance.AddCategory("Stream Browser");
stream.AddPreference(
Preference::Bool("use_7_segment_display", true)
.Label("Use 7 segment style display")
.Description("Use 7 segment style display for DMM and PSU values"));
stream.AddPreference(
Preference::Real("instrument_badge_latch_duration", 0.4)
.Label("Intrument badge latch duration (seconds)")
Expand Down Expand Up @@ -241,6 +245,10 @@ void PreferenceManager::InitializeDefaults()
Preference::Color("psu_meas_label_color", ColorFromString("#00C100"))
.Label("PSU measured label color")
.Description("Color for PSU 'meas.' label"));
stream.AddPreference(
Preference::Color("psu_7_segment_color", ColorFromString("#B2FFFF"))
.Label("PSU 7 segment display color")
.Description("Color for PSU 7 segment style display"));
stream.AddPreference(
Preference::Color("awg_hiz_badge_color", ColorFromString("#666600"))
.Label("Function Generator HI-Z badge color")
Expand Down
Loading
Loading