diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml
index faa48a8d..433022ee 100644
--- a/.github/workflows/builds.yml
+++ b/.github/workflows/builds.yml
@@ -31,6 +31,7 @@ env:
jobs:
builds-and-tests:
strategy:
+ fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
target: [dbg, opt]
@@ -230,7 +231,7 @@ jobs:
if: matrix.build-system == 'cmake'
run: |
python -m pip install --upgrade pip
- pip install scikit-image
+ python -m pip install -r glvis/tests/requirements.txt
- name: setup Linux testing dependencies
if: matrix.build-system == 'cmake' && matrix.os == 'ubuntu-latest'
diff --git a/CHANGELOG b/CHANGELOG
index e0b0e9a5..a328b3f8 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -25,6 +25,12 @@ Version 4.2.1 (development)
- Added a compilation parameter for the default font size.
+- Added option to specify the floating point number formatting in GLVis axes and
+ colorbar. Use 'Alt+a' for the axes and 'Alt+c' for the colorbar. Formatting
+ can also be specified in socket stream or glvis script with axis_numberformat
+ or colorbar_numberformat, followed by a C-like formatting string, for example
+ "colorbar_numberformat '%+06.1f'".
+
- Added visualization of quadrature data (QuadratureFunction in MFEM). Both
loading from file, with the new command line argument '-q', or from a socket
stream, with the keyword 'quadrature', are supported. Three visualization
diff --git a/README.md b/README.md
index ca73cc36..530fbe73 100644
--- a/README.md
+++ b/README.md
@@ -165,6 +165,8 @@ Key commands
- . – Start/stop `z`-spinning (speed/direction can be controlled with 0 / Enter)
- ←, →, ↑, ↓ – Manual rotation
- 1, 2, 3, 4, 5, 6, 7, 8, 9 – Manual rotation along coordinate axes
+- Alt + a – Set axes number format
+- Alt + c – Set colorbar number format
- Ctrl + ←, →, ↑, ↓ – Translate the viewpoint
- Ctrl + o – Toggle an element ordering curve
- n / N – Cycle through numberings. The options are:
diff --git a/glvis.cpp b/glvis.cpp
index 82700368..7d570f1f 100644
--- a/glvis.cpp
+++ b/glvis.cpp
@@ -769,6 +769,28 @@ void ExecuteScriptCommand()
cout << min << ' ' << max << ' ' << num << endl;
MyExpose();
}
+ else if (word == "axis_numberformat")
+ {
+ char delim;
+ string axis_formatting;
+ scr >> ws >> delim;
+ getline(scr, axis_formatting, delim);
+ cout << "Script: axis_numberformat: " << flush;
+ vs->SetAxisNumberFormat(axis_formatting);
+ cout << axis_formatting << endl;
+ MyExpose();
+ }
+ else if (word == "colorbar_numberformat")
+ {
+ char delim;
+ string colorbar_formatting;
+ scr >> ws >> delim;
+ getline(scr, colorbar_formatting, delim);
+ cout << "Script: colorbar_numberformat: " << flush;
+ vs->SetColorbarNumberFormat(colorbar_formatting);
+ cout << colorbar_formatting << endl;
+ MyExpose();
+ }
else if (word == "window")
{
scr >> window_x >> window_y >> window_w >> window_h;
@@ -1489,6 +1511,8 @@ int main (int argc, char *argv[])
cout << "Can not open script: " << script_file << endl;
return 1;
}
+ cout << "Running script from file: " << script_file << endl;
+ cout << "You may need to press to execute the script steps." << endl;
PlayScript(scr);
return 0;
}
diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp
index 3660bafd..7c92d0f0 100644
--- a/lib/aux_vis.cpp
+++ b/lib/aux_vis.cpp
@@ -14,6 +14,7 @@
#include
#include
#include
+#include
#include "mfem.hpp"
#include "sdl.hpp"
@@ -1783,3 +1784,57 @@ void SetFont(const std::string& fn)
<< "', height = " << font_size << endl;
#endif
}
+
+function NumberFormatter(int precision, char format, bool showsign)
+{
+ return [precision, format, showsign](double x) -> string
+ {
+ ostringstream oss;
+ switch (format) {
+ case 'f':
+ oss << fixed;
+ break;
+ case 's':
+ oss << scientific;
+ break;
+ case 'd':
+ oss << defaultfloat;
+ break;
+ default:
+ MFEM_WARNING("Unknown formatting type. Using default. "
+ << "Valid options include: ['f', 's', 'd']" << endl);
+ oss << defaultfloat;
+ break;
+ };
+ if (showsign)
+ {
+ oss << showpos;
+ }
+ oss << setprecision(precision) << x;
+ return oss.str();
+ };
+}
+
+function NumberFormatter(string formatting)
+{
+ if (!isValidNumberFormatting(formatting))
+ {
+ MFEM_WARNING("Invalid formatting string. Using default. " << endl);
+ return NumberFormatter();
+ }
+ else
+ {
+ return [formatting](double x) -> string
+ {
+ char buf[64];
+ snprintf(buf, sizeof(buf), formatting.c_str(), x);
+ return string(buf);
+ };
+ }
+}
+
+bool isValidNumberFormatting(const string& formatting)
+{
+ regex rgx = regex(R"(%[\-+#0\s]?[0-9]{0,3}\.?[0-9]{0,3}[FfEeGg])");
+ return regex_match(formatting, rgx);
+}
diff --git a/lib/aux_vis.hpp b/lib/aux_vis.hpp
index 97d03ccf..3361a153 100644
--- a/lib/aux_vis.hpp
+++ b/lib/aux_vis.hpp
@@ -136,5 +136,52 @@ bool SetFont(const vector& patterns, int height);
void SetFont(const std::string& fn);
void SetUseHiDPI(bool status);
+function NumberFormatter(int precision=4, char format='d', bool showsign=false);
+function NumberFormatter(string formatting);
+bool isValidNumberFormatting(const string& formatting);
+
+// This is a helper function for prompting the user for inputs. The benefit
+// over using just `cin >> input` is that you can specify a type and optionally
+// a validator lambda. The a validator if not specified, it defaults to the
+// True function. If the input cannot be type casted to the expected type, or
+// if it fails the validation, the user is asked again for a new input.
+template
+T prompt(const string question,
+ const T* default_value = nullptr,
+ function validator = [](T) { return true; })
+{
+ T input;
+ string strInput;
+
+ while (true)
+ {
+ cout << question << " ";
+ getline(cin, strInput);
+ stringstream buf(strInput);
+
+ if (strInput.empty() && default_value != nullptr)
+ {
+ cout << "Input empty. Using default value: " << *default_value << endl;
+ return *default_value;
+ }
+
+ if (buf >> input)
+ {
+ if (validator(input))
+ {
+ return input;
+ }
+ else
+ {
+ cout << "Input is not valid. Please try again." << endl;
+ }
+ }
+ else
+ {
+ cout << "Input can not be casted to expected type. Please try again." << endl;
+ }
+ }
+ return input;
+}
#endif
diff --git a/lib/sdl.cpp b/lib/sdl.cpp
index a4262be8..9b932a6c 100644
--- a/lib/sdl.cpp
+++ b/lib/sdl.cpp
@@ -267,51 +267,34 @@ void SdlWindow::mouseEventUp(SDL_MouseButtonEvent& eb)
}
}
-void SdlWindow::keyEvent(SDL_Keysym& ks)
+void SdlWindow::keyDownEvent(SDL_Keysym& ks)
{
- bool handled = false;
- if (ks.sym >= 128 || ks.sym < 32)
- {
- if (onKeyDown[ks.sym])
- {
- onKeyDown[ks.sym](ks.mod);
- handled = true;
- }
- }
- else if (ks.sym < 256 && std::isdigit(ks.sym))
- {
- if (!(SDL_GetModState() & KMOD_SHIFT))
- {
- // handle number key event here
- onKeyDown[ks.sym](ks.mod);
- handled = true;
- }
- }
- else if (ctrlDown)
- {
- if (onKeyDown[ks.sym])
- {
- onKeyDown[ks.sym](ks.mod);
- handled = true;
- }
- }
- if (ks.sym == SDLK_RCTRL || ks.sym == SDLK_LCTRL)
- {
- ctrlDown = true;
+ // Some keyDown events will be followed by a textInput event which will
+ // handle key translation due to Shift or CapsLock, so we leave such events
+ // to be processed there.
+ // Note: the same condition has to be used in signalKeyDown().
+ if ((ks.sym >= 32 && ks.sym < 127) &&
+ (ks.mod & ~(KMOD_SHIFT | KMOD_CAPS)) == 0)
+ {
+ lastKeyDownProcessed = false;
+ return;
}
- if (handled)
+ // If any 'mod' key other than KMOD_SHIFT or KMOD_CAPS is pressed, or the key
+ // is not in the range [32,127) then we processed the event here.
+ lastKeyDownProcessed = true;
+ if (onKeyDown[ks.sym])
{
+ onKeyDown[ks.sym](ks.mod);
+
+ // Record the key in 'saved_keys':
bool isAlt = ks.mod & (KMOD_ALT);
bool isCtrl = ks.mod & (KMOD_CTRL);
saved_keys += "[";
if (isCtrl) { saved_keys += "C-"; }
if (isAlt) { saved_keys += "Alt-"; }
- if (ks.sym < 256 && std::isalpha(ks.sym))
+ if (ks.sym >= 32 && ks.sym < 127)
{
- // key with corresponding text output
- char c = ks.sym;
- if (!(ks.mod & KMOD_SHIFT)) { c = std::tolower(c); }
- saved_keys += c;
+ saved_keys += (char)(ks.sym);
}
else
{
@@ -321,25 +304,21 @@ void SdlWindow::keyEvent(SDL_Keysym& ks)
}
}
-void SdlWindow::keyEvent(char c)
+void SdlWindow::textInputEvent(const SDL_TextInputEvent &tie)
{
- if (!std::isdigit(c) && onKeyDown[c])
- {
- SDL_Keymod mods = SDL_GetModState();
- bool isAlt = mods & (KMOD_ALT);
- bool isCtrl = mods & (KMOD_CTRL);
+ // This event follows a keyDown event where we've recorded if the event was
+ // processed in keyDownEvent(). If it was not processed, we do it here.
+ if (lastKeyDownProcessed) { return; }
+ const char c = tie.text[0];
+ if (onKeyDown[c])
+ {
+ // Keys with 'mods' (other than Shift and CapsLock) are processed in
+ // keyDownEvent().
+ const int mods = 0;
onKeyDown[c](mods);
- if (isAlt || isCtrl)
- {
- saved_keys += "[";
- if (isCtrl) { saved_keys += "C-"; }
- if (isAlt) { saved_keys += "Alt-"; }
- }
+
+ // Record the key in 'saved_keys':
saved_keys += c;
- if (isAlt || isCtrl)
- {
- saved_keys += "]";
- }
}
}
@@ -403,17 +382,30 @@ void SdlWindow::mainIter()
keep_going = true;
break;
case SDL_KEYDOWN:
- keyEvent(e.key.keysym);
+// For debugging: uncomment the next line to track key events.
+// #define TRACK_KEY_EVENTS
+#ifdef TRACK_KEY_EVENTS
+ cout << "Event: SDL_KEYDOWN sym=" << e.key.keysym.sym
+ << " mod=" << e.key.keysym.mod << endl;
+#endif
+ keyDownEvent(e.key.keysym);
break;
case SDL_KEYUP:
- if (e.key.keysym.sym == SDLK_LCTRL
- || e.key.keysym.sym == SDLK_RCTRL)
- {
- ctrlDown = false;
- }
+#ifdef TRACK_KEY_EVENTS
+ cout << "Event: SDL_KEYUP sym=" << e.key.keysym.sym
+ << " mod=" << e.key.keysym.mod << endl;
+#endif
break;
case SDL_TEXTINPUT:
- keyEvent(e.text.text[0]);
+#ifdef TRACK_KEY_EVENTS
+ cout << "Event: SDL_TEXTINPUT text[0..3]="
+ << (int)(unsigned char)(e.text.text[0])
+ << ' ' << (int)(unsigned char)(e.text.text[1])
+ << ' ' << (int)(unsigned char)(e.text.text[2])
+ << ' ' << (int)(unsigned char)(e.text.text[3])
+ << " (as codes 0-255)" << endl;
+#endif
+ textInputEvent(e.text);
break;
case SDL_MOUSEMOTION:
motionEvent(e.motion);
@@ -630,20 +622,21 @@ void SdlWindow::setWindowPos(int x, int y)
void SdlWindow::signalKeyDown(SDL_Keycode k, SDL_Keymod m)
{
SDL_Event event;
- if (k >= 32 && k < 128)
+
+ event.type = SDL_KEYDOWN;
+ event.key.windowID = window_id;
+ event.key.keysym.sym = k;
+ event.key.keysym.mod = m;
+ queueEvents({ event });
+
+ // The same condition as in keyDownEvent().
+ if ((k >= 32 && k < 127) && (m & ~(KMOD_SHIFT | KMOD_CAPS)) == 0)
{
event.type = SDL_TEXTINPUT;
event.text.windowID = window_id;
event.text.text[0] = k;
+ queueEvents({ event });
}
- else
- {
- event.type = SDL_KEYDOWN;
- event.key.windowID = window_id;
- event.key.keysym.sym = k;
- event.key.keysym.mod = m;
- }
- queueEvents({ event });
}
void SdlWindow::swapBuffer()
diff --git a/lib/sdl.hpp b/lib/sdl.hpp
index 6d157328..df5fb987 100644
--- a/lib/sdl.hpp
+++ b/lib/sdl.hpp
@@ -103,8 +103,6 @@ class SdlWindow
TouchDelegate onTouchPinch{nullptr};
TouchDelegate onTouchRotate{nullptr};
- bool ctrlDown{false};
-
#ifdef __EMSCRIPTEN__
std::string canvas_id_;
#endif
@@ -127,14 +125,15 @@ class SdlWindow
bool takeScreenshot{false};
std::string screenshot_file;
bool screenshot_convert;
+ bool lastKeyDownProcessed;
// internal event handlers
void windowEvent(SDL_WindowEvent& ew);
void motionEvent(SDL_MouseMotionEvent& em);
void mouseEventDown(SDL_MouseButtonEvent& eb);
void mouseEventUp(SDL_MouseButtonEvent& eb);
- void keyEvent(SDL_Keysym& ks);
- void keyEvent(char c);
+ void keyDownEvent(SDL_Keysym& ks);
+ void textInputEvent(const SDL_TextInputEvent &e);
void multiGestureEvent(SDL_MultiGestureEvent & e);
bool is_multithreaded{true};
diff --git a/lib/threads.cpp b/lib/threads.cpp
index 9044fc19..2a410849 100644
--- a/lib/threads.cpp
+++ b/lib/threads.cpp
@@ -205,6 +205,36 @@ int GLVisCommand::AxisLabels(const char *a_x, const char *a_y, const char *a_z)
return 0;
}
+int GLVisCommand::AxisNumberFormat(string formatting)
+{
+ if (lock() < 0)
+ {
+ return -1;
+ }
+ command = AXIS_NUMBERFORMAT;
+ axis_formatting = formatting;
+ if (signal() < 0)
+ {
+ return -2;
+ }
+ return 0;
+}
+
+int GLVisCommand::ColorbarNumberFormat(string formatting)
+{
+ if (lock() < 0)
+ {
+ return -1;
+ }
+ command = COLORBAR_NUMBERFORMAT;
+ colorbar_formatting = formatting;
+ if (signal() < 0)
+ {
+ return -2;
+ }
+ return 0;
+}
+
int GLVisCommand::Pause()
{
if (lock() < 0)
@@ -525,6 +555,24 @@ int GLVisCommand::Execute()
break;
}
+ case AXIS_NUMBERFORMAT:
+ {
+ cout << "Command: axis_numberformat: '"
+ << axis_formatting << "'" << endl;
+ (*vs)->SetAxisNumberFormat(axis_formatting);
+ MyExpose();
+ break;
+ }
+
+ case COLORBAR_NUMBERFORMAT:
+ {
+ cout << "Command: colorbar_numberformat: '"
+ << colorbar_formatting << "'" << endl;
+ (*vs)->SetColorbarNumberFormat(colorbar_formatting);
+ MyExpose();
+ break;
+ }
+
case PAUSE:
{
cout << "Command: pause: ";
@@ -1202,6 +1250,48 @@ void communication_thread::execute()
goto comm_terminate;
}
}
+ else if (ident == "axis_numberformat")
+ {
+ char c;
+ string formatting;
+
+ *is[0] >> ws >> c; // read the opening char
+ getline(*is[0], formatting, c); // read formatting string & use c for termination
+
+ // all processors sent the command
+ for (size_t i = 1; i < is.size(); i++)
+ {
+ *is[i] >> ws >> ident; // 'axis_numberformat'
+ *is[i] >> ws >> c;
+ getline(*is[i], ident, c);
+ }
+
+ if (glvis_command->AxisNumberFormat(formatting))
+ {
+ goto comm_terminate;
+ }
+ }
+ else if (ident == "colorbar_numberformat")
+ {
+ char c;
+ string formatting;
+
+ *is[0] >> ws >> c; // read the opening char
+ getline(*is[0], formatting, c); // read formatting string & use c for termination
+
+ // all processors sent the command
+ for (size_t i = 1; i < is.size(); i++)
+ {
+ *is[i] >> ws >> ident; // 'colorbar_numberformat'
+ *is[i] >> ws >> c;
+ getline(*is[i], ident, c);
+ }
+
+ if (glvis_command->ColorbarNumberFormat(formatting))
+ {
+ goto comm_terminate;
+ }
+ }
else if (ident == "shading")
{
string shd;
diff --git a/lib/threads.hpp b/lib/threads.hpp
index 8b5bce2e..77579f3e 100644
--- a/lib/threads.hpp
+++ b/lib/threads.hpp
@@ -57,7 +57,9 @@ class GLVisCommand
PLOT_CAPTION = 18,
AXIS_LABELS = 19,
PALETTE_REPEAT = 20,
- LEVELLINES = 21
+ LEVELLINES = 21,
+ AXIS_NUMBERFORMAT = 22,
+ COLORBAR_NUMBERFORMAT = 23
};
std::atomic command_ready{false};
@@ -88,6 +90,8 @@ class GLVisCommand
int lvl_num;
double camera[9];
std::string autopause_mode;
+ std::string axis_formatting;
+ std::string colorbar_formatting;
// internal variables
int autopause;
@@ -125,6 +129,8 @@ class GLVisCommand
int Palette(int pal);
int PaletteRepeat(int n);
int Levellines(double minv, double maxv, int number);
+ int AxisNumberFormat(string formatting);
+ int ColorbarNumberFormat(string formatting);
int Camera(const double cam[]);
int Autopause(const char *mode);
diff --git a/lib/vsdata.cpp b/lib/vsdata.cpp
index 3c4add44..5b94e113 100644
--- a/lib/vsdata.cpp
+++ b/lib/vsdata.cpp
@@ -414,6 +414,22 @@ void VisualizationSceneScalarData::Arrow(gl3::GlDrawable& buf,
}
}
+void VisualizationSceneScalarData::SetColorbarNumberFormat(int precision,
+ char format,
+ bool showsign)
+{
+ colorbar_formatter = NumberFormatter(precision, format, showsign);
+ // The first two arguments are required but I don't think they are used?
+ PrepareColorBar(0,0);
+}
+
+void VisualizationSceneScalarData::SetColorbarNumberFormat(string formatting)
+{
+ colorbar_formatter = NumberFormatter(formatting);
+ // The first two arguments are required but I don't think they are used?
+ PrepareColorBar(0,0);
+}
+
void VisualizationSceneScalarData::PrepareColorBar (double minval,
double maxval,
Array *mesh_level,
@@ -496,9 +512,7 @@ void VisualizationSceneScalarData::PrepareColorBar (double minval,
val = ULogVal(i / 4.0);
- ostringstream buf;
- buf << setprecision(4) << val;
- color_bar.addText(text_x,Y,posz, buf.str());
+ color_bar.addText(text_x,Y,posz,colorbar_formatter(val));
}
}
else
@@ -508,9 +522,7 @@ void VisualizationSceneScalarData::PrepareColorBar (double minval,
val = (*mesh_level)[i];
Y = miny + (maxy - miny) * LogUVal(val);
- ostringstream buf;
- buf << setprecision(4) << val;
- color_bar.addText(text_x,Y,posz, buf.str());
+ color_bar.addText(text_x,Y,posz,colorbar_formatter(val));
}
}
@@ -521,9 +533,7 @@ void VisualizationSceneScalarData::PrepareColorBar (double minval,
val = (*lsurf_levels)[i];
Y = miny + (maxy - miny) * LogUVal(val);
- ostringstream buf;
- buf << setprecision(4) << val;
- color_bar.addText(text_x,Y,posz, buf.str());
+ color_bar.addText(text_x,Y,posz,colorbar_formatter(val));
}
}
updated_bufs.emplace_back(&color_bar);
@@ -550,10 +560,31 @@ void VisualizationSceneScalarData::PrepareCaption()
thread_local VisualizationSceneScalarData * vsdata;
extern thread_local VisualizationScene * locscene;
-void KeycPressed()
+void KeycPressed(GLenum state)
{
- vsdata->ToggleDrawColorbar();
- SendExposeEvent();
+ if (state & KMOD_ALT)
+ {
+ cout << "Setting colorbar number formatting..." << endl;
+ int default_precision = 4;
+ char default_format = 'd';
+ bool default_showsign = false;
+
+ int precision = prompt("Enter precision (4): ",
+ &default_precision,
+ [](int p){ return p>=0; });
+ char format = prompt("Enter format [(d)efault, (f)ixed, (s)cientific] (d): ",
+ &default_format,
+ [](char c){ return c=='d' || c=='f' || c=='s'; });
+ bool showsign = prompt("Show sign? [(1)true, (0)false] (0): ",
+ &default_showsign);
+ vsdata->SetColorbarNumberFormat(precision, format, showsign);
+ SendExposeEvent();
+ }
+ else
+ {
+ vsdata->ToggleDrawColorbar();
+ SendExposeEvent();
+ }
}
void KeyCPressed()
@@ -588,6 +619,24 @@ void Key_Mod_a_Pressed(GLenum state)
cout << autoscale_modes[autoscale] << endl;
SendExposeEvent();
}
+ else if (state & KMOD_ALT)
+ {
+ cout << "Setting axes number formatting..." << endl;
+ int default_precision = 4;
+ char default_format = 'd';
+ bool default_showsign = false;
+
+ int precision = prompt("Enter precision (4): ",
+ &default_precision,
+ [](int p){ return p>=0; });
+ char format = prompt("Enter format [(d)efault, (f)ixed, (s)cientific] (d): ",
+ &default_format,
+ [](char c){ return c=='d' || c=='f' || c=='s'; });
+ bool showsign = prompt("Show sign? [(1)true, (0)false] (0): ",
+ &default_showsign);
+ vsdata->SetAxisNumberFormat(precision, format, showsign);
+ SendExposeEvent();
+ }
else
{
vsdata->ToggleDrawAxes();
@@ -1426,6 +1475,20 @@ void VisualizationSceneScalarData::SetAxisLabels(const char * a_x,
PrepareAxes();
}
+void VisualizationSceneScalarData::SetAxisNumberFormat(int precision,
+ char format,
+ bool showsign)
+{
+ axis_formatter = NumberFormatter(precision, format, showsign);
+ PrepareAxes();
+}
+
+void VisualizationSceneScalarData::SetAxisNumberFormat(string formatting)
+{
+ axis_formatter = NumberFormatter(formatting);
+ PrepareAxes();
+}
+
void VisualizationSceneScalarData::PrepareAxes()
{
// std::array blk = GetLineColor();
@@ -1514,13 +1577,17 @@ void VisualizationSceneScalarData::PrepareAxes()
int ox = -desc/2;
int oy = -3*desc/2;
ostringstream buf;
- buf << setprecision(4)
- << "(" << bb.x[0] << "," << bb.y[0] << "," << bb.z[0] << ")" ;
+ buf << "("
+ << axis_formatter(bb.x[0]) << ","
+ << axis_formatter(bb.y[0]) << ","
+ << axis_formatter(bb.z[0]) << ")";
axes_buf.addText(bb.x[0], bb.y[0], bb.z[0], ox, oy, buf.str());
ostringstream buf1;
- buf1 << setprecision(4)
- << "(" << bb.x[1] << "," << bb.y[1] << "," << bb.z[1] << ")" ;
+ buf1 << "("
+ << axis_formatter(bb.x[1]) << ","
+ << axis_formatter(bb.y[1]) << ","
+ << axis_formatter(bb.z[1]) << ")";
axes_buf.addText(bb.x[1], bb.y[1], bb.z[1], ox, oy, buf1.str());
}
updated_bufs.emplace_back(&axes_buf);
diff --git a/lib/vsdata.hpp b/lib/vsdata.hpp
index 92683110..126101a4 100644
--- a/lib/vsdata.hpp
+++ b/lib/vsdata.hpp
@@ -16,6 +16,7 @@
#include "mfem.hpp"
#include "openglvis.hpp"
+#include "aux_vis.hpp"
using namespace mfem;
@@ -79,6 +80,10 @@ class VisualizationSceneScalarData : public VisualizationScene
Shading shading;
int auto_ref_max, auto_ref_max_surf_elem;
+ // Formatter for axes & colorbar numbers. Set defaults.
+ function axis_formatter = NumberFormatter(4, 'd', false);
+ function colorbar_formatter = NumberFormatter(4, 'd', false);
+
vector updated_bufs;
gl3::GlDrawable axes_buf;
gl3::GlDrawable coord_cross_buf;
@@ -269,12 +274,18 @@ class VisualizationSceneScalarData : public VisualizationScene
// Turn on or off the caption
void PrepareCaption();
+ void SetColorbarNumberFormat(int precision, char format, bool showsign);
+ void SetColorbarNumberFormat(string formatting);
+
void PrepareColorBar(double minval, double maxval,
Array * level = NULL,
Array * levels = NULL);
void SetAxisLabels(const char * a_x, const char * a_y, const char * a_z);
+ void SetAxisNumberFormat(int precision, char format, bool showsign);
+ void SetAxisNumberFormat(string formatting);
+
void PrepareAxes();
void ToggleDrawAxes()
{
diff --git a/lib/vssolution.cpp b/lib/vssolution.cpp
index bab6d0d5..2270fc68 100644
--- a/lib/vssolution.cpp
+++ b/lib/vssolution.cpp
@@ -85,6 +85,8 @@ std::string VisualizationSceneSolution::GetHelpString() const
<< "| y/Y Rotate the cutting plane |" << endl
<< "| z/Z Move the cutting plane |" << endl
<< "| \\ - Set light source position |" << endl
+ << "| Alt+a - Axes number format |" << endl
+ << "| Alt+c - Colorbar number format |" << endl
<< "| Ctrl+o - Element ordering curve |" << endl
<< "| Ctrl+p - Print to a PDF file |" << endl
<< "+------------------------------------+" << endl
diff --git a/lib/vssolution3d.cpp b/lib/vssolution3d.cpp
index bc135ab2..4d253b2b 100644
--- a/lib/vssolution3d.cpp
+++ b/lib/vssolution3d.cpp
@@ -77,6 +77,8 @@ std::string VisualizationSceneSolution3d::GetHelpString() const
<< "| y/Y Rotate cutting plane (theta) |" << endl
<< "| z/Z Translate cutting plane |" << endl
<< "| \\ - Set light source position |" << endl
+ << "| Alt+a - Axes number format |" << endl
+ << "| Alt+c - Colorbar number format |" << endl
<< "| Ctrl+o - Element ordering curve |" << endl
<< "| Ctrl+p - Print to a PDF file |" << endl
<< "+------------------------------------+" << endl
diff --git a/lib/vsvector.cpp b/lib/vsvector.cpp
index 96e3b317..479f70e8 100644
--- a/lib/vsvector.cpp
+++ b/lib/vsvector.cpp
@@ -62,6 +62,8 @@ std::string VisualizationSceneVector::GetHelpString() const
<< "| v - Cycle through vector fields |" << endl
<< "| V - Change the arrows scaling |" << endl
<< "| \\ - Set light source position |" << endl
+ << "| Alt+a - Axes number format |" << endl
+ << "| Alt+c - Colorbar number format |" << endl
<< "| Ctrl+p - Print to a PDF file |" << endl
<< "+------------------------------------+" << endl
<< "| Function keys |" << endl
diff --git a/lib/vsvector3d.cpp b/lib/vsvector3d.cpp
index 244ed611..ac239344 100644
--- a/lib/vsvector3d.cpp
+++ b/lib/vsvector3d.cpp
@@ -74,6 +74,8 @@ std::string VisualizationSceneVector3d::GetHelpString() const
<< "| y/Y Rotate cutting plane (theta) |" << endl
<< "| z/Z Translate cutting plane |" << endl
<< "| \\ - Set light source position |" << endl
+ << "| Alt+a - Axes number format |" << endl
+ << "| Alt+c - Colorbar number format |" << endl
<< "| Ctrl+p - Print to a PDF file |" << endl
<< "+------------------------------------+" << endl
<< "| Function keys |" << endl
diff --git a/tests/data b/tests/data
index 81de802a..7457c82e 160000
--- a/tests/data
+++ b/tests/data
@@ -1 +1 @@
-Subproject commit 81de802ad63ba41cd1fcd3825fbb3d2471538ace
+Subproject commit 7457c82e272a521c7877ad54b1ac4e0533945377
diff --git a/tests/glvis_driver.py b/tests/glvis_driver.py
index 34556929..af3fb2d2 100644
--- a/tests/glvis_driver.py
+++ b/tests/glvis_driver.py
@@ -72,31 +72,31 @@ def compare_images(baseline_file, output_file, expect_fail=False):
print("[FAIL] Differences were not detected in the control case.")
else:
print("[PASS] Images match.")
- print(" actual ssim = {}, cutoff = {}".format(ssim, cutoff_ssim))
+ print(f" actual ssim = {ssim}, cutoff = {cutoff_ssim}")
return ssim >= cutoff_ssim if not expect_fail else ssim < cutoff_ssim
# Function to test a given glvis command with a variety of key-based commands.
# Not currently in use.
def test_case(exec_path, exec_args, baseline, t_group, t_name, cmd):
- print("Testing {0}:{1}...".format(t_group, t_name))
+ print(f"Testing {t_group0}:{t_name1}..."))
full_screenshot_cmd = cmd + screenshot_keys
- cmd = "{0} {1} -k \"{2}\"".format(exec_path, exec_args, full_screenshot_cmd)
- print("Exec: {}".format(cmd))
+ cmd = f"{exec_path0} {exec_args} -k \"{full_screenshot_cmd2}\""
+ print(f"Exec: {cmd}")
ret = os.system(cmd + " > /dev/null 2>&1")
if ret != 0:
- print("[FAIL] GLVis exited with error code {}.".format(ret))
+ print(f"[FAIL] GLVis exited with error code {ret}.")
return False
if not os.path.exists(t_group):
os.mkdir(t_group)
- output_name = "{0}/{1}.png".format(t_group, t_name)
+ output_name = f"{t_group0}/{t_name1}.png"
- ret = os.system("mv {0} {1}".format(screenshot_file, output_name))
+ ret = os.system(f"mv {screenshot_file0} {output_name}")
if ret != 0:
- print("[FAIL] Could not move output image: exit code {}.".format(ret))
+ print(f"[FAIL] Could not move output image: exit code {ret}.")
return False
if baseline:
- baseline_name = "{0}/test.{1}.png".format(baseline, test_name)
+ baseline_name = f"{baseline0}/test.{test_name1}.png"
return compare_images(baseline_name, output_name)
else:
print("[IGNORE] No baseline exists to compare against.")
@@ -106,35 +106,35 @@ def test_stream(exec_path, exec_args, save_file, baseline):
if exec_args is None:
exec_args = ""
test_name = os.path.basename(save_file)
- print("Testing {}...".format(save_file))
+ print(f"Testing {save_file}...")
# Create new stream file with command to screenshot and close
stream_data = None
with open(save_file) as in_f:
stream_data = in_f.read()
- output_name = "test.{}.png".format(test_name)
- output_name_fail = "test.fail.{}.png".format(test_name)
+ output_name = f"test.{test_name}.png"
+ output_name_fail = f"test.fail.{test_name}.png"
tmp_file = "test.saved"
with open(tmp_file, 'w') as out_f:
out_f.write(stream_data)
out_f.write("\nwindow_size 800 600")
- out_f.write("\nscreenshot {}".format(output_name))
+ out_f.write(f"\nscreenshot {output_name}")
# Zooming in should create some difference in the images
out_f.write("\nkeys *")
- out_f.write("\nscreenshot {}".format(output_name_fail))
+ out_f.write(f"\nscreenshot {output_name_fail}")
out_f.write("\nkeys q")
# Run GLVis with modified stream file
- cmd = "{0} {1} -saved {2}".format(exec_path, exec_args, tmp_file)
- print("Exec: {}".format(cmd))
+ cmd = f"{exec_path} {exec_args} -saved {tmp_file2}"
+ print(f"Exec: {cmd}")
ret = os.system(cmd)
if ret != 0:
- print("[FAIL] GLVis exited with error code {}.".format(ret))
+ print(f"[FAIL] GLVis exited with error code {ret}.")
return False
if baseline:
- baseline_name = "{0}/test.{1}.png".format(baseline, test_name)
+ baseline_name = f"{baseline0}/test.{test_name1}.png")
test_baseline = compare_images(baseline_name, output_name)
test_control = compare_images(baseline_name, output_name_fail,
expect_fail=True)
diff --git a/tests/requirements.txt b/tests/requirements.txt
new file mode 100644
index 00000000..48389795
--- /dev/null
+++ b/tests/requirements.txt
@@ -0,0 +1,3 @@
+scikit-image
+plotly
+numpy >= 1.20.0, < 2.0.0