@@ -375,6 +375,25 @@ void emit_typed_objects(std::ostream& os, const AxesSpec& axis_spec) {
375375 }
376376}
377377
378+ void emit_auto_layout (std::ostream& os, const FigureSpec& spec, const AxesSpec& axis_spec) {
379+ if (!spec.auto_layout ) {
380+ return ;
381+ }
382+ // Heuristic margins in character units for single-panel readability.
383+ const std::size_t yl = axis_spec.ylabel .size ();
384+ const std::size_t y2l = axis_spec.y2label .size ();
385+ const std::size_t xl = axis_spec.xlabel .size ();
386+ const std::size_t tl = axis_spec.title .size ();
387+ const int lmargin = static_cast <int >(std::clamp<std::size_t >(8 + yl / 2 , 8 , 16 ));
388+ const int rmargin = static_cast <int >(std::clamp<std::size_t >(4 + y2l / 2 , 4 , 14 ));
389+ const int bmargin = static_cast <int >(std::clamp<std::size_t >(4 + xl / 10 , 4 , 8 ));
390+ const int tmargin = static_cast <int >(std::clamp<std::size_t >(2 + tl / 18 , 2 , 6 ));
391+ os << " set lmargin " << lmargin << " \n " ;
392+ os << " set rmargin " << rmargin << " \n " ;
393+ os << " set bmargin " << bmargin << " \n " ;
394+ os << " set tmargin " << tmargin << " \n " ;
395+ }
396+
378397void emit_plot_body (std::ostream& os,
379398 const Figure& fig,
380399 const std::vector<std::vector<std::filesystem::path>>& data_files) {
@@ -422,6 +441,7 @@ void emit_plot_body(std::ostream& os,
422441 for (std::size_t axis_idx = 0 ; axis_idx < all_axes.size (); ++axis_idx) {
423442 const auto & axis = all_axes[axis_idx];
424443 const auto & axis_spec = axis.spec ();
444+ emit_auto_layout (os, spec, axis_spec);
425445
426446 os << " unset title\n " ;
427447 os << " unset xlabel\n " ;
@@ -440,7 +460,12 @@ void emit_plot_body(std::ostream& os,
440460
441461 const bool legend_enabled = axis_spec.legend && axis_spec.legend_spec .enabled ;
442462 if (legend_enabled) {
443- os << " set key " << legend_position_token (axis_spec.legend_spec .position ) << " \n " ;
463+ LegendPosition legend_pos = axis_spec.legend_spec .position ;
464+ if (spec.auto_layout && axis.series ().size () >= 4 &&
465+ legend_pos == LegendPosition::TopRight) {
466+ legend_pos = LegendPosition::OutsideRight;
467+ }
468+ os << " set key " << legend_position_token (legend_pos) << " \n " ;
444469 os << " set key maxcols " << std::max (1 , axis_spec.legend_spec .columns ) << " \n " ;
445470 os << " set key " << (axis_spec.legend_spec .opaque ? " opaque" : " noopaque" ) << " \n " ;
446471 os << " set key box linewidth " << (axis_spec.legend_spec .boxed ? " 0.5" : " 0.0" ) << " \n " ;
@@ -474,6 +499,12 @@ void emit_plot_body(std::ostream& os,
474499 if (axis_spec.grid || spec.style .grid ) {
475500 os << " set grid xtics ytics linewidth 0.35 linecolor rgb '#e2e2e2'\n " ;
476501 }
502+ if (axis_spec.enable_crosshair ) {
503+ os << " set mouse\n " ;
504+ os << " set mxtics 4\n " ;
505+ os << " set mytics 4\n " ;
506+ os << " set grid mxtics mytics linewidth 0.25 linecolor rgb '#f0f0f0'\n " ;
507+ }
477508 if (axis_spec.xlog ) {
478509 os << " set logscale x\n " ;
479510 }
@@ -705,6 +736,19 @@ RenderResult GnuplotBackend::render(const Figure& fig, const std::filesystem::pa
705736 return result;
706737 }
707738
739+ if (fig.spec ().interactive_preview ) {
740+ const auto preview_path = out_dir / " tmp" / " interactive_preview.gp" ;
741+ std::ofstream preview_os (preview_path);
742+ if (preview_os.is_open ()) {
743+ preview_os << " set encoding utf8\n " ;
744+ preview_os << " set terminal qt persist\n " ;
745+ emit_plot_body (preview_os, fig, data_files);
746+ preview_os << " pause mouse close\n " ;
747+ } else {
748+ gnuplotpp::log::Warn (" failed to write interactive preview script: " , preview_path.string ());
749+ }
750+ }
751+
708752 const std::string check_cmd = " command -v " + executable_ + " >/dev/null 2>&1" ;
709753 if (std::system (check_cmd.c_str ()) != 0 ) {
710754 result.ok = false ;
0 commit comments