diff --git a/CHANGELOG b/CHANGELOG
index 46abcd48..492b96e1 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -9,6 +9,15 @@
https://glvis.org
+Version 4.5.1 (development)
+===========================
+- Added an optional point line overlay (useful e.g. for reference curves), which
+ can be toggled with 'Ctrl+l'. Points can be loaded from a file with the '-pts'
+ option, sent via socket with the 'pointline' command, or specified in a GLVis
+ script. The overlay appears as a red line connecting the given points. The
+ points line format is: number of points, followed by x y z coordinates.
+
+
Version 4.5 released on Feb 6, 2026
===================================
diff --git a/README.md b/README.md
index 2a78818e..e74b1a53 100644
--- a/README.md
+++ b/README.md
@@ -146,6 +146,7 @@ Key commands
GLVis will use `SDL` to take screenshots in `bmp` format, which it will then
convert to `png` if ImageMagick's `convert` tool is available.
- G – 3D scene export to [glTF format](https://www.khronos.org/gltf)
+- Ctrl + l – Toggle point line (see `-pts` option)
- Ctrl + p – Print to a PDF file using `gl2ps`. Other
vector formats (SVG, EPS) are also possible, but keep in mind that the
printing takes a while and the generated files are big.
diff --git a/glvis.cpp b/glvis.cpp
index c68418b2..2087ba82 100644
--- a/glvis.cpp
+++ b/glvis.cpp
@@ -12,6 +12,7 @@
// GLVis - an OpenGL visualization server based on the MFEM library
#include
+#include
#include
#include
#include
@@ -157,7 +158,9 @@ class Session
};
void GLVisServer(int portnum, bool save_stream, bool fix_elem_orient,
- bool save_coloring, string plot_caption, bool headless = false)
+ bool save_coloring, string plot_caption,
+ std::vector> point_coords,
+ bool headless = false)
{
std::vector current_sessions;
string data_type;
@@ -308,7 +311,7 @@ void GLVisServer(int portnum, bool save_stream, bool fix_elem_orient,
}
Session new_session(fix_elem_orient, save_coloring, plot_caption, headless);
-
+ if (!point_coords.empty()) { new_session.GetState().point_coords = point_coords; }
constexpr int tmp_filename_size = 50;
char tmp_file[tmp_filename_size];
if (save_stream)
@@ -383,6 +386,7 @@ int main (int argc, char *argv[])
const char *palette_name = string_none;
const char *window_title = string_default;
const char *font_name = string_default;
+ const char *points_file = string_none;
int portnum = 19916;
bool persistent = true;
int multisample = GetMultisample();
@@ -493,6 +497,8 @@ int main (int argc, char *argv[])
args.AddOption(&enable_hidpi, "-hidpi", "--high-dpi",
"-nohidpi", "--no-high-dpi",
"Enable/disable support for HiDPI at runtime, if supported.");
+ args.AddOption(&points_file, "-pts", "--points-file",
+ "Points file: number of points, followed by x y z coordinates.");
cout << endl
<< " _/_/_/ _/ _/ _/ _/" << endl
@@ -612,6 +618,21 @@ int main (int argc, char *argv[])
GLVisGeometryRefiner.SetType(geom_ref_type);
+ // Load points file if specified (Ctrl+l to toggle)
+ if (points_file != string_none)
+ {
+ ifstream ifs(points_file);
+ if (!ifs)
+ {
+ cout << "Cannot open points file: " << points_file << endl;
+ }
+ else
+ {
+ int num_points = ReadPointLine(ifs, win.data_state.point_coords, cerr);
+ cout << "Loaded " << num_points << " points from " << points_file << endl;
+ }
+ }
+
string data_type;
// check for saved stream file
@@ -693,7 +714,9 @@ int main (int argc, char *argv[])
std::thread serverThread{GLVisServer, portnum, save_stream,
win.data_state.fix_elem_orient,
win.data_state.save_coloring,
- win.plot_caption, win.headless};
+ win.plot_caption,
+ std::move(win.data_state.point_coords),
+ win.headless};
// Start message loop in main thread
MainThreadLoop(win.headless, persistent);
diff --git a/lib/data_state.cpp b/lib/data_state.cpp
index 31c222f0..52b62bbe 100644
--- a/lib/data_state.cpp
+++ b/lib/data_state.cpp
@@ -10,6 +10,9 @@
// CONTRIBUTING.md for details.
#include
+#include
+#include
+#include
#include "data_state.hpp"
#include "visual.hpp"
@@ -36,6 +39,51 @@ class VectorExtrudeCoefficient : public VectorCoefficient
QuadratureFunction* Extrude1DQuadFunction(Mesh *mesh, Mesh *mesh2d,
QuadratureFunction *qf, int ny);
+
+int ReadPointLine(std::istream &in,
+ std::vector> &points,
+ std::ostream &warn)
+{
+ points.clear();
+
+ int num_points;
+ in >> num_points;
+
+ if (num_points > 0)
+ {
+ points.reserve(num_points);
+ }
+
+ for (int j = 0; num_points < 0 || j < num_points; j++)
+ {
+ double x, y, z;
+ if (in >> x >> y >> z)
+ {
+ points.push_back({x, y, z});
+ }
+ else
+ {
+ warn << "Warning: failed reading point " << j << std::endl;
+ break;
+ }
+ }
+
+ const int read_points = (int) points.size();
+ if (read_points < 2)
+ {
+ warn << "Warning: ReadPointLine needs at least 2 points (got "
+ << read_points << ")" << std::endl;
+ }
+ else if (read_points < num_points)
+ {
+ warn << "Warning: ReadPointLine expected " << num_points
+ << " (got " << read_points << ")" << std::endl;
+ }
+
+ return read_points;
+}
+
+
DataState &DataState::operator=(DataState &&ss)
{
internal = std::move(ss.internal);
@@ -49,6 +97,7 @@ DataState &DataState::operator=(DataState &&ss)
save_coloring = ss.save_coloring;
keep_attr = ss.keep_attr;
cmplx_phase = ss.cmplx_phase;
+ point_coords = std::move(ss.point_coords);
return *this;
}
diff --git a/lib/data_state.hpp b/lib/data_state.hpp
index 18182fc6..550736af 100644
--- a/lib/data_state.hpp
+++ b/lib/data_state.hpp
@@ -15,6 +15,7 @@
#include