66#include < filesystem>
77#include < fstream>
88#include < iomanip>
9+ #include < cmath>
910#include < sstream>
1011#include < string>
1112#include < utility>
@@ -72,6 +73,46 @@ bool is_ieee_preset(const Preset preset) {
7273 return preset == Preset::IEEE_SingleColumn || preset == Preset::IEEE_DoubleColumn;
7374}
7475
76+ bool is_hex_digit (const char c) {
77+ return (c >= ' 0' && c <= ' 9' ) || (c >= ' a' && c <= ' f' ) || (c >= ' A' && c <= ' F' );
78+ }
79+
80+ bool is_hex_rgb (const std::string& color) {
81+ if (color.size () != 7 || color.front () != ' #' ) {
82+ return false ;
83+ }
84+ for (std::size_t i = 1 ; i < color.size (); ++i) {
85+ if (!is_hex_digit (color[i])) {
86+ return false ;
87+ }
88+ }
89+ return true ;
90+ }
91+
92+ std::string clamp_opacity_color (const std::string& color, const double opacity) {
93+ const double clamped = std::clamp (opacity, 0.0 , 1.0 );
94+ if (!is_hex_rgb (color)) {
95+ return color;
96+ }
97+ const int alpha =
98+ static_cast <int >(std::lround (clamped * 255.0 )); // gnuplot expects AARRGGBB
99+ std::ostringstream os;
100+ os << " #" << std::hex << std::setfill (' 0' ) << std::nouppercase << std::setw (2 ) << alpha
101+ << color.substr (1 );
102+ return os.str ();
103+ }
104+
105+ bool has_custom_color_or_opacity (const Figure& fig) {
106+ for (const auto & axis : fig.all_axes ()) {
107+ for (const auto & series : axis.series ()) {
108+ if (series.spec .has_color || series.spec .has_opacity ) {
109+ return true ;
110+ }
111+ }
112+ }
113+ return false ;
114+ }
115+
75116std::string terminal_for (OutputFormat format, const FigureSpec& spec) {
76117 std::ostringstream os;
77118 os << std::fixed << std::setprecision (3 );
@@ -109,32 +150,50 @@ std::string with_clause(const SeriesData& series,
109150 series.spec .has_line_width ? series.spec .line_width_pt : style.line_width_pt ;
110151 const bool ieee = is_ieee_preset (preset);
111152 const int dt = static_cast <int >(series_idx % 7U ) + 1 ;
153+ std::string color;
154+ if (series.spec .has_color ) {
155+ color = series.spec .color ;
156+ } else if (ieee || series.spec .has_opacity ) {
157+ color = " #000000" ;
158+ }
159+ if (series.spec .has_opacity ) {
160+ color = clamp_opacity_color (color, series.spec .opacity );
161+ }
112162
113163 std::ostringstream os;
114164 os << std::fixed << std::setprecision (3 );
115165 switch (series.spec .type ) {
116166 case SeriesType::Line:
117167 os << " with lines lw " << std::max (0.5 , line_width);
168+ if (!color.empty ()) {
169+ os << " lc rgb '" << color << " '" ;
170+ }
118171 if (ieee) {
119- os << " lc rgb '#000000' dt " << dt;
172+ os << " dt " << dt;
120173 }
121174 break ;
122175 case SeriesType::Scatter:
123176 os << " with points pt 7 ps " << std::max (0.5 , style.point_size );
124- if (ieee ) {
125- os << " lc rgb '#000000 '" ;
177+ if (!color. empty () ) {
178+ os << " lc rgb '" << color << " '" ;
126179 }
127180 break ;
128181 case SeriesType::ErrorBars:
129182 os << " with yerrorbars lw " << std::max (0.5 , line_width);
183+ if (!color.empty ()) {
184+ os << " lc rgb '" << color << " '" ;
185+ }
130186 if (ieee) {
131- os << " lc rgb '#000000' dt " << dt;
187+ os << " dt " << dt;
132188 }
133189 break ;
134190 case SeriesType::Band:
135191 os << " with lines lw " << std::max (0.5 , line_width);
192+ if (!color.empty ()) {
193+ os << " lc rgb '" << color << " '" ;
194+ }
136195 if (ieee) {
137- os << " lc rgb '#000000' dt " << dt;
196+ os << " dt " << dt;
138197 }
139198 break ;
140199 }
@@ -147,6 +206,7 @@ void emit_plot_body(std::ostream& os,
147206 const auto & spec = fig.spec ();
148207 const auto & all_axes = fig.all_axes ();
149208 const bool ieee = is_ieee_preset (spec.preset );
209+ const bool custom_color_or_opacity = has_custom_color_or_opacity (fig);
150210 const double tick_font_pt = ieee ? 8.0 : spec.style .font_pt ;
151211 const double label_font_pt = ieee ? 8.5 : spec.style .font_pt ;
152212 const double title_font_pt = ieee ? 8.5 : spec.style .font_pt ;
@@ -161,7 +221,7 @@ void emit_plot_body(std::ostream& os,
161221 os << " set ytics font '" << esc (spec.style .font ) << " ," << tick_font_pt << " '\n " ;
162222 os << " set format x '%.2g'\n " ;
163223 os << " set format y '%.2g'\n " ;
164- if (ieee) {
224+ if (ieee && !custom_color_or_opacity ) {
165225 os << " set monochrome\n " ;
166226 }
167227 os << " set key noopaque\n " ;
0 commit comments