From 91a656c3b1d8a3cb3c97dd8e66acd4d3b755a97a Mon Sep 17 00:00:00 2001 From: godardma Date: Fri, 3 Jul 2026 11:44:52 +0200 Subject: [PATCH 1/2] [graphics] solving IPE figure not clearing ( closes #383 ) --- src/graphics/3rd/ipe/codac2_Figure2D_IPE.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/graphics/3rd/ipe/codac2_Figure2D_IPE.cpp b/src/graphics/3rd/ipe/codac2_Figure2D_IPE.cpp index 38c465bbf..140e1a4b3 100644 --- a/src/graphics/3rd/ipe/codac2_Figure2D_IPE.cpp +++ b/src/graphics/3rd/ipe/codac2_Figure2D_IPE.cpp @@ -190,6 +190,7 @@ void Figure2D_IPE::clear() // clear _color map and layers _colors.clear(); _layers.clear(); + _items.clear(); init_figure(); } From ec5e3d23bc4942c254e2d2bc1ce08aecd7a1006c Mon Sep 17 00:00:00 2001 From: godardma Date: Fri, 3 Jul 2026 14:31:01 +0200 Subject: [PATCH 2/2] [graphics] save function for Figure2D ( closes #377 ) --- doc/manual/manual/visualization/figures.rst | 3 ++- examples/00_graphics/graphic_animation.cpp | 21 ++++++++++++++++++ examples/00_graphics/graphic_animation.py | 22 ++++++++++++++++++- .../graphics/figures/codac2_py_Figure2D.cpp | 4 ++++ src/graphics/3rd/ipe/codac2_Figure2D_IPE.cpp | 3 +++ src/graphics/3rd/ipe/codac2_Figure2D_IPE.h | 7 ++++++ .../3rd/vibes/codac2_Figure2D_VIBes.cpp | 5 +++++ .../3rd/vibes/codac2_Figure2D_VIBes.h | 7 ++++++ src/graphics/figures/codac2_Figure2D.cpp | 6 +++++ src/graphics/figures/codac2_Figure2D.h | 18 +++++++++++++++ src/graphics/figures/codac2_OutputFigure2D.h | 7 ++++++ 11 files changed, 101 insertions(+), 2 deletions(-) diff --git a/doc/manual/manual/visualization/figures.rst b/doc/manual/manual/visualization/figures.rst index f146017eb..95a525a95 100644 --- a/doc/manual/manual/visualization/figures.rst +++ b/doc/manual/manual/visualization/figures.rst @@ -156,4 +156,5 @@ VIBes only Some methods are exclusive to the real-time display with VIBes : - center_viewbox : takes two Vector as arguments, the center and radius of each axis -- auto_scale : takes no argument, the figure will be automatically scaled to fit the window \ No newline at end of file +- auto_scale : takes no argument, the figure will be automatically scaled to fit the window +- save : takes the name of the file as argument and saves the current state of the VIBes window to the file. Allowed formats are png, jpg, bmp and svg. \ No newline at end of file diff --git a/examples/00_graphics/graphic_animation.cpp b/examples/00_graphics/graphic_animation.cpp index 1b567d086..6f357e68c 100644 --- a/examples/00_graphics/graphic_animation.cpp +++ b/examples/00_graphics/graphic_animation.cpp @@ -21,4 +21,25 @@ int main() theta += 2.0 * M_PI / (double) steps; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } + + Figure2D fig_save ("Animation with exports",GraphicOutput::VIBES|GraphicOutput::IPE); + fig_save.set_window_properties({600,50},{500,500}); // position, window size + fig_save.set_axes(axis(0,{-0.5,4}), axis(1,{-0.5,1.5})); + + Vector X({0,0,0}); + + ColorMap cmap = ColorMap::rainbow(); + for (double t = 0; t < 2.*PI; t+=PI/20.) + { + fig_save.draw_tank(X, 0.05, cmap.color(t/(2.*PI))); + X+=Vector({0.1*cos(X[2]),0.1*sin(X[2]),0.1*cos(t)}); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + if (Interval(PI).inflate(0.1).contains(t)) + { + fig_save.save("animation_tank.png"); + fig_save.save("animation_tank.jpg"); + fig_save.save("animation_tank.bmp"); + fig_save.save("animation_tank.svg"); + } + } } \ No newline at end of file diff --git a/examples/00_graphics/graphic_animation.py b/examples/00_graphics/graphic_animation.py index 69955e528..24dfff9e7 100644 --- a/examples/00_graphics/graphic_animation.py +++ b/examples/00_graphics/graphic_animation.py @@ -1,4 +1,5 @@ from codac import * +import math import time fig = Figure2D("Animation", GraphicOutput.VIBES | GraphicOutput.IPE) @@ -11,4 +12,23 @@ fig.clear() fig.draw_point([5*cos(theta).mid(), 5*sin(theta).mid()], Color.red()) theta += 2*PI / steps - time.sleep(0.1) \ No newline at end of file + time.sleep(0.1) + +fig_save = Figure2D("Animation with exports", GraphicOutput.VIBES | GraphicOutput.IPE) +fig_save.set_window_properties([600,50],[500,500]) # position, window size +fig_save.set_axes(axis(0,[-0.5,4]), axis(1,[-0.5,1.5])) + +X = Vector([0,0,0]) +t = 0 +cmap = ColorMap.rainbow() + +while t <2*PI: + fig_save.draw_tank(X, 0.05, cmap.color(t/(2*PI))) + X+=Vector([0.1*math.cos(X[2]),0.1*math.sin(X[2]),0.1*math.cos(t)]) + time.sleep(0.1) + if (Interval(PI).inflate(0.1).contains(t)): + fig_save.save("animation_tank.png") + fig_save.save("animation_tank.jpg") + fig_save.save("animation_tank.bmp") + fig_save.save("animation_tank.svg") + t += PI/20 \ No newline at end of file diff --git a/python/src/graphics/figures/codac2_py_Figure2D.cpp b/python/src/graphics/figures/codac2_py_Figure2D.cpp index 41b26324c..e78d90819 100644 --- a/python/src/graphics/figures/codac2_py_Figure2D.cpp +++ b/python/src/graphics/figures/codac2_py_Figure2D.cpp @@ -109,6 +109,10 @@ void export_Figure2D(py::module& m) .def("clear", &Figure2D::clear, VOID_FIGURE2D_CLEAR) + + .def("save", &Figure2D::save, + VOID_FIGURE2D_SAVE_CONST_STRING_REF, + "filename"_a) .def("scaled_unit", &Figure2D::scaled_unit, DOUBLE_FIGURE2D_SCALED_UNIT_CONST) diff --git a/src/graphics/3rd/ipe/codac2_Figure2D_IPE.cpp b/src/graphics/3rd/ipe/codac2_Figure2D_IPE.cpp index 140e1a4b3..62a6a94cd 100644 --- a/src/graphics/3rd/ipe/codac2_Figure2D_IPE.cpp +++ b/src/graphics/3rd/ipe/codac2_Figure2D_IPE.cpp @@ -195,6 +195,9 @@ void Figure2D_IPE::clear() init_figure(); } +void Figure2D_IPE::save(const std::string& filename) +{} + std::string ipe_str(const Color& c) { return c.hex_str().substr(1); diff --git a/src/graphics/3rd/ipe/codac2_Figure2D_IPE.h b/src/graphics/3rd/ipe/codac2_Figure2D_IPE.h index 23d0e15f6..ddfeba394 100644 --- a/src/graphics/3rd/ipe/codac2_Figure2D_IPE.h +++ b/src/graphics/3rd/ipe/codac2_Figure2D_IPE.h @@ -72,6 +72,13 @@ namespace codac2 */ void clear(); + /** + * \brief Saves the figure to a file + * + * \param filename Name of the file to save the figure to + */ + void save(const std::string& filename); + /** * \brief Begins a new path in the IPE file * diff --git a/src/graphics/3rd/vibes/codac2_Figure2D_VIBes.cpp b/src/graphics/3rd/vibes/codac2_Figure2D_VIBes.cpp index eb3e40059..aeb59a32b 100644 --- a/src/graphics/3rd/vibes/codac2_Figure2D_VIBes.cpp +++ b/src/graphics/3rd/vibes/codac2_Figure2D_VIBes.cpp @@ -93,6 +93,11 @@ void Figure2D_VIBes::clear() _layers.clear(); } +void Figure2D_VIBes::save(const std::string& filename) +{ + vibes::saveImage(filename, _fig.name()); +} + void Figure2D_VIBes::draw_point(const Vector& c, const StyleProperties& style) { assert(_fig.size() <= c.size()); diff --git a/src/graphics/3rd/vibes/codac2_Figure2D_VIBes.h b/src/graphics/3rd/vibes/codac2_Figure2D_VIBes.h index 04572df9c..f780f544a 100644 --- a/src/graphics/3rd/vibes/codac2_Figure2D_VIBes.h +++ b/src/graphics/3rd/vibes/codac2_Figure2D_VIBes.h @@ -65,6 +65,13 @@ namespace codac2 * \brief Clears the figure */ void clear(); + + /** + * \brief Saves the figure to a file + * + * \param filename Name of the file to save the figure to + */ + void save(const std::string& filename); // Geometric shapes diff --git a/src/graphics/figures/codac2_Figure2D.cpp b/src/graphics/figures/codac2_Figure2D.cpp index 15c9503c8..2658e715b 100644 --- a/src/graphics/figures/codac2_Figure2D.cpp +++ b/src/graphics/figures/codac2_Figure2D.cpp @@ -111,6 +111,12 @@ void Figure2D::clear() output_fig->clear(); } +void Figure2D::save(const std::string& filename) +{ + for(const auto& output_fig : _output_figures) + output_fig->save(filename); +} + double Figure2D::scaled_unit() const { return std::max(_axes[0].limits.diam(),_axes[1].limits.diam()) / _window_size.max_coeff(); diff --git a/src/graphics/figures/codac2_Figure2D.h b/src/graphics/figures/codac2_Figure2D.h index 3b920c734..ec637baa8 100644 --- a/src/graphics/figures/codac2_Figure2D.h +++ b/src/graphics/figures/codac2_Figure2D.h @@ -182,6 +182,13 @@ namespace codac2 */ void clear(); + /** + * \brief Saves the figure to a file + * + * \param filename Name of the file to save the figure to + */ + void save(const std::string& filename); + /** * \brief Getter for the scaling factor of the figure * @@ -715,6 +722,17 @@ namespace codac2 selected_fig()->clear(); } + /** + * \brief Saves the figure to a file + * + * \param filename Name of the file to save the figure to + */ + static void save(const std::string& filename) + { + auto_init(); + selected_fig()->save(filename); + } + // Geometric shapes /** diff --git a/src/graphics/figures/codac2_OutputFigure2D.h b/src/graphics/figures/codac2_OutputFigure2D.h index a83ba57e4..a4157949c 100644 --- a/src/graphics/figures/codac2_OutputFigure2D.h +++ b/src/graphics/figures/codac2_OutputFigure2D.h @@ -73,6 +73,13 @@ namespace codac2 * \brief Clears the figure */ virtual void clear() = 0; + + /** + * \brief Saves the figure to a file + * + * \param filename Name of the file to save the figure to + */ + virtual void save(const std::string& filename) = 0; protected: