// // Created by Alan Freitas on 2020-07-05. // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace matplot { void axes_type::draw() { parent_->draw(); } void axes_type::touch() { parent_->touch(); } void axes_type::emplace_object(axes_object_handle obj) { if (next_plot_replace_) { children_.clear(); colororder_index_ = 0; } children_.push_back(obj); touch(); } void axes_type::run_title_command() { include_comment("Axes title"); if (title_visible_ && !title_.empty()) { std::string cmd; cmd += "set title \""; if (title_enhanced_ && iequals(title_font_weight_, "bold")) { cmd += "{/:Bold "; } if (title_enhanced_ && iequals(title_font_weight_, "italic")) { cmd += "{/:Italic "; } cmd += escape(title()); if (title_enhanced_ && iequals(title_font_weight_, "bold")) { cmd += "}"; } if (title_enhanced_ && iequals(title_font_weight_, "italic")) { cmd += "}"; } if (parent_->backend_->supports_fonts()) { cmd += "\" font \"" + escape(font()) + "," + std::to_string(unsigned(font_size() * title_font_size_multiplier_)) + "\""; cmd += " textcolor '" + to_string(title_color_) + "'"; } if (title_enhanced_) { cmd += " enhanced"; } else { cmd += " noenhanced"; } run_command(cmd); } else { run_command("unset title"); } } void axes_type::run_colormap_command() { bool any_obj_needs_colormap = false; for (const auto &child : children_) { if (child->requires_colormap()) { any_obj_needs_colormap = true; break; } } if (any_obj_needs_colormap && !colormap_.empty() && !children_.empty()) { std::stringstream ss; // limit the number of colors in the palette // this is useful for contour plots if (max_colors_) { ss << " set palette maxcolors " << max_colors_ << "\n"; } ss << " set palette model RGB\n"; ss << " set palette defined (\\\n"; if (max_colors_ == 0) { // put all colormap colors in the palette if (colormap_.size() == 1) { colormap_.emplace_back(colormap_.front()); } for (size_t i = 0; i < colormap_.size(); ++i) { if (colormap_[i].size() == 4) { std::array c = to_array<4>(colormap_[i]); std::array c2{}; std::transform(c.begin(), c.end(), c2.begin(), [](const double &x) { return static_cast(x); }); // std::copy(c.begin(), c.end(), c2.begin()); ss << " " << i << " \"" << to_string(c2) << "\""; } else { ss << " " << i << " " << colormap_[i][0] << " " << colormap_[i][1] << " " << colormap_[i][2]; } if (i != colormap_.size() - 1) { ss << ",\\\n"; } else { ss << ")\n"; } } } else { // if we limit the number of colors, the new colors // are the max_color appropriate interpolations in // the original colormap for (size_t i = 0; i < max_colors_; ++i) { color_array c = colormap_interpolation( static_cast(i), 0., max_colors_ - 1.); ss << " " << i << " " << c[0 + 1] << " " << c[1 + 1] << " " << c[2 + 1]; if (i != max_colors_ - 1 || max_colors_ == 1) { ss << ",\\\n"; } else { ss << ")\n"; } } // we can't have just one color, at least in some versions // of gnuplot we can't if (max_colors_ == 1) { color_array c = colormap_interpolation(0, 0., max_colors_ - 1.); ss << " " << 1 << " " << c[0 + 1] << " " << c[1 + 1] << " " << c[2 + 1] << ")\n"; } } run_command(ss.str()); } } std::tuple axes_type::calculate_margins() { // There are conditions on which the xlim are smaller than requested // to make room for other elements double width_multiplier = is_3d() && !is_3d_map() ? 0.72 : 1.; double height_multiplier = is_3d() && !is_3d_map() ? 0.72 : 1.; double x = x_origin(); double y = y_origin(); // if parent has a title if (!parent_->title().empty()) { // open room for the title double room_needed = 0.2; double this_axes_contribution = room_needed * height(); height_multiplier -= this_axes_contribution; // the further from y=0, the larger the shift y -= 1.5 * y * this_axes_contribution; } double rmargin_multiplier = (1. - width_multiplier) / 2.; double lmargin_multiplier = (1. - width_multiplier) / 2.; double tmargin_multiplier = (1. - height_multiplier) / 2.; double bmargin_multiplier = (1. - height_multiplier) / 2.; const auto bg_width = width_multiplier * width(); const auto bg_height = height_multiplier * height(); // We increase the margins for colorbar double colorbar_lmargin = 0.; double colorbar_rmargin = 0.; double colorbar_bmargin = 0.; double colorbar_tmargin = 0.; constexpr double extra_for_ticks = 0.05; if (cb_axis_.visible() && !cb_inside_) { // identify if colorbar is north/south/east/west auto [cbx, cby, cbw, cbh] = cb_position_; bool automatic_position = cbw == cbh; if (automatic_position) { if (cb_vertical_) { colorbar_rmargin = 0.04; } else { colorbar_bmargin = 0.1; } } else if (cbw > cbh) { // north/south float south_margin = cby + cbh; float north_margin = 1.f - cby; if (south_margin <= north_margin) { // south colorbar_bmargin = south_margin + extra_for_ticks; } else { // north colorbar_tmargin = north_margin + extra_for_ticks; } } else { // west/east float east_margin = cbx + cbw; float west_margin = 1.f - cbx; if (east_margin <= west_margin) { // east colorbar_rmargin = east_margin + extra_for_ticks; } else { // west colorbar_lmargin = west_margin + extra_for_ticks; } } } const double lmargin = x + width() * lmargin_multiplier + colorbar_lmargin; const double rmargin = x + width() - width() * rmargin_multiplier - colorbar_rmargin; const double bmargin = y + height() * bmargin_multiplier + colorbar_bmargin; const double tmargin = y + height() - height() * tmargin_multiplier - colorbar_tmargin; return std::make_tuple(bg_width, bg_height, lmargin, rmargin, bmargin, tmargin); } void axes_type::run_position_margin_command() { include_comment("Axes position"); run_command("set origin " + std::to_string(x_origin()) + "," + std::to_string(y_origin())); auto [bg_width, bg_height, lmargin, rmargin, bmargin, tmargin] = calculate_margins(); run_command("set size " + num2str(bg_width) + "," + num2str(bg_height)); run_command("set lmargin at screen " + num2str(lmargin)); run_command("set rmargin at screen " + num2str(rmargin)); run_command("set bmargin at screen " + num2str(bmargin)); run_command("set tmargin at screen " + num2str(tmargin)); if (!axes_aspect_ratio_auto_) { if (is_3d()) { run_command("set view equal xyz"); } else { run_command("set size ratio " + std::to_string(axes_aspect_ratio_)); } } } void axes_type::run_grid_command() { // if the user didn't explicitly set the grid, turn on the grid // by default in logscale if (!x_user_grid_) { x_grid_ = x_axis().scale() == axis_type::axis_scale::log || is_3d(); x_minor_grid_ = x_axis().scale() == axis_type::axis_scale::log; } if (!y_user_grid_) { y_grid_ = y_axis().scale() == axis_type::axis_scale::log || is_3d(); y_minor_grid_ = y_axis().scale() == axis_type::axis_scale::log; } if (!z_user_grid_) { z_grid_ = z_axis().scale() == axis_type::axis_scale::log || is_3d(); z_minor_grid_ = z_axis().scale() == axis_type::axis_scale::log; } if (!r_user_grid_) { r_grid_ = r_axis().scale() == axis_type::axis_scale::log || is_polar(); r_minor_grid_ = r_axis().scale() == axis_type::axis_scale::log; } // set grid xtics ytics mxtics mytics layerdefault linecolor 'blue' // linetype 1 linewidth 2, linecolor 'red' if (x_grid_ || x_minor_grid_ || y_grid_ || y_minor_grid_ || z_grid_ || z_minor_grid_ || r_grid_ || r_minor_grid_) { include_comment("Create grid"); std::string cmd = "set grid"; if (x_grid_) { cmd += " xtics"; } if (x_minor_grid_) { cmd += " mxtics"; } if (y_grid_) { cmd += " ytics"; } if (y_minor_grid_) { cmd += " mytics"; } if (z_grid_) { cmd += " ztics"; } if (z_minor_grid_) { cmd += " mztics"; } if (r_grid_) { cmd += " polar"; } if (r_minor_grid_) { cmd += " mrtics"; } cmd += " vertical"; if (grid_front_) { cmd += " front"; } cmd += grid_line_style_.plot_string( line_spec::style_to_plot::plot_line_only, false); if (x_minor_grid_ || y_minor_grid_ || z_minor_grid_) { cmd += ","; if (grid_front_) { cmd += " front "; } cmd += minor_grid_line_style_.plot_string( line_spec::style_to_plot::plot_line_only, false); } run_command(cmd); } } void axes_type::run_box_command() { include_comment("Axes box"); if (!visible_) { run_command("unset border"); run_command("unset xtics"); run_command("unset ytics"); run_command("unset ztics"); return; } /* Border types for box 1 bottom bottom left front 2 left bottom left back 4 top bottom right front 8 right bottom right back 16 no effect left vertical 32 no effect back vertical 64 no effect right vertical 128 no effect front vertical 256 no effect top left back 512 no effect top right back 1024 no effect top left front 2048 no effect top right front */ if (!box_ || is_polar()) { if (is_3d()) { // create borders // bottom left front 1 // bottom right front 4 // left vertical 16 run_command("set border 21 linecolor \"" + to_string(x_axis().color()) + "\" linewidth " + num2str(line_width())); } else if (is_2d()) { // create left (2) and bottom (1) unsigned border = 0; if (x_axis().visible()) { border += 1; } if (y_axis().visible()) { border += 2; } run_command("set border " + num2str(border) + " linecolor \"" + to_string(x_axis().color()) + "\" linewidth " + num2str(line_width())); } else { run_command("unset border"); } } else { // if (box_) if (is_3d()) { if (box_full_) { run_command("set border 4095 linecolor \"" + to_string(box_color_) + "\" linewidth " + num2str(line_width())); } else { // create borders // bottom left front 1 // bottom left back 2 // bottom right front 4 // bottom right back 8 // left vertical 16 // back vertical 32 // right vertical 64 // top left back 256 // top right back 512 run_command("set border 895 linecolor \"" + to_string(box_color_) + "\" linewidth " + num2str(line_width())); } } else { // create all borders (bottom / left / top / right) in black run_command("set border 15 linecolor \"" + to_string(box_color_) + "\" linewidth " + num2str(line_width())); } } } void axes_type::run_axes_command() { // plot tics // http://www.gnuplot.info/docs_4.2/node182.html if (is_polar()) { run_command("set polar"); } auto set_or_unset_axis = [this](class axis_type &ax, std::string axis_name, bool minor_ticks = false) { // cb is the only axis we don't unset if tics are empty if (ax.visible() && !ax.tick_values().empty()) { if (ax.geographic()) { run_command("set " + axis_name + "tics geographic"); } if (ax.on_axis()) { run_command("set " + axis_name + "tics axis"); } run_command("set format " + axis_name + " \"" + escape(ax.tick_label_format()) + "\""); std::string tics_str = "set " + axis_name + "tics textcolor \"" + to_string(ax.color()); if (parent_->backend_->supports_fonts()) { tics_str += "\" font \"" + font() + "," + num2str(unsigned(font_size())) + "\""; } tics_str += " nomirror in scale " + num2str(ax.tick_length()) + " "; run_command(tics_str); if (ax.tick_values_manual() || ax.tick_values().empty()) { run_command("set " + axis_name + "tics" + ax.tick_values_string(minor_ticks) + ax.tick_rotate_string()); } else { if (ax.tickangle() != 0.) { run_command("set " + axis_name + "tics" + ax.tick_rotate_string()); } if (minor_ticks) { run_command("set m" + axis_name + "tics 2"); } } } else { if (axis_name == "r") { // Hiding the r-axis works differently. // We can't completely unset the tics or the grid does not // appear properly run_command("unset " + axis_name + "axis"); run_command("set " + axis_name + "tics scale 0"); } else if (axis_name == "cb") { // the colorbar / colorbox has a special command to unset it // if only ticks are empty we still want to show the box if (!ax.visible()) { run_command("unset colorbox"); } if (ax.tick_values().empty()) { run_command("unset cbtics"); } } else { run_command("unset " + axis_name + "tics"); } } const bool zero_axis_is_possible = axis_name != "r" && axis_name != "t" && axis_name != "cb"; if (zero_axis_is_possible) { if (ax.zero_axis()) { run_command("set " + axis_name + "zeroaxis linestyle -1"); } else { run_command("unset " + axis_name + "zeroaxis"); } } }; if (!visible_) { run_command("unset xtics"); run_command("unset ytics"); run_command("unset x2tics"); run_command("unset y2tics"); if (is_3d()) { run_command("unset ztics"); } if (is_polar()) { run_command("unset raxis"); run_command("set rtics scale 0"); run_command("unset ttics"); } if (!cb_axis_.visible()) { run_command("unset colorbox"); } } else { set_or_unset_axis(x_axis_, "x", x_minor_grid_); set_or_unset_axis(x2_axis_, "x2", x_minor_grid_); set_or_unset_axis(y_axis_, "y", y_minor_grid_); set_or_unset_axis(y2_axis_, "y2", y_minor_grid_); const bool is_map = azimuth_ == 0 && elevation_ == 90; if (is_3d() && !is_map) { set_or_unset_axis(z_axis_, "z", z_minor_grid_); } if (is_polar()) { set_or_unset_axis(r_axis_, "r", r_minor_grid_); set_or_unset_axis(t_axis_, "t", t_minor_grid_); } if (cb_axis_.visible()) { std::stringstream ss; ss << "set colorbox"; if (!cb_axis_.reverse()) { ss << " noinvert"; } else { ss << " invert"; } if (cb_vertical_) { ss << " vertical"; } else { ss << " horizontal"; } if (cb_position_[2] == 0. && cb_position_[3] == 0.) { ss << " default"; } else { auto [x, y, w, h] = position_; ss << " user"; ss << " origin " << x + cb_position_[0] * w << "," << y + cb_position_[1] * h; ss << " size " << cb_position_[2] * w << "," << cb_position_[3] * h; } run_command(ss.str()); } set_or_unset_axis(cb_axis_, "cb", false); /* * if (color_box_) { run_command("set colorbox"); if (color_box_log_scale_) { run_command("set logscale cb"); } } else { run_command("unset colorbox"); if (color_box_log_scale_) { run_command("unset logscale cb"); } } */ } include_comment("Axes ranges"); run_command("set xrange " + x_axis().range_string()); run_command("set yrange " + y_axis().range_string()); run_command("set y2range " + y2_axis().range_string()); if (is_3d()) { run_command("set zrange " + z_axis().range_string()); } if (is_polar() && r_axis().limits_mode_manual()) { run_command("set rrange " + r_axis().range_string()); } if (cb_axis_.visible()) { run_command("set cbrange " + cb_axis_.range_string()); } include_comment("Axes scale"); if (x_axis().scale() == axis_type::axis_scale::log) { run_command("set logscale x"); } if (y_axis().scale() == axis_type::axis_scale::log) { run_command("set logscale y"); } if (is_3d()) { if (z_axis().scale() == axis_type::axis_scale::log) { run_command("set logscale z"); } } if (is_3d()) { include_comment("Axes view"); run_command("set xyplane 0"); // gnuplot azimuths are bounded [0:360] float gnuplot_azimuth_ = azimuth_ + 360; while (gnuplot_azimuth_ < 0) { gnuplot_azimuth_ += 360; } while (gnuplot_azimuth_ > 360) { gnuplot_azimuth_ -= 360; } // gnuplot elevation is bounded [0,180] // our elevation is bounded [-90,+90] // matlab -90 = gnuplot 180 // matlab -45 = gnuplot 135 // matlab 0 = gnuplot 90 // matlab +45 = gnuplot 45 // matlab +90 = gnuplot 0 float gnuplot_elevation_ = 90 - elevation_; while (gnuplot_elevation_ < 0) { gnuplot_elevation_ += 180; } while (gnuplot_elevation_ > 180) { gnuplot_elevation_ -= 180; } run_command("set view " + num2str(gnuplot_elevation_) + "," + num2str(gnuplot_azimuth_)); } } void axes_type::run_labels_command() { include_comment("Axes labels"); // set xlabel {"