# HG changeset patch # User Markus Mützel # Date 1675192003 -3600 # Node ID 82128f65258524d6050a6b42d3543a81f647b1db # Parent 8b869c5d6ce8d1b8a802eaef5be4fa14f2ae23b0 print: Add option to merge only subsequent triangles with SVG toolchain (bug #63646). Trying to merge all triangles that are sharing an edge might take a long time. But that might be necessary to avoid hairlines in figures containing patch or surface graphics objects. Allow selecting if no, all, or only subsequent triangles sharing an edge should be merged into a polygon. * src/octave-svgconvert.cc (main): Distinguish between merging no (0), only consecutive (1), or all (2) triangles into polygons. (octave_polygon::reconstruct): Skip numerically expensive part of trying to merge all polygons that are sharing an edge unless it was selected. * scripts/plot/util/print.m: Document new options "-polymerge", "-nopolymerge", and "-polymerge-all" for polygon merging with the SVG toolchain. (svgconvert): Call "octave-svgconvert" with the selected polygon merge mode. * scripts/plot/private/__print_parge_opts__.m: Select default value and parse input for new options. diff -r 8b869c5d6ce8 -r 82128f652585 scripts/plot/util/print.m --- a/scripts/plot/util/print.m Mon Jan 30 18:54:27 2023 +0100 +++ b/scripts/plot/util/print.m Tue Jan 31 20:06:43 2023 +0100 @@ -157,6 +157,20 @@ ## Caution: If Octave was built against Qt version earlier than 5.13, ## @option{-svgconvert} may lead to inaccurate rendering of image objects. ## +## @item -polymerge +## @itemx -nopolymerge +## @itemx -polymerge-all +## When using the SVG based backend @option{-svgconvert}, faces are rendered +## as triangles. In some cases, some viewers might display fine lines where +## those triangles share an edge. These options control whether all triangles +## that share edges are merged into polygons (@option{-polymerge-all} which +## might take some time for graphics consisting of many triangles -- including +## line markers), only consecutive polygons are merged (@option{-polymerge}), +## or no triangles are merged at all (@option{-no-polymerge}). By default, +## only consecutive triangles sharing an edge are merged, unless the printed +## figure contains patch or surface graphics objects in which case all +## triangles that are sharing an edge are merged. +## ## @item -portrait ## @itemx -landscape ## Specify the orientation of the plot for printed output. @@ -1160,7 +1174,8 @@ cmd = sprintf ('%s - %%s %3.2f "%s" %d "%%s"', ... undo_string_escapes (opts.svgconvert_binary), ... get (0, "screenpixelsperinch"), ... - undo_string_escapes (fullfile (fontdir, "FreeSans.otf")), 1); + undo_string_escapes (fullfile (fontdir, "FreeSans.otf")), + opts.polymerge); if (opts.debug) fprintf ("svgconvert command: '%s'\n", cmd); diff -r 8b869c5d6ce8 -r 82128f652585 scripts/plot/util/private/__print_parse_opts__.m --- a/scripts/plot/util/private/__print_parse_opts__.m Mon Jan 30 18:54:27 2023 +0100 +++ b/scripts/plot/util/private/__print_parse_opts__.m Tue Jan 31 20:06:43 2023 +0100 @@ -59,6 +59,7 @@ arg_st.ghostscript.antialiasing_textalphabits = 4; arg_st.ghostscript.antialiasing_graphicsalphabits = 1; arg_st.lpr_binary = __quote_path__ (__find_binary__ ("lpr")); + arg_st.polymerge = 1; arg_st.name = ""; arg_st.orientation = ""; arg_st.pstoedit_binary = __quote_path__ (__find_binary__ ("pstoedit")); @@ -88,6 +89,11 @@ varargin(1) = []; endif + if (! isempty (findall (arg_st.figure, "type", "patch", ... + "-or", "type", "surface"))) + arg_st.polymerge = 2; + endif + for i = 1:numel (varargin) if (! ischar (varargin{i}) && ! iscellstr (varargin{i})) error ("print: input arguments must be a graphics handle or strings."); @@ -127,6 +133,12 @@ arg_st.svgconvert = true; elseif (strcmp (arg, "-nosvgconvert")) arg_st.svgconvert = false; + elseif (strcmp (arg, "-polymerge")) + arg_st.polymerge = 1; + elseif (strcmp (arg, "-nopolymerge")) + arg_st.polymerge = 0; + elseif (strcmp (arg, "-polymerge-all")) + arg_st.polymerge = 2; elseif (strcmp (arg, "-textspecial")) arg_st.special_flag = "textspecial"; elseif (strcmp (arg, "-fillpage")) diff -r 8b869c5d6ce8 -r 82128f652585 src/octave-svgconvert.cc --- a/src/octave-svgconvert.cc Mon Jan 30 18:54:27 2023 +0100 +++ b/src/octave-svgconvert.cc Tue Jan 31 20:06:43 2023 +0100 @@ -165,10 +165,12 @@ void reset (void) { m_polygons.clear (); } - QList reconstruct (void) + QList reconstruct (int reconstruct_level) { if (m_polygons.isEmpty ()) return QList (); + else if (reconstruct_level < 2) + return m_polygons; // Once a polygon has been merged to another, it is marked unsuded QVector unused; @@ -753,7 +755,7 @@ parent_elt.removeChild (orig.at (ii)); } -void reconstruct_polygons (QDomElement& parent_elt) +void reconstruct_polygons (QDomElement& parent_elt, int reconstruct_level) { QDomNodeList nodes = parent_elt.childNodes (); QColor current_color; @@ -791,7 +793,8 @@ if (color != current_color) { // Reconstruct the previous series of triangles - QList polygons = current_polygon.reconstruct (); + QList polygons + = current_polygon.reconstruct (reconstruct_level); collection.push_back (QPair,QList > (replaced_nodes, polygons)); @@ -810,19 +813,20 @@ { if (current_polygon.count ()) { - QList polygons = current_polygon.reconstruct (); + QList polygons = current_polygon.reconstruct (reconstruct_level); collection.push_back (QPair,QList > (replaced_nodes, polygons)); replaced_nodes.clear (); current_polygon.reset (); } - reconstruct_polygons (elt); + reconstruct_polygons (elt, reconstruct_level); } } // Finish collection.push_back (QPair,QList > - (replaced_nodes, current_polygon.reconstruct ())); + (replaced_nodes, + current_polygon.reconstruct (reconstruct_level))); for (int ii = 0; ii < collection.count (); ii++) replace_polygons (parent_elt, collection[ii].first, collection[ii].second); @@ -880,7 +884,10 @@ * fmt: format of the output file. May be one of pdf or svg\n\ * dpi: device dependent resolution in screen pixel per inch\n\ * font: specify a file name for the default FreeSans font\n\ -* reconstruct: specify whether to reconstruct triangle to polygons (0 or 1)\n\ +* reconstruct: specify whether to reconstruct triangle to polygons\n\ + 0: no reconstruction (merging) of polygons\n\ + 1: merge consecutive triangles if they share an edge\n\ + 2: merge all triangles that share edges (might take a long time)\n\ * outfile: output file name\n"; if (strcmp (argv[1], "-h") == 0) @@ -981,8 +988,9 @@ } // Do basic polygons reconstruction - if (QString (argv[5]).toInt ()) - reconstruct_polygons (root); + int reconstruct_level = QString (argv[5]).toInt (); + if (reconstruct_level) + reconstruct_polygons (root, reconstruct_level); // Add custom properties to SVG add_custom_properties (root);