From 6ff9e40dca7c9e2244de5e330c338fe80773e741 Mon Sep 17 00:00:00 2001 From: Thomas Verbovsek Date: Thu, 31 Oct 2024 14:01:15 +0100 Subject: [PATCH 01/16] fixed imshow for layout attribute changes; call gr_clearws if keep_aspect_ratio or only_quadratic_aspect_ratio is set on multiplots; fix boxzoom in multiplot case; fix log and some other attributes which are set on plot which didnt worked by multiplots; fixed problem with only 1 heatmap and 1 line caused by old values in ranges; made start and stop private, changed pos to position; added 3 new attributes for layout_grid; pos, col_span and row_span for layout_grid_element with updateFilter; fixed legend problem with different window sizes; adjustment for side_regions if no keep_aspect_ratio is set; small change with a workaround for a restriction in plot.cxx; fixed charheight and default_diag_factor for legend for non multiplots; added marginal_heatmap also as central region parent in layout_grid, fixed interaction on marginal_heatmaps in multiplot case; adjusted diag_factor for pie plots cause the legend got always smaller; added missing restore and savestate calls in interaction; changed char_height for 3d axes; fixed window if multiple 3d plots with different windows was plotted; fixed isosurface didnt reset the light correctly; fixed some problem in plot.cxx where not the correct central_region was modified; fixed pie plot got destroyed if tooltips on a different plot were triggered; fixed problem with different files; fixed problem with id which wasnt increased correctly; can now adjust num of rows and cols without strange behaviour; the start and stop col/row of a layout_grid_element can be changed; flip_col_and_row option for layout; polar plots can also be part of multiplots; fix interaction on more than 1 3d plot; DEFAULT_ASPECT_RATIO_FOR_SCALING is only valid for non multiploots; changed args to plot[plot_i] where it was missing; fixed layout if an element is missing in a column or row; fixed mixed plot_types if multiple plots are displayed; fix interaction if layout is used; fixed a problem with _default_diag_factor which lead to wrong colorbar widths and offset if multiple plots where displayed; find a good divisor to split the plots to get a better result if using grplot; new --plot attribute --- lib/grm/grplot/README.md | 10 + lib/grm/grplot/grplot_mainwindow.cxx | 8 +- lib/grm/grplot/grplot_widget.cxx | 63 +- .../graphics_tree/private_schema.xsd | 8 + .../grm/dom_render/graphics_tree/schema.xsd | 42 +- lib/grm/src/grm/dom_render/render.cxx | 1396 +++++++++++++- lib/grm/src/grm/import.cxx | 1718 +++++++++-------- lib/grm/src/grm/interaction.cxx | 49 +- lib/grm/src/grm/plot.cxx | 156 +- 9 files changed, 2381 insertions(+), 1069 deletions(-) diff --git a/lib/grm/grplot/README.md b/lib/grm/grplot/README.md index a565cb9b4..b0dde7f46 100644 --- a/lib/grm/grplot/README.md +++ b/lib/grm/grplot/README.md @@ -85,6 +85,16 @@ To start 'grplot' with a test case you have to run: grplot --test ``` +## Multiple plots + +'grplot' also supports figures with multiple plots by using the --plot command line option. After this keyword the data-file must be named followed by the specific plot parameters if they should be applied to that plot. It's also possible to use interactions in each subplot. + +To create multiple plots in one figure with 'grplot' use: + +```shell +grplot --plot --plot ... +``` + ## Data file These files contain the data that should be plotted. Besides the data these files can include parameters which modify the plot. Important to know is that the parameters which can stand in these files doesn't belong to a specific plot. They are atleast valid for all 2D or 3D plots for example. The first lines of teh datafile define parameters just like the `title` and have the following pattern: diff --git a/lib/grm/grplot/grplot_mainwindow.cxx b/lib/grm/grplot/grplot_mainwindow.cxx index 26b1d8792..7d00a93d5 100644 --- a/lib/grm/grplot/grplot_mainwindow.cxx +++ b/lib/grm/grplot/grplot_mainwindow.cxx @@ -1,6 +1,8 @@ #include "grplot_mainwindow.hxx" const unsigned int MAXPATHLEN = 1024; +const unsigned int WIDTH = 600; +const unsigned int HEIGHT = 450; GRPlotMainWindow::GRPlotMainWindow(int argc, char **argv) : QMainWindow() { @@ -27,19 +29,19 @@ GRPlotMainWindow::GRPlotMainWindow(int argc, char **argv) : QMainWindow() fprintf(stderr, "No plot type with the name %s was found.\n", kind.c_str()); } setCentralWidget(message); - resize(600, 450); + resize(WIDTH, HEIGHT); } else { grplot_widget_ = new GRPlotWidget(this, argc, argv); setCentralWidget(grplot_widget_); - grplot_widget_->resize(600, 450); + grplot_widget_->resize(WIDTH, HEIGHT); } setWindowTitle("GR Plot"); if (strcmp(argv[1], "--listen") != 0) { - resize(600, 450); + resize(WIDTH, HEIGHT); } } diff --git a/lib/grm/grplot/grplot_widget.cxx b/lib/grm/grplot/grplot_widget.cxx index 4689ff880..7a0f008de 100644 --- a/lib/grm/grplot/grplot_widget.cxx +++ b/lib/grm/grplot/grplot_widget.cxx @@ -78,7 +78,7 @@ extern "C" void cmd_callback_wrapper(const grm_event_t *event) GRPlotWidget::GRPlotWidget(QMainWindow *parent, int argc, char **argv) : QWidget(parent), pixmap(), redraw_pixmap(false), args_(nullptr), rubberBand(nullptr) { - const char *kind; + const char *kind = ""; unsigned int z_length; double *z = nullptr; int error = 0; @@ -144,12 +144,14 @@ GRPlotWidget::GRPlotWidget(QMainWindow *parent, int argc, char **argv) "disable_x_trans", "disable_y_trans", "draw_grid", + "flip_col_and_row", "grplot", "hide", "is_major", "is_mirrored", "keep_aspect_ratio", "keep_radii_axes", + "keep_size_if_swapped", "keep_window", "marginal_heatmap_side_plot", "mirrored_axis", @@ -161,6 +163,8 @@ GRPlotWidget::GRPlotWidget(QMainWindow *parent, int argc, char **argv) "space", "stairs", "text_is_title", + "trim_col", + "trim_row", "x_flip", "x_grid", "x_log", @@ -1499,37 +1503,41 @@ void GRPlotWidget::mouseMoveEvent(QMouseEvent *event) getMousePos(event, &x, &y); collectTooltips(); auto global_root = grm_get_document_root(); - auto plot_elem = global_root->querySelectors("plot"); - if (plot_elem) + auto plot_elems = global_root->querySelectorsAll("plot"); + // TODO: Select only the plot_elem where the mouse is (active_plot) + for (const auto &plot_elem : plot_elems) { - kind = static_cast(plot_elem->getAttribute("_kind")); - if (kind == "marginal_heatmap") + if (plot_elem) { - grm_args_t *input_args; - input_args = grm_args_new(); + kind = static_cast(plot_elem->getAttribute("_kind")); + if (kind == "marginal_heatmap") + { + grm_args_t *input_args; + input_args = grm_args_new(); - grm_args_push(input_args, "x", "i", x); - grm_args_push(input_args, "y", "i", y); - grm_input(input_args); - } + grm_args_push(input_args, "x", "i", x); + grm_args_push(input_args, "y", "i", y); + grm_input(input_args); + } - /* get the correct cursor and sets it */ - int cursor_state = grm_get_hover_mode(x, y, disable_movable_xform); - if (cursor_state == DEFAULT_HOVER_MODE) - { - csr->setShape(Qt::ArrowCursor); - } - else if (cursor_state == MOVABLE_HOVER_MODE) - { - csr->setShape(Qt::OpenHandCursor); - } - else if (cursor_state == INTEGRAL_SIDE_HOVER_MODE) - { - csr->setShape(Qt::SizeHorCursor); - } - setCursor(*csr); + /* get the correct cursor and sets it */ + int cursor_state = grm_get_hover_mode(x, y, disable_movable_xform); + if (cursor_state == DEFAULT_HOVER_MODE) + { + csr->setShape(Qt::ArrowCursor); + } + else if (cursor_state == MOVABLE_HOVER_MODE) + { + csr->setShape(Qt::OpenHandCursor); + } + else if (cursor_state == INTEGRAL_SIDE_HOVER_MODE) + { + csr->setShape(Qt::SizeHorCursor); + } + setCursor(*csr); - redraw(); + redraw(); + } } update(); } @@ -2656,7 +2664,6 @@ void GRPlotWidget::generateLinearContextSlot() auto scrollAreaContent = new QWidget; scrollAreaContent->setLayout(form); auto scrollArea = new QScrollArea; - scrollArea = new QScrollArea; scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea->setWidgetResizable(true); diff --git a/lib/grm/src/grm/dom_render/graphics_tree/private_schema.xsd b/lib/grm/src/grm/dom_render/graphics_tree/private_schema.xsd index 01b19af3a..9ff782ac8 100644 --- a/lib/grm/src/grm/dom_render/graphics_tree/private_schema.xsd +++ b/lib/grm/src/grm/dom_render/graphics_tree/private_schema.xsd @@ -27,6 +27,9 @@ + + + @@ -47,6 +50,10 @@ + + + + @@ -269,6 +276,7 @@ + diff --git a/lib/grm/src/grm/dom_render/graphics_tree/schema.xsd b/lib/grm/src/grm/dom_render/graphics_tree/schema.xsd index 5073b99e4..f0747aecb 100644 --- a/lib/grm/src/grm/dom_render/graphics_tree/schema.xsd +++ b/lib/grm/src/grm/dom_render/graphics_tree/schema.xsd @@ -21,8 +21,6 @@ - - @@ -50,24 +48,27 @@ - + - + - - - - + + + + + - - - - + + + + + + @@ -79,22 +80,22 @@ - + - + - - + + + + + - - - - + @@ -536,6 +537,7 @@ + diff --git a/lib/grm/src/grm/dom_render/render.cxx b/lib/grm/src/grm/dom_render/render.cxx index 44acc37cd..a3107c276 100644 --- a/lib/grm/src/grm/dom_render/render.cxx +++ b/lib/grm/src/grm/dom_render/render.cxx @@ -464,6 +464,7 @@ static void processTickGroup(const std::shared_ptr &element, const std::shared_ptr &context); static void processThetaAxes(const std::shared_ptr &element, const std::shared_ptr &context); +static void processSpace3d(const std::shared_ptr &element); /* ~~~~~~~~~~~~~~~~~~~~~~~~~ utility functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -529,11 +530,30 @@ static double getMaxViewport(const std::shared_ptr &element, bool double max_vp; int pixel_width, pixel_height; double metric_width, metric_height; - auto plot_element = global_root->querySelectors("plot"); + auto plot_element = element; + // Todo: this is not correct in subplot case for element layout_grid and figure + if (str_equals_any(element->localName(), "figure", "layout_grid", "layout_grid_element")) + plot_element = element->querySelectors("plot"); + else + getPlotParent(plot_element); GRM::Render::getFigureSize(&pixel_width, &pixel_height, &metric_width, &metric_height); auto aspect_ratio_ws = metric_width / metric_height; auto max_width_height = grm_max(pixel_width, pixel_height); + if (plot_element != nullptr && plot_element->parentElement() != nullptr && + plot_element->parentElement()->localName() == "layout_grid_element") + { + double figure_viewport[4]; + auto figure_vp_element = plot_element->parentElement(); + figure_viewport[0] = static_cast(figure_vp_element->getAttribute("plot_x_min")); + figure_viewport[1] = static_cast(figure_vp_element->getAttribute("plot_x_max")); + figure_viewport[2] = static_cast(figure_vp_element->getAttribute("plot_y_min")); + figure_viewport[3] = static_cast(figure_vp_element->getAttribute("plot_y_max")); + + metric_width *= (figure_viewport[1] - figure_viewport[0]); + metric_height *= (figure_viewport[3] - figure_viewport[2]); + aspect_ratio_ws = metric_width / metric_height; + } if (plot_element == nullptr) return 1; if (x) @@ -900,8 +920,8 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptr plot_parent = element, left_side_region, right_side_region, bottom_side_region, @@ -936,7 +956,38 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptr(plot_parent->getAttribute("_start_aspect_ratio")); + if (plot_parent->parentElement()->localName() == "layout_grid_element") + { + double figure_viewport[4]; + auto figure_vp_element = plot_parent->parentElement(); + figure_viewport[0] = static_cast(figure_vp_element->getAttribute("plot_x_min")); + figure_viewport[1] = static_cast(figure_vp_element->getAttribute("plot_x_max")); + figure_viewport[2] = static_cast(figure_vp_element->getAttribute("plot_y_min")); + figure_viewport[3] = static_cast(figure_vp_element->getAttribute("plot_y_max")); + + org_aspect_ratio_ws = (metric_width * (figure_viewport[1] - figure_viewport[0])) / + (metric_height * (figure_viewport[3] - figure_viewport[2])); + org_vp0 = static_cast(plot_parent->getAttribute("plot_x_min")); + org_vp1 = static_cast(plot_parent->getAttribute("plot_x_max")); + org_vp2 = static_cast(plot_parent->getAttribute("plot_y_min")); + org_vp3 = static_cast(plot_parent->getAttribute("plot_y_max")); + if (org_aspect_ratio_ws > 1) + { + org_vp2 /= org_aspect_ratio_ws; + org_vp3 /= org_aspect_ratio_ws; + } + else + { + org_vp0 *= org_aspect_ratio_ws; + org_vp1 *= org_aspect_ratio_ws; + } + + metric_width *= (figure_viewport[1] - figure_viewport[0]); + metric_height *= (figure_viewport[3] - figure_viewport[2]); + aspect_ratio_ws = metric_width / metric_height; + } if (keep_aspect_ratio && (!only_quadratic_aspect_ratio || (only_quadratic_aspect_ratio && !uniform_data)) && !diag_factor && kind != "imshow") @@ -957,6 +1008,25 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptrparentElement()->localName() == "layout_grid_element") + { + if (org_aspect_ratio_ws > start_aspect_ratio_ws) + { + auto x_min = org_vp0 * (start_aspect_ratio_ws / org_aspect_ratio_ws); + auto x_max = org_vp1 * (start_aspect_ratio_ws / org_aspect_ratio_ws); + auto diff = 0.5 * ((org_vp1 - org_vp0) - (x_max - x_min)); + org_vp0 += diff; + org_vp1 -= diff; + } + else + { + auto y_min = org_vp2 / (start_aspect_ratio_ws / org_aspect_ratio_ws); + auto y_max = org_vp3 / (start_aspect_ratio_ws / org_aspect_ratio_ws); + auto diff = 0.5 * ((org_vp3 - org_vp2) - (y_max - y_min)); + org_vp2 += diff; + org_vp3 -= diff; + } + } } else if (keep_aspect_ratio && uniform_data && only_quadratic_aspect_ratio && !diag_factor && kind != "imshow") { @@ -972,6 +1042,21 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptrparentElement()->localName() == "layout_grid_element") + { + if (org_aspect_ratio_ws > 1) + { + double border = 0.5 * (org_vp1 - org_vp0) * (1.0 - 1.0 / org_aspect_ratio_ws); + org_vp0 += border; + org_vp1 -= border; + } + else if (org_aspect_ratio_ws <= 1) + { + double border = 0.5 * (org_vp3 - org_vp2) * (1.0 - org_aspect_ratio_ws); + org_vp2 += border; + org_vp3 -= border; + } + } } if (str_equals_any(kind, "wireframe", "surface", "plot3", "scatter3", "trisurface", "volume")) @@ -983,6 +1068,16 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptrparentElement()->localName() == "layout_grid_element") + { + auto sum1 = org_vp0 + org_vp1, sum2 = org_vp2 + org_vp3; + extent = grm_min(org_vp1 - org_vp0, org_vp3 - org_vp2); + org_vp0 = 0.5 * (sum1 - extent); + org_vp1 = 0.5 * (sum1 + extent); + org_vp2 = 0.5 * (sum2 - extent); + org_vp3 = 0.5 * (sum2 + extent); + } } else { @@ -991,17 +1086,24 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptrparentElement()->localName() != "layout_grid_element") + { + org_vp0 = vp0; + org_vp1 = vp1; + org_vp2 = vp2; + org_vp3 = vp3; + } // margin respects colorbar and sideplot in the specific side_region // TODO: respect individual size defined by user - sidePlotMargin(left_side_region, &left_margin, (vp1 - vp0) * 0.1, (keep_aspect_ratio && !diag_factor), + sidePlotMargin(left_side_region, &left_margin, (org_vp1 - org_vp0) * 0.1, (keep_aspect_ratio && !diag_factor), + aspect_ratio_ws, start_aspect_ratio_ws); + sidePlotMargin(right_side_region, &right_margin, (org_vp1 - org_vp0) * 0.1, (keep_aspect_ratio && !diag_factor), aspect_ratio_ws, start_aspect_ratio_ws); - sidePlotMargin(right_side_region, &right_margin, (vp1 - vp0) * 0.1, (keep_aspect_ratio && !diag_factor), + sidePlotMargin(bottom_side_region, &bottom_margin, (org_vp3 - org_vp2) * 0.1, (keep_aspect_ratio && !diag_factor), aspect_ratio_ws, start_aspect_ratio_ws); - sidePlotMargin(bottom_side_region, &bottom_margin, (vp3 - vp2) * 0.1, (keep_aspect_ratio && !diag_factor), + sidePlotMargin(top_side_region, &top_margin, (org_vp3 - org_vp2) * 0.1, (keep_aspect_ratio && !diag_factor), aspect_ratio_ws, start_aspect_ratio_ws); - sidePlotMargin(top_side_region, &top_margin, (vp3 - vp2) * 0.1, (keep_aspect_ratio && !diag_factor), aspect_ratio_ws, - start_aspect_ratio_ws); if (!top_text_is_title && top_margin != 0) top_margin += 0.05; if (bottom_margin != 0) bottom_margin += 0.05; @@ -1151,7 +1253,7 @@ static void setViewportForSideRegionElements(const std::shared_ptr bool uniform_data) { double viewport[4]; - std::string location = PLOT_DEFAULT_SIDEREGION_LOCATION; + std::string location = PLOT_DEFAULT_SIDEREGION_LOCATION, kind; double max_vp, min_vp; double offset_rel, width_rel; double metric_width, metric_height, start_aspect_ratio_ws; @@ -1170,22 +1272,91 @@ static void setViewportForSideRegionElements(const std::shared_ptr only_quadratic_aspect_ratio = static_cast(plot_parent->getAttribute("only_quadratic_aspect_ratio")); start_aspect_ratio_ws = static_cast(plot_parent->getAttribute("_start_aspect_ratio")); location = static_cast(side_region->getAttribute("location")); + kind = static_cast(plot_parent->getAttribute("_kind")); - GRM::Render::getFigureSize(nullptr, nullptr, &metric_width, &metric_height); - auto aspect_ratio_ws = metric_width / metric_height; double diag_factor = std::sqrt((viewport[1] - viewport[0]) * (viewport[1] - viewport[0]) + (viewport[3] - viewport[2]) * (viewport[3] - viewport[2])); if (!element->hasAttribute("_default_diag_factor")) - element->setAttribute("_default_diag_factor", - ((DEFAULT_ASPECT_RATIO_FOR_SCALING) * - (start_aspect_ratio_ws <= 1 ? start_aspect_ratio_ws : (1.0 / start_aspect_ratio_ws))) / - diag_factor); + { + auto initial_size_x = static_cast(active_figure->getAttribute("_initial_width")); + auto initial_size_y = static_cast(active_figure->getAttribute("_initial_height")); + auto size_x = static_cast(active_figure->getAttribute("size_x")); + auto size_y = static_cast(active_figure->getAttribute("size_y")); + auto size_scale_factor = 1.0; + if ((initial_size_x != size_x || initial_size_y != size_y) && (active_figure->hasAttribute("_kind_changed"))) + size_scale_factor = (size_x < size_y) ? (size_y / size_x) : (size_x / size_y); + double figure_viewport[4]; + auto figure_vp_element = plot_parent->parentElement()->localName() == "layout_grid_element" + ? plot_parent->parentElement() + : plot_parent; + figure_viewport[0] = static_cast(figure_vp_element->getAttribute("plot_x_min")); + figure_viewport[1] = static_cast(figure_vp_element->getAttribute("plot_x_max")); + figure_viewport[2] = static_cast(figure_vp_element->getAttribute("plot_y_min")); + figure_viewport[3] = static_cast(figure_vp_element->getAttribute("plot_y_max")); + + auto default_diag_factor = + ((DEFAULT_ASPECT_RATIO_FOR_SCALING) * + (start_aspect_ratio_ws <= 1 ? start_aspect_ratio_ws : (1.0 / start_aspect_ratio_ws))) / + (diag_factor * size_scale_factor); + if (figure_vp_element != plot_parent) + { + auto num_col = static_cast(figure_vp_element->parentElement()->getAttribute("num_col")); + auto num_row = static_cast(figure_vp_element->parentElement()->getAttribute("num_row")); + auto start_col = static_cast(figure_vp_element->getAttribute("_start_col")); + auto stop_col = static_cast(figure_vp_element->getAttribute("_stop_col")); + auto start_row = static_cast(figure_vp_element->getAttribute("_start_row")); + auto stop_row = static_cast(figure_vp_element->getAttribute("_stop_row")); + if (figure_vp_element->parentElement()->parentElement() != nullptr && + figure_vp_element->parentElement()->parentElement()->localName() == "layout_grid") + { + num_col = static_cast(figure_vp_element->parentElement()->parentElement()->getAttribute("num_col")); + num_row = static_cast(figure_vp_element->parentElement()->parentElement()->getAttribute("num_row")); + } + num_col -= stop_col - start_col - 1; + num_row -= stop_row - start_row - 1; + + if (num_row > 1 && num_row < num_col && num_row % 2 != num_col % 2) + { + default_diag_factor *= (1. / (num_col + 1)); + if (num_row % 2 == 0) default_diag_factor /= start_aspect_ratio_ws; + if (polar_kinds.count(kind) > 0) default_diag_factor *= DEFAULT_ASPECT_RATIO_FOR_SCALING; + } + else if (num_row > 1) + { + default_diag_factor *= (1. / num_col); + if (polar_kinds.count(kind) > 0 && num_row != num_col) + default_diag_factor *= DEFAULT_ASPECT_RATIO_FOR_SCALING; + } + else if (num_row == 1 && num_row % 2 == num_col % 2) + default_diag_factor *= 0.6; + else if (num_row == 1 && num_row % 2 != num_col % 2) + default_diag_factor *= 0.5; + } + element->setAttribute("_default_diag_factor", default_diag_factor); + } // special case for keep_aspect_ratio with uniform data which can lead to smaller plots if ((keep_aspect_ratio && uniform_data && only_quadratic_aspect_ratio) || !keep_aspect_ratio) { - if (!element->hasAttribute("_offset_set_by_user")) offset *= DEFAULT_ASPECT_RATIO_FOR_SCALING; - if (!element->hasAttribute("_width_set_by_user")) width *= DEFAULT_ASPECT_RATIO_FOR_SCALING; + double figure_viewport[4]; + auto figure_vp_element = plot_parent->parentElement()->localName() == "layout_grid_element" + ? plot_parent->parentElement() + : plot_parent; + figure_viewport[0] = static_cast(figure_vp_element->getAttribute("plot_x_min")); + figure_viewport[1] = static_cast(figure_vp_element->getAttribute("plot_x_max")); + figure_viewport[2] = static_cast(figure_vp_element->getAttribute("plot_y_min")); + figure_viewport[3] = static_cast(figure_vp_element->getAttribute("plot_y_max")); + + GRM::Render::getFigureSize(nullptr, nullptr, &metric_width, &metric_height); + metric_width *= (figure_viewport[1] - figure_viewport[0]); + metric_height *= (figure_viewport[3] - figure_viewport[2]); + auto aspect_ratio_ws = metric_width / metric_height; + + if (figure_vp_element == plot_parent) + { + if (!element->hasAttribute("_offset_set_by_user")) offset *= DEFAULT_ASPECT_RATIO_FOR_SCALING; + if (!element->hasAttribute("_width_set_by_user")) width *= DEFAULT_ASPECT_RATIO_FOR_SCALING; + } if (aspect_ratio_ws <= 1) { offset_rel = offset * aspect_ratio_ws; @@ -1196,6 +1367,40 @@ static void setViewportForSideRegionElements(const std::shared_ptr offset_rel = offset / aspect_ratio_ws; width_rel = width / aspect_ratio_ws; } + if (figure_vp_element != plot_parent) + { + auto num_col = static_cast(figure_vp_element->parentElement()->getAttribute("num_col")); + auto num_row = static_cast(figure_vp_element->parentElement()->getAttribute("num_row")); + auto start_col = static_cast(figure_vp_element->getAttribute("_start_col")); + auto stop_col = static_cast(figure_vp_element->getAttribute("_stop_col")); + auto start_row = static_cast(figure_vp_element->getAttribute("_start_row")); + auto stop_row = static_cast(figure_vp_element->getAttribute("_stop_row")); + if (figure_vp_element->parentElement()->parentElement() != nullptr && + figure_vp_element->parentElement()->parentElement()->localName() == "layout_grid") + { + num_col = static_cast(figure_vp_element->parentElement()->parentElement()->getAttribute("num_col")); + num_row = static_cast(figure_vp_element->parentElement()->parentElement()->getAttribute("num_row")); + } + num_col -= stop_col - start_col - 1; + num_row -= stop_row - start_row - 1; + + if (location == "left" || location == "right") + { + if (num_row != 1) + { + offset_rel /= num_col; + width_rel /= num_col; + } + } + else + { + if (num_col != 1) + { + offset_rel /= num_row; + width_rel /= num_row; + } + } + } } else { @@ -1292,6 +1497,7 @@ static void calculateViewport(const std::shared_ptr &element) GRM::Render::getFigureSize(nullptr, nullptr, &metric_width, &metric_height); aspect_ratio_ws = metric_width / metric_height; + // plot viewport fits into the window, for this the complete window must be considered in aspect_ratio_ws if (aspect_ratio_ws > 1) { vp[2] /= aspect_ratio_ws; @@ -1302,6 +1508,20 @@ static void calculateViewport(const std::shared_ptr &element) vp[0] *= aspect_ratio_ws; vp[1] *= aspect_ratio_ws; } + + if (element->parentElement()->localName() == "layout_grid_element") + { + double figure_viewport[4]; + auto figure_vp_element = element->parentElement(); + figure_viewport[0] = static_cast(figure_vp_element->getAttribute("plot_x_min")); + figure_viewport[1] = static_cast(figure_vp_element->getAttribute("plot_x_max")); + figure_viewport[2] = static_cast(figure_vp_element->getAttribute("plot_y_min")); + figure_viewport[3] = static_cast(figure_vp_element->getAttribute("plot_y_max")); + + metric_width *= (figure_viewport[1] - figure_viewport[0]); + metric_height *= (figure_viewport[3] - figure_viewport[2]); + aspect_ratio_ws = metric_width / metric_height; + } if (!element->hasAttribute("_start_aspect_ratio")) element->setAttribute("_start_aspect_ratio", aspect_ratio_ws); render->setViewport(element, vp[0], vp[1], vp[2], vp[3]); @@ -1331,6 +1551,7 @@ static void calculateViewport(const std::shared_ptr &element) static_cast(plot_parent->getAttribute("plot_y_min")) / (DEFAULT_ASPECT_RATIO_FOR_SCALING); plot_viewport[3] = static_cast(plot_parent->getAttribute("plot_y_max")) / (DEFAULT_ASPECT_RATIO_FOR_SCALING); + keep_aspect_ratio = static_cast(plot_parent->getAttribute("keep_aspect_ratio")); only_quadratic_aspect_ratio = static_cast(plot_parent->getAttribute("only_quadratic_aspect_ratio")); kind = static_cast(plot_parent->getAttribute("_kind")); @@ -1414,6 +1635,7 @@ static void calculateViewport(const std::shared_ptr &element) static_cast(plot_parent->getAttribute("plot_y_min")) / (DEFAULT_ASPECT_RATIO_FOR_SCALING); plot_viewport[3] = static_cast(plot_parent->getAttribute("plot_y_max")) / (DEFAULT_ASPECT_RATIO_FOR_SCALING); + location = static_cast(element->parentElement()->getAttribute("location")); kind = static_cast(plot_parent->getAttribute("_kind")); @@ -1471,6 +1693,7 @@ static void calculateViewport(const std::shared_ptr &element) static_cast(plot_parent->getAttribute("plot_y_min")) / (DEFAULT_ASPECT_RATIO_FOR_SCALING); plot_viewport[3] = static_cast(plot_parent->getAttribute("plot_y_max")) / (DEFAULT_ASPECT_RATIO_FOR_SCALING); + keep_aspect_ratio = static_cast(plot_parent->getAttribute("keep_aspect_ratio")); only_quadratic_aspect_ratio = static_cast(plot_parent->getAttribute("only_quadratic_aspect_ratio")); location = static_cast(element->parentElement()->getAttribute("location")); @@ -1574,9 +1797,10 @@ static void calculateViewport(const std::shared_ptr &element) const std::shared_ptr &context = render->getContext(); std::string kind, labels_key = static_cast(element->getAttribute("labels")); auto labels = GRM::get>((*context)[labels_key]); - std::shared_ptr central_region; + std::shared_ptr central_region, plot_parent = element; bool keep_aspect_ratio = false; + getPlotParent(plot_parent); for (const auto &child : element->parentElement()->children()) { if (child->localName() == "central_region") @@ -1617,24 +1841,113 @@ static void calculateViewport(const std::shared_ptr &element) GRM::Render::getFigureSize(nullptr, nullptr, &metric_width, &metric_height); auto aspect_ratio_ws = metric_width / metric_height; - scale_factor *= DEFAULT_ASPECT_RATIO_FOR_SCALING; + if (plot_parent->parentElement()->localName() == "layout_grid_element") + { + double figure_viewport[4]; + auto figure_vp_element = plot_parent->parentElement(); + figure_viewport[0] = static_cast(figure_vp_element->getAttribute("plot_x_min")); + figure_viewport[1] = static_cast(figure_vp_element->getAttribute("plot_x_max")); + figure_viewport[2] = static_cast(figure_vp_element->getAttribute("plot_y_min")); + figure_viewport[3] = static_cast(figure_vp_element->getAttribute("plot_y_max")); + + metric_width *= (figure_viewport[1] - figure_viewport[0]); + metric_height *= (figure_viewport[3] - figure_viewport[2]); + aspect_ratio_ws = metric_width / metric_height; + } + + if (plot_parent->parentElement()->localName() != "layout_grid_element") + scale_factor *= DEFAULT_ASPECT_RATIO_FOR_SCALING; scale_factor *= (aspect_ratio_ws <= 1) ? aspect_ratio_ws : 1.0 / aspect_ratio_ws; + + if (plot_parent->parentElement()->localName() == "layout_grid_element") + { + auto figure_vp_element = plot_parent->parentElement(); + auto num_col = static_cast(figure_vp_element->parentElement()->getAttribute("num_col")); + auto num_row = static_cast(figure_vp_element->parentElement()->getAttribute("num_row")); + auto start_col = static_cast(figure_vp_element->getAttribute("_start_col")); + auto stop_col = static_cast(figure_vp_element->getAttribute("_stop_col")); + auto start_row = static_cast(figure_vp_element->getAttribute("_start_row")); + auto stop_row = static_cast(figure_vp_element->getAttribute("_stop_row")); + if (figure_vp_element->parentElement()->parentElement() != nullptr && + figure_vp_element->parentElement()->parentElement()->localName() == "layout_grid") + { + num_col = + static_cast(figure_vp_element->parentElement()->parentElement()->getAttribute("num_col")); + num_row = + static_cast(figure_vp_element->parentElement()->parentElement()->getAttribute("num_row")); + } + num_col -= stop_col - start_col - 1; + num_row -= stop_row - start_row - 1; + + if (num_col > 1 && num_row > 1) + scale_factor /= grm_min(num_col, num_row); + else + scale_factor *= grm_max(num_col, num_row); + } } else { double diag_factor = std::sqrt((viewport[1] - viewport[0]) * (viewport[1] - viewport[0]) + (viewport[3] - viewport[2]) * (viewport[3] - viewport[2])); if (!element->hasAttribute("_default_diag_factor")) - element->setAttribute( - "_default_diag_factor", - ((DEFAULT_ASPECT_RATIO_FOR_SCALING) * - (start_aspect_ratio_ws <= 1 ? start_aspect_ratio_ws : (1.0 / start_aspect_ratio_ws))) / - diag_factor); + { + auto initial_size_x = static_cast(active_figure->getAttribute("_initial_width")); + auto initial_size_y = static_cast(active_figure->getAttribute("_initial_height")); + auto size_x = static_cast(active_figure->getAttribute("size_x")); + auto size_y = static_cast(active_figure->getAttribute("size_y")); + auto size_scale_factor = 1.0; + if ((initial_size_x != size_x || initial_size_y != size_y) && + (active_figure->hasAttribute("_kind_changed"))) + size_scale_factor = (size_x < size_y) ? (size_y / size_x) : (size_x / size_y); + double figure_viewport[4]; + auto figure_vp_element = plot_parent->parentElement()->localName() == "layout_grid_element" + ? plot_parent->parentElement() + : plot_parent; + figure_viewport[0] = static_cast(figure_vp_element->getAttribute("plot_x_min")); + figure_viewport[1] = static_cast(figure_vp_element->getAttribute("plot_x_max")); + figure_viewport[2] = static_cast(figure_vp_element->getAttribute("plot_y_min")); + figure_viewport[3] = static_cast(figure_vp_element->getAttribute("plot_y_max")); + + auto default_diag_factor = + ((DEFAULT_ASPECT_RATIO_FOR_SCALING) * + (start_aspect_ratio_ws <= 1 ? start_aspect_ratio_ws : (1.0 / start_aspect_ratio_ws))) / + (diag_factor * size_scale_factor); + if (figure_vp_element != plot_parent) + { + auto num_col = static_cast(figure_vp_element->parentElement()->getAttribute("num_col")); + auto num_row = static_cast(figure_vp_element->parentElement()->getAttribute("num_row")); + auto start_col = static_cast(figure_vp_element->getAttribute("_start_col")); + auto stop_col = static_cast(figure_vp_element->getAttribute("_stop_col")); + auto start_row = static_cast(figure_vp_element->getAttribute("_start_row")); + auto stop_row = static_cast(figure_vp_element->getAttribute("_stop_row")); + if (figure_vp_element->parentElement()->parentElement() != nullptr && + figure_vp_element->parentElement()->parentElement()->localName() == "layout_grid") + { + num_col = static_cast( + figure_vp_element->parentElement()->parentElement()->getAttribute("num_col")); + num_row = static_cast( + figure_vp_element->parentElement()->parentElement()->getAttribute("num_row")); + } + num_col -= stop_col - start_col - 1; + num_row -= stop_row - start_row - 1; + + auto plot_diag_factor = + std::sqrt((figure_viewport[1] - figure_viewport[0]) * (figure_viewport[1] - figure_viewport[0]) + + (figure_viewport[3] - figure_viewport[2]) * (figure_viewport[3] - figure_viewport[2])); + default_diag_factor = + ((DEFAULT_ASPECT_RATIO_FOR_SCALING) * + (start_aspect_ratio_ws <= 1 ? start_aspect_ratio_ws : (1.0 / start_aspect_ratio_ws))) / + ((diag_factor * size_scale_factor) / plot_diag_factor); + if (num_col > 1 && num_row > num_col) default_diag_factor *= DEFAULT_ASPECT_RATIO_FOR_SCALING; + } + element->setAttribute("_default_diag_factor", default_diag_factor); + } auto default_diag_factor = static_cast(element->getAttribute("_default_diag_factor")); scale_factor = diag_factor * default_diag_factor; } element->setAttribute("_scale_factor", scale_factor); + if (!element->hasAttribute("_initial_scale_factor")) element->setAttribute("_initial_scale_factor", scale_factor); if (kind != "pie") { @@ -2372,6 +2685,7 @@ static void markerHelper(const std::shared_ptr &element, const std } else if (str == "polymarker_3d") { + processSpace3d(element->parentElement()->parentElement()); if (redraw_ws) gr_polymarker3d(1, (double *)&(x_vec[i]), (double *)&(y_vec[i]), (double *)&(z_vec[i])); } } @@ -2492,6 +2806,7 @@ static void lineHelper(const std::shared_ptr &element, const std:: } else if (str == "polyline_3d") { + processSpace3d(element->parentElement()->parentElement()); if (redraw_ws) gr_polyline3d(2, (double *)&(x_vec[i]), (double *)&(y_vec[i]), (double *)&(z_vec[i])); } } @@ -2529,6 +2844,8 @@ static void getTickSize(const std::shared_ptr &element, double &ti GRM::Render::getFigureSize(nullptr, nullptr, &metric_width, &metric_height); auto aspect_ratio_ws = metric_width / metric_height; + auto location = static_cast( + element->parentElement()->parentElement()->parentElement()->getAttribute("location")); if (element->hasAttribute("_tick_size_set_by_user")) { @@ -2566,6 +2883,20 @@ static void getTickSize(const std::shared_ptr &element, double &ti if ((keep_aspect_ratio && uniform_data && only_quadratic_aspect_ratio) || !keep_aspect_ratio) { + if (plot_parent->parentElement()->localName() == "layout_grid_element") + { + double figure_viewport[4]; + auto figure_vp_element = plot_parent->parentElement(); + figure_viewport[0] = static_cast(figure_vp_element->getAttribute("plot_x_min")); + figure_viewport[1] = static_cast(figure_vp_element->getAttribute("plot_x_max")); + figure_viewport[2] = static_cast(figure_vp_element->getAttribute("plot_y_min")); + figure_viewport[3] = static_cast(figure_vp_element->getAttribute("plot_y_max")); + + metric_width *= (figure_viewport[1] - figure_viewport[0]); + metric_height *= (figure_viewport[3] - figure_viewport[2]); + aspect_ratio_ws = metric_width / metric_height; + } + if (aspect_ratio_ws <= 1) { tick_size_rel = tick_size * aspect_ratio_ws; @@ -2574,7 +2905,9 @@ static void getTickSize(const std::shared_ptr &element, double &ti { tick_size_rel = tick_size / aspect_ratio_ws; } - if (!element->hasAttribute("_tick_size_set_by_user")) tick_size_rel *= DEFAULT_ASPECT_RATIO_FOR_SCALING; + if (!element->hasAttribute("_tick_size_set_by_user") && + plot_parent->parentElement()->localName() != "layout_grid_element") + tick_size_rel *= DEFAULT_ASPECT_RATIO_FOR_SCALING; } else { @@ -2601,18 +2934,68 @@ static void getTickSize(const std::shared_ptr &element, double &ti auto start_aspect_ratio_ws = static_cast(plot_parent->getAttribute("_start_aspect_ratio")); auto diag_factor = std::sqrt((viewport[1] - viewport[0]) * (viewport[1] - viewport[0]) + (viewport[3] - viewport[2]) * (viewport[3] - viewport[2])); - if (element->hasAttribute("_default_diag_factor")) - { - default_diag_factor = static_cast(element->getAttribute("_default_diag_factor")); - } - else + if (!element->hasAttribute("_default_diag_factor")) { + auto initial_size_x = static_cast(active_figure->getAttribute("_initial_width")); + auto initial_size_y = static_cast(active_figure->getAttribute("_initial_height")); + auto size_x = static_cast(active_figure->getAttribute("size_x")); + auto size_y = static_cast(active_figure->getAttribute("size_y")); + auto size_scale_factor = 1.0; + if ((initial_size_x != size_x || initial_size_y != size_y) && + (active_figure->hasAttribute("_kind_changed"))) + size_scale_factor = (size_x < size_y) ? (size_y / size_x) : (size_x / size_y); + double figure_viewport[4]; + auto figure_vp_element = plot_parent->parentElement()->localName() == "layout_grid_element" + ? plot_parent->parentElement() + : plot_parent; + figure_viewport[0] = static_cast(figure_vp_element->getAttribute("plot_x_min")); + figure_viewport[1] = static_cast(figure_vp_element->getAttribute("plot_x_max")); + figure_viewport[2] = static_cast(figure_vp_element->getAttribute("plot_y_min")); + figure_viewport[3] = static_cast(figure_vp_element->getAttribute("plot_y_max")); + default_diag_factor = ((DEFAULT_ASPECT_RATIO_FOR_SCALING) * (start_aspect_ratio_ws <= 1 ? start_aspect_ratio_ws : (1.0 / start_aspect_ratio_ws))) / - diag_factor; + (diag_factor * size_scale_factor); + if (figure_vp_element != plot_parent) + { + auto num_col = static_cast(figure_vp_element->parentElement()->getAttribute("num_col")); + auto num_row = static_cast(figure_vp_element->parentElement()->getAttribute("num_row")); + auto start_col = static_cast(figure_vp_element->getAttribute("_start_col")); + auto stop_col = static_cast(figure_vp_element->getAttribute("_stop_col")); + auto start_row = static_cast(figure_vp_element->getAttribute("_start_row")); + auto stop_row = static_cast(figure_vp_element->getAttribute("_stop_row")); + if (figure_vp_element->parentElement()->parentElement() != nullptr && + figure_vp_element->parentElement()->parentElement()->localName() == "layout_grid") + { + num_col = static_cast( + figure_vp_element->parentElement()->parentElement()->getAttribute("num_col")); + num_row = static_cast( + figure_vp_element->parentElement()->parentElement()->getAttribute("num_row")); + } + num_col -= stop_col - start_col - 1; + num_row -= stop_row - start_row - 1; + + if (num_row > 1 && num_row < num_col && num_row % 2 != num_col % 2) + { + default_diag_factor *= (1. / (num_col + 1)); + if (num_row % 2 == 0) default_diag_factor /= start_aspect_ratio_ws; + if (polar_kinds.count(kind) > 0) default_diag_factor *= DEFAULT_ASPECT_RATIO_FOR_SCALING; + } + else if (num_row > 1) + { + default_diag_factor *= (1. / num_col); + if (polar_kinds.count(kind) > 0 && num_row != num_col) + default_diag_factor *= DEFAULT_ASPECT_RATIO_FOR_SCALING; + } + else if (num_row == 1 && num_row % 2 == num_col % 2) + default_diag_factor *= 0.6; + else if (num_row == 1 && num_row % 2 != num_col % 2) + default_diag_factor *= 0.5; + } element->setAttribute("_default_diag_factor", default_diag_factor); } + default_diag_factor = static_cast(element->getAttribute("_default_diag_factor")); tick_size_rel = tick_size * diag_factor * default_diag_factor; } tick_size = tick_size_rel; @@ -3069,17 +3452,7 @@ void GRM::Render::getFigureSize(int *pixel_width, int *pixel_height, double *met dpi[1] = dpm[1] * 0.0254; /* TODO: Overwork this calculation */ - if (figure->hasAttribute("fig_size_x") && figure->hasAttribute("fig_size_y")) - { - tmp_size_d[0] = static_cast(figure->getAttribute("fig_size_x")); - tmp_size_d[1] = static_cast(figure->getAttribute("fig_size_y")); - for (i = 0; i < 2; ++i) - { - pixel_size[i] = (int)grm_round(tmp_size_d[i] * dpi[i]); - metric_size[i] = tmp_size_d[i] / 0.0254; - } - } - else if (figure->hasAttribute("size_x") && figure->hasAttribute("size_y")) + if (figure->hasAttribute("size_x") && figure->hasAttribute("size_y")) { for (i = 0; i < 2; ++i) { @@ -4524,6 +4897,13 @@ static void processRelativeCharHeight(const std::shared_ptr &eleme GRM::Render::getFigureSize(nullptr, nullptr, &metric_width, &metric_height); auto aspect_ratio_ws = metric_width / metric_height; + if (plot_element->parentElement()->localName() == "layout_grid_element") + { + metric_width *= (figure_viewport[1] - figure_viewport[0]); + metric_height *= (figure_viewport[3] - figure_viewport[2]) * DEFAULT_ASPECT_RATIO_FOR_SCALING; + aspect_ratio_ws = metric_width / metric_height; + } + // special case for keep_aspect_ratio with uniform data which can lead to smaller plots if (keep_aspect_ratio && only_quadratic_aspect_ratio) { @@ -4569,15 +4949,86 @@ static void processRelativeCharHeight(const std::shared_ptr &eleme } else { - calculateCentralRegionMarginOrDiagFactor(element, &figure_viewport[0], &figure_viewport[1], - &figure_viewport[2], &figure_viewport[3], true); - double plot_diag_factor = - std::sqrt((figure_viewport[1] - figure_viewport[0]) * (figure_viewport[1] - figure_viewport[0]) + - (figure_viewport[3] - figure_viewport[2]) * (figure_viewport[3] - figure_viewport[2])); + auto initial_size_x = static_cast(active_figure->getAttribute("_initial_width")); + auto initial_size_y = static_cast(active_figure->getAttribute("_initial_height")); + auto size_x = static_cast(active_figure->getAttribute("size_x")); + auto size_y = static_cast(active_figure->getAttribute("size_y")); + auto size_scale_factor = 1.0; + if ((initial_size_x != size_x || initial_size_y != size_y) && (active_figure->hasAttribute("_kind_changed"))) + size_scale_factor = (size_x < size_y) ? (size_y / size_x) : (size_x / size_y); auto start_aspect_ratio_ws = static_cast(plot_element->getAttribute("_start_aspect_ratio")); - default_diag_factor = ((DEFAULT_ASPECT_RATIO_FOR_SCALING) * - (start_aspect_ratio_ws <= 1 ? start_aspect_ratio_ws : (1.0 / start_aspect_ratio_ws))) * - (plot_diag_factor / diag_factor); + double multi_plot_factor = + grm_max(std::sqrt((figure_viewport[1] - figure_viewport[0]) * (figure_viewport[1] - figure_viewport[0]) + + (figure_viewport[3] - figure_viewport[2]) * (figure_viewport[3] - figure_viewport[2]) * + DEFAULT_ASPECT_RATIO_FOR_SCALING * DEFAULT_ASPECT_RATIO_FOR_SCALING) / + sqrt(5), + figure_viewport[1] - figure_viewport[0]); + + if (figure_vp_element == plot_element) + { + calculateCentralRegionMarginOrDiagFactor(element, &figure_viewport[0], &figure_viewport[1], + &figure_viewport[2], &figure_viewport[3], true); + double plot_diag_factor = + std::sqrt((figure_viewport[1] - figure_viewport[0]) * (figure_viewport[1] - figure_viewport[0]) + + (figure_viewport[3] - figure_viewport[2]) * (figure_viewport[3] - figure_viewport[2])); + default_diag_factor = + ((DEFAULT_ASPECT_RATIO_FOR_SCALING) * + (start_aspect_ratio_ws <= 1 ? start_aspect_ratio_ws : (1.0 / start_aspect_ratio_ws))) * + (plot_diag_factor / (diag_factor * size_scale_factor)); + } + else + { + GRM::Render::getFigureSize(nullptr, nullptr, &metric_width, &metric_height); + auto default_aspect_ratio_ws = + (metric_width * (figure_viewport[1] - figure_viewport[0])) / + (metric_height * (figure_viewport[3] - figure_viewport[2]) * DEFAULT_ASPECT_RATIO_FOR_SCALING); + figure_viewport[0] = static_cast(plot_element->getAttribute("plot_x_min")); + figure_viewport[1] = static_cast(plot_element->getAttribute("plot_x_max")); + figure_viewport[2] = static_cast(plot_element->getAttribute("plot_y_min")); + figure_viewport[3] = static_cast(plot_element->getAttribute("plot_y_max")); + if (default_aspect_ratio_ws > 1) + { + viewport[2] /= default_aspect_ratio_ws; + viewport[3] /= default_aspect_ratio_ws; + } + else + { + viewport[0] *= default_aspect_ratio_ws; + viewport[1] *= default_aspect_ratio_ws; + } + calculateCentralRegionMarginOrDiagFactor(element, &figure_viewport[0], &figure_viewport[1], + &figure_viewport[2], &figure_viewport[3], true); + calculateCentralRegionMarginOrDiagFactor(element, &figure_viewport[0], &figure_viewport[1], + &figure_viewport[2], &figure_viewport[3], true); + + auto num_col = static_cast(figure_vp_element->parentElement()->getAttribute("num_col")); + auto num_row = static_cast(figure_vp_element->parentElement()->getAttribute("num_row")); + auto start_col = static_cast(figure_vp_element->getAttribute("_start_col")); + auto stop_col = static_cast(figure_vp_element->getAttribute("_stop_col")); + auto start_row = static_cast(figure_vp_element->getAttribute("_start_row")); + auto stop_row = static_cast(figure_vp_element->getAttribute("_stop_row")); + if (figure_vp_element->parentElement()->parentElement() != nullptr && + figure_vp_element->parentElement()->parentElement()->localName() == "layout_grid") + { + num_col = + static_cast(figure_vp_element->parentElement()->parentElement()->getAttribute("num_col")); + num_row = + static_cast(figure_vp_element->parentElement()->parentElement()->getAttribute("num_row")); + } + num_col -= stop_col - start_col - 1; + num_row -= stop_row - start_row - 1; + + double plot_diag_factor = + std::sqrt((figure_viewport[1] - figure_viewport[0]) * (figure_viewport[1] - figure_viewport[0]) + + (figure_viewport[3] - figure_viewport[2]) * (figure_viewport[3] - figure_viewport[2])); + default_diag_factor = + ((DEFAULT_ASPECT_RATIO_FOR_SCALING) * + (start_aspect_ratio_ws <= 1 ? start_aspect_ratio_ws : (1.0 / start_aspect_ratio_ws))) * + (plot_diag_factor / (diag_factor * size_scale_factor)); + + if (num_col < num_row && num_col == 1) multi_plot_factor = 1. / (DEFAULT_ASPECT_RATIO_FOR_SCALING); + default_diag_factor *= multi_plot_factor; + } element->setAttribute("_default_diag_factor", default_diag_factor); } diag_factor *= default_diag_factor; @@ -4586,7 +5037,9 @@ static void processRelativeCharHeight(const std::shared_ptr &eleme if ((keep_aspect_ratio && uniform_data && only_quadratic_aspect_ratio) || !keep_aspect_ratio) { - if (!element->hasAttribute("_max_char_height_set_by_user")) max_char_height *= DEFAULT_ASPECT_RATIO_FOR_SCALING; + if (!element->hasAttribute("_max_char_height_set_by_user") && + plot_element->parentElement()->localName() != "layout_grid_element") + max_char_height *= DEFAULT_ASPECT_RATIO_FOR_SCALING; if (aspect_ratio_ws <= 1) { max_char_height_rel = max_char_height * aspect_ratio_ws; @@ -5593,6 +6046,13 @@ void GRM::Render::calculateCharHeight(const std::shared_ptr &eleme GRM::Render::getFigureSize(nullptr, nullptr, &metric_width, &metric_height); auto aspect_ratio_ws = metric_width / metric_height; + if (plot_parent->parentElement()->localName() == "layout_grid_element") + { + metric_width *= (figure_viewport[1] - figure_viewport[0]); + metric_height *= (figure_viewport[3] - figure_viewport[2]) * DEFAULT_ASPECT_RATIO_FOR_SCALING; + aspect_ratio_ws = metric_width / metric_height; + } + // special case for keep_aspect_ratio with uniform data which can lead to smaller plots if (keep_aspect_ratio && only_quadratic_aspect_ratio) { @@ -5628,16 +6088,76 @@ void GRM::Render::calculateCharHeight(const std::shared_ptr &eleme (viewport[3] - viewport[2]) * (viewport[3] - viewport[2])); if (!element->hasAttribute("_default_diag_factor")) { - calculateCentralRegionMarginOrDiagFactor(element, &figure_viewport[0], &figure_viewport[1], - &figure_viewport[2], &figure_viewport[3], true); - double plot_diag_factor = - std::sqrt((figure_viewport[1] - figure_viewport[0]) * (figure_viewport[1] - figure_viewport[0]) + - (figure_viewport[3] - figure_viewport[2]) * (figure_viewport[3] - figure_viewport[2])); + double default_diag_factor; + auto initial_size_x = static_cast(active_figure->getAttribute("_initial_width")); + auto initial_size_y = static_cast(active_figure->getAttribute("_initial_height")); + auto size_x = static_cast(active_figure->getAttribute("size_x")); + auto size_y = static_cast(active_figure->getAttribute("size_y")); + auto size_scale_factor = 1.0; + if ((initial_size_x != size_x || initial_size_y != size_y) && (active_figure->hasAttribute("_kind_changed"))) + size_scale_factor = (size_x < size_y) ? (size_y / size_x) : (size_x / size_y); auto start_aspect_ratio_ws = static_cast(plot_parent->getAttribute("_start_aspect_ratio")); - double default_diag_factor = - ((DEFAULT_ASPECT_RATIO_FOR_SCALING) * - (start_aspect_ratio_ws <= 1 ? start_aspect_ratio_ws : (1.0 / start_aspect_ratio_ws))) * - (plot_diag_factor / diag_factor); + double multi_plot_factor = + grm_max(std::sqrt((figure_viewport[1] - figure_viewport[0]) * (figure_viewport[1] - figure_viewport[0]) + + (figure_viewport[3] - figure_viewport[2]) * (figure_viewport[3] - figure_viewport[2]) * + DEFAULT_ASPECT_RATIO_FOR_SCALING * DEFAULT_ASPECT_RATIO_FOR_SCALING) / + sqrt(5), + figure_viewport[1] - figure_viewport[0]); + + if (figure_vp_element == plot_parent) + { + calculateCentralRegionMarginOrDiagFactor(element, &figure_viewport[0], &figure_viewport[1], + &figure_viewport[2], &figure_viewport[3], true); + double plot_diag_factor = + std::sqrt((figure_viewport[1] - figure_viewport[0]) * (figure_viewport[1] - figure_viewport[0]) + + (figure_viewport[3] - figure_viewport[2]) * (figure_viewport[3] - figure_viewport[2])); + default_diag_factor = + ((DEFAULT_ASPECT_RATIO_FOR_SCALING) * + (start_aspect_ratio_ws <= 1 ? start_aspect_ratio_ws : (1.0 / start_aspect_ratio_ws))) * + (plot_diag_factor / (diag_factor * size_scale_factor)); + } + else + { + figure_viewport[0] = static_cast(plot_parent->getAttribute("plot_x_min")); + figure_viewport[1] = static_cast(plot_parent->getAttribute("plot_x_max")); + figure_viewport[2] = static_cast(plot_parent->getAttribute("plot_y_min")); + figure_viewport[3] = static_cast(plot_parent->getAttribute("plot_y_max")); + calculateCentralRegionMarginOrDiagFactor(element, &figure_viewport[0], &figure_viewport[1], + &figure_viewport[2], &figure_viewport[3], true); + + auto num_col = static_cast(figure_vp_element->parentElement()->getAttribute("num_col")); + auto num_row = static_cast(figure_vp_element->parentElement()->getAttribute("num_row")); + auto start_col = static_cast(figure_vp_element->getAttribute("_start_col")); + auto stop_col = static_cast(figure_vp_element->getAttribute("_stop_col")); + auto start_row = static_cast(figure_vp_element->getAttribute("_start_row")); + auto stop_row = static_cast(figure_vp_element->getAttribute("_stop_row")); + if (figure_vp_element->parentElement()->parentElement() != nullptr && + figure_vp_element->parentElement()->parentElement()->localName() == "layout_grid") + { + num_col = + static_cast(figure_vp_element->parentElement()->parentElement()->getAttribute("num_col")); + num_row = + static_cast(figure_vp_element->parentElement()->parentElement()->getAttribute("num_row")); + } + num_col -= stop_col - start_col - 1; + num_row -= stop_row - start_row - 1; + + double plot_diag_factor = + std::sqrt((figure_viewport[1] - figure_viewport[0]) * (figure_viewport[1] - figure_viewport[0]) + + (figure_viewport[3] - figure_viewport[2]) * (figure_viewport[3] - figure_viewport[2])); + default_diag_factor = + ((DEFAULT_ASPECT_RATIO_FOR_SCALING) * + (start_aspect_ratio_ws <= 1 ? start_aspect_ratio_ws : (1.0 / start_aspect_ratio_ws))) * + (plot_diag_factor / (diag_factor * size_scale_factor)); + + if (num_col < num_row && num_col == 1) multi_plot_factor = 1. / (DEFAULT_ASPECT_RATIO_FOR_SCALING); + // 3d axes need unscaled diag_factor cause scaling is done by gr3 + if (str_equals_any(kind, "wireframe", "surface", "plot3", "scatter3", "trisurface", "volume") && + !element->hasAttribute("diag_factor")) + element->setAttribute("diag_factor", (diag_factor * size_scale_factor) * default_diag_factor * + start_aspect_ratio_ws / DEFAULT_ASPECT_RATIO_FOR_SCALING); + default_diag_factor *= multi_plot_factor; + } element->setAttribute("_default_diag_factor", default_diag_factor); } diag_factor *= static_cast(element->getAttribute("_default_diag_factor")); @@ -5669,7 +6189,9 @@ void GRM::Render::calculateCharHeight(const std::shared_ptr &eleme { char_height *= aspect_ratio_ws; } - if (!element->hasAttribute("_char_height_set_by_user")) char_height *= DEFAULT_ASPECT_RATIO_FOR_SCALING; + if (!element->hasAttribute("_char_height_set_by_user") && + plot_parent->parentElement()->localName() != "layout_grid_element") + char_height *= DEFAULT_ASPECT_RATIO_FOR_SCALING; } plot_parent->setAttribute("char_height", char_height); @@ -5740,9 +6262,9 @@ static void processBackgroundColor(const std::shared_ptr &element) { double vp[4]; double metric_width, metric_height; - double aspect_ratio_ws; std::shared_ptr plot_elem = element; getPlotParent(plot_elem); + if (plot_elem->parentElement()->localName() == "layout_grid_element") plot_elem = plot_elem->parentElement(); vp[0] = static_cast(plot_elem->getAttribute("plot_x_min")); vp[1] = static_cast(plot_elem->getAttribute("plot_x_max")); @@ -5750,7 +6272,20 @@ static void processBackgroundColor(const std::shared_ptr &element) vp[3] = static_cast(plot_elem->getAttribute("plot_y_max")); GRM::Render::getFigureSize(nullptr, nullptr, &metric_width, &metric_height); - aspect_ratio_ws = metric_width / metric_height; + auto aspect_ratio_ws = metric_width / metric_height; + if (plot_elem->parentElement()->localName() == "layout_grid_element") + { + double figure_viewport[4]; + auto figure_vp_element = plot_elem->parentElement(); + figure_viewport[0] = static_cast(figure_vp_element->getAttribute("plot_x_min")); + figure_viewport[1] = static_cast(figure_vp_element->getAttribute("plot_x_max")); + figure_viewport[2] = static_cast(figure_vp_element->getAttribute("plot_y_min")); + figure_viewport[3] = static_cast(figure_vp_element->getAttribute("plot_y_max")); + + metric_width *= (figure_viewport[1] - figure_viewport[0]); + metric_height *= (figure_viewport[3] - figure_viewport[2]); + aspect_ratio_ws = metric_width / metric_height; + } auto background_color_index = static_cast(element->getAttribute("background_color")); gr_savestate(); @@ -5991,6 +6526,8 @@ static void processAxes3d(const std::shared_ptr &element, const st getTickSize(element, tick_size); tick_size *= tick_orientation; applyMoveTransformation(element); + GRM::Render::processWindow(element->parentElement()->parentElement()); + processSpace3d(element->parentElement()->parentElement()); if (redraw_ws) gr_axes3d(x_tick, y_tick, z_tick, x_org, y_org, z_org, x_major, y_major, z_major, tick_size); } @@ -7527,20 +8064,18 @@ static void processIsosurface(const std::shared_ptr &element, float diffuse = (float)static_cast(element->getAttribute("diffuse")); float specular = (float)static_cast(element->getAttribute("specular")); float specular_power = (float)static_cast(element->getAttribute("specular_power")); - float *data = &(conv_data[0]); - float light_parameters[4]; gr3_clear(); - - /* Save and restore original light parameters */ - gr3_getlightparameters(&light_parameters[0], &light_parameters[1], &light_parameters[2], &light_parameters[3]); gr3_setlightparameters(ambient, diffuse, specular, specular_power); + GRM::Render::processWindow(element->parentElement()); + processSpace3d(element->parentElement()); + if (redraw_ws) gr3_isosurface(z_dims_vec[0], z_dims_vec[1], z_dims_vec[2], data, (float)isovalue, foreground_colors, strides); - gr3_setlightparameters(light_parameters[0], light_parameters[1], light_parameters[2], light_parameters[3]); + gr3_setdefaultlightparameters(); } static void processLegend(const std::shared_ptr &element, const std::shared_ptr &context) @@ -7574,6 +8109,7 @@ static void processLegend(const std::shared_ptr &element, const st auto specs_key = static_cast(element->getAttribute("specs")); std::vector specs = GRM::get>((*context)[specs_key]); auto scale_factor = static_cast(element->getAttribute("_scale_factor")); + auto initial_scale_factor = static_cast(element->getAttribute("_initial_scale_factor")); viewport[0] = static_cast(element->getAttribute("viewport_x_min")); viewport[1] = static_cast(element->getAttribute("viewport_x_max")); @@ -7702,6 +8238,9 @@ static void processLegend(const std::shared_ptr &element, const st } if (label_elem != nullptr) { + label_elem->setAttribute("char_height", + static_cast(plot_parent->getAttribute("char_height")) * + initial_scale_factor); gr_savestate(); mask = gr_uselinespec(specs[spec_i].data()); gr_restorestate(); @@ -7912,6 +8451,7 @@ static void processLegend(const std::shared_ptr &element, const st clearOldChildren(&del, element); auto scale_factor = static_cast(element->getAttribute("_scale_factor")); + auto initial_scale_factor = static_cast(element->getAttribute("_initial_scale_factor")); auto h = static_cast(element->getAttribute("_start_h")); viewport[0] = static_cast(element->getAttribute("viewport_x_min")); @@ -7970,6 +8510,9 @@ static void processLegend(const std::shared_ptr &element, const st } if (label_elem != nullptr) { + label_elem->setAttribute("char_height", static_cast(plot_parent->getAttribute("char_height")) * + initial_scale_factor); + processCharHeight(label_elem); render->setTextAlign(label_elem, GKS_K_TEXT_HALIGN_LEFT, GKS_K_TEXT_VALIGN_HALF); if (del != del_values::update_without_default && del != del_values::update_with_default) { @@ -9024,6 +9567,8 @@ static void processGrid3d(const std::shared_ptr &element, const st getAxes3dInformation(element, x_org_pos, y_org_pos, z_org_pos, x_org, y_org, z_org, x_major, y_major, z_major, x_tick, y_tick, z_tick); applyMoveTransformation(element); + GRM::Render::processWindow(element->parentElement()->parentElement()); + processSpace3d(element->parentElement()->parentElement()); if (redraw_ws) gr_grid3d(x_tick, y_tick, z_tick, x_org, y_org, z_org, abs(x_major), abs(y_major), abs(z_major)); } @@ -10027,6 +10572,7 @@ static void processPolyline3d(const std::shared_ptr &element, } else { + processSpace3d(element->parentElement()->parentElement()); if (redraw_ws) gr_polyline3d((int)x_vec.size(), x_p, y_p, z_p); } } @@ -10105,6 +10651,7 @@ static void processPolymarker3d(const std::shared_ptr &element, } else { + processSpace3d(element->parentElement()->parentElement()); if (redraw_ws) gr_polymarker3d((int)x_vec.size(), x_p, y_p, z_p); } } @@ -12832,6 +13379,7 @@ static void processSurface(const std::shared_ptr &element, const s } applyMoveTransformation(element); + processSpace3d(element->parentElement()); if (!accelerate) { double *px_p = &(x_vec[0]); @@ -13765,6 +14313,7 @@ static void processText(const std::shared_ptr &element, const std: scientific_format = static_cast(element->getAttribute("scientific_format")); applyMoveTransformation(element); + processTextEncoding(active_figure); if (space == CoordinateSpace::WC) { gr_wctondc(&x, &y); @@ -13801,6 +14350,7 @@ static void processText(const std::shared_ptr &element, const std: } } + if (element->parentElement()->localName() == "label") processCharHeight(element->parentElement()); if (text_fits && redraw_ws && scientific_format == 2) { gr_settextcolorind(text_color_ind); // needed to have a visible text after update @@ -14498,6 +15048,7 @@ static void processTriSurface(const std::shared_ptr &element, double *py_p = &(py_vec[0]); double *pz_p = &(pz_vec[0]); applyMoveTransformation(element); + processSpace3d(element->parentElement()); if (redraw_ws) gr_trisurface(nx, px_p, py_p, pz_p); } @@ -14581,6 +15132,7 @@ static void processVolume(const std::shared_ptr &element, const st if (element->hasAttribute("d_min")) d_min = static_cast(element->getAttribute("d_min")); if (element->hasAttribute("d_max")) d_max = static_cast(element->getAttribute("d_max")); + processSpace3d(element->parentElement()); if (redraw_ws) { gr_inqvpsize(&width, &height, &device_pixel_ratio); @@ -14676,6 +15228,8 @@ static void processWireframe(const std::shared_ptr &element, const double *py_p = &(y_vec[0]); double *pz_p = &(z_vec[0]); applyMoveTransformation(element); + processSpace3d(element->parentElement()); + if (redraw_ws) gr_surface((int)x_length, (int)y_length, px_p, py_p, pz_p, GR_OPTION_FILLED_MESH); } @@ -16127,7 +16681,11 @@ static void processElement(const std::shared_ptr &element, const s if (element->localName() == "figure") { if (!static_cast(element->getAttribute("active"))) return; - if (global_root->querySelectorsAll("draw_graphics").empty()) plotProcessWsWindowWsViewport(element, context); + if (element->hasAttribute("size_x") && !element->hasAttribute("_initial_width")) + element->setAttribute("_initial_width", static_cast(element->getAttribute("size_x"))); + if (element->hasAttribute("size_y") && !element->hasAttribute("_initial_height")) + element->setAttribute("_initial_height", static_cast(element->getAttribute("size_y"))); + if (element->querySelectorsAll("draw_graphics").empty()) plotProcessWsWindowWsViewport(element, context); } if (element->localName() == "plot") { @@ -16390,6 +16948,8 @@ static void initializeGridElements(const std::shared_ptr &element, { for (const auto &child : element->children()) { + std::string prefix = ""; + int row_start, row_stop, col_start, col_stop; if (child->localName() != "layout_grid_element" && child->localName() != "layout_grid") return; auto abs_height = (child->hasAttribute("absolute_height")) @@ -16414,10 +16974,11 @@ static void initializeGridElements(const std::shared_ptr &element, (child->hasAttribute("aspect_ratio")) ? static_cast(child->getAttribute("aspect_ratio")) : -1.0; auto fit_parents_height = static_cast(child->getAttribute("fit_parents_height")); auto fit_parents_width = static_cast(child->getAttribute("fit_parents_width")); - auto row_start = static_cast(child->getAttribute("start_row")); - auto row_stop = static_cast(child->getAttribute("stop_row")); - auto col_start = static_cast(child->getAttribute("start_col")); - auto col_stop = static_cast(child->getAttribute("stop_col")); + if (child->localName() != "layout_grid") prefix = "_"; + row_start = static_cast(child->getAttribute(prefix + "start_row")); + row_stop = static_cast(child->getAttribute(prefix + "stop_row")); + col_start = static_cast(child->getAttribute(prefix + "start_col")); + col_stop = static_cast(child->getAttribute(prefix + "stop_col")); auto *slice = new grm::Slice(row_start, row_stop, col_start, col_stop); if (child->localName() == "layout_grid_element") @@ -16725,6 +17286,7 @@ void GRM::Render::render() finalizeGrid(active_figure); renderHelper(root, this->context); renderZQueue(this->context); + if (active_figure->hasAttribute("_kind_changed")) active_figure->removeAttribute("_kind_changed"); root->setAttribute("_modified", false); // reset the modified flag, cause all updates are made if (root->hasAttribute("_update_ws") && static_cast(root->getAttribute("_update_ws"))) gr_updatews(); if (bounding_boxes) @@ -16756,6 +17318,7 @@ void GRM::Render::render() void GRM::Render::process_tree() { global_root->setAttribute("_modified", true); + finalizeGrid(active_figure); renderHelper(global_root, this->context); renderZQueue(this->context); global_root->setAttribute("_modified", false); // reset the modified flag, cause all updates are made @@ -16850,6 +17413,8 @@ std::vector GRM::Render::getDefaultAndTooltip(const std::shared_ptr {std::string("clip_region"), std::vector{ "0", "Defines whether a quadratic(0) or elliptic(1) clip region will be used to clip the displayed plot"}}, + {std::string("col_span"), + std::vector{"None", "Define the number of columns the grid element contains"}}, {std::string("color_ind"), std::vector{"None", "The index of the used color"}}, {std::string("color_ind_values"), std::vector{"None", "References the color-values stored in the context in index format"}}, @@ -16882,8 +17447,6 @@ std::vector GRM::Render::getDefaultAndTooltip(const std::shared_ptr {std::string("error_bar_x"), std::vector{"None", "The x-value for the error"}}, {std::string("error_bar_y_max"), std::vector{"None", "The ending y-value for the error"}}, {std::string("error_bar_y_min"), std::vector{"None", "The beginning y-value for the error"}}, - {std::string("fig_size_x"), std::vector{"None", "The figure x-size"}}, - {std::string("fig_size_y"), std::vector{"None", "The figure y-size"}}, {std::string("fill_color_ind"), std::vector{"None", "Sets the index of the current fill color"}}, {std::string("fill_color_rgb"), std::vector{"None", "Color for the bars in rgb format"}}, {std::string("fill_int_style"), std::vector{"None", "Sets the index of the current fill style"}}, @@ -16892,6 +17455,8 @@ std::vector GRM::Render::getDefaultAndTooltip(const std::shared_ptr std::vector{"None", "Toggle if the parent grid should match the element`s height"}}, {std::string("fit_parents_width"), std::vector{"None", "Toggle if the parent grid should match the element`s width"}}, + {std::string("flip_col_and_row"), + std::vector{"False", "Define if the rows and cols inside the layout should be flipped"}}, {std::string("font"), std::vector{"computermodern", "The used text font"}}, {std::string("font_precision"), std::vector{"precision_outline", "The precision of the text font"}}, {std::string("grplot"), std::vector{"0", "Sets if GRPlot is used or not"}}, @@ -16911,6 +17476,8 @@ std::vector GRM::Render::getDefaultAndTooltip(const std::shared_ptr {std::string("keep_aspect_ratio"), std::vector{"1", "Sets if the aspect ratio is kept"}}, {std::string("keep_radii_axes"), std::vector{"False", "Defines if the radii-axes respect the ranges defined by y_lim_max/min"}}, + {std::string("keep_size_if_swapped"), + std::vector{"True", "If the position of this layout grid element gets changed it's size is kept"}}, {std::string("keep_window"), std::vector{"1", "Sets if the window will be inflicted by attribute changes"}}, {std::string("kind"), @@ -16982,6 +17549,9 @@ std::vector GRM::Render::getDefaultAndTooltip(const std::shared_ptr {std::string("pos"), std::vector{ "None", "F.e. where the x-axis should be placed in relation to the y-axis (position on the y-axis)"}}, + {std::string("position"), + std::vector{"None", "Defines the position of a grid_layout-element. The first numbers defines the " + "row the second the column. Between both numbers must stand a space."}}, {std::string("projection_type"), std::vector{"None", "The used projection type"}}, {std::string("px"), std::vector{"None", "References the px-values stored in the context. The " "px-values are the modified version of the x-values"}}, @@ -17010,6 +17580,8 @@ std::vector GRM::Render::getDefaultAndTooltip(const std::shared_ptr {std::string("relative_upwards_flt"), std::vector{"None", "The flat relative upward error. It gets applied at all error points"}}, {std::string("resample_method"), std::vector{"None", "The used resample method"}}, + {std::string("row_span"), + std::vector{"None", "Define the number of rows the grid element contains"}}, {std::string("scale"), std::vector{"None", "The set scale"}}, {std::string("scientific_format"), std::vector{"None", "Set the used format which will determine how a specific text will be drawn. " @@ -17067,6 +17639,12 @@ std::vector GRM::Render::getDefaultAndTooltip(const std::shared_ptr {std::string("total"), std::vector{"None", "The total-value of the bins"}}, {std::string("transformation"), std::vector{"5", "The used transformation"}}, {std::string("transparency"), std::vector{"None", "Sets the transparency value"}}, + {std::string("trim_col"), + std::vector{"False", "Define if the program should remove empty cells columnwise inside the layout " + "grid if this reduces the number of columns"}}, + {std::string("trim_row"), + std::vector{"False", "Define if the program should remove empty cells rowwise inside the layout " + "grid if this reduces the number of rows"}}, {std::string("u"), std::vector{"None", "References the u-values stored in the context"}}, {std::string("upwards_cap_color"), std::vector{"None", "The color value for the upwards caps"}}, {std::string("v"), std::vector{"None", "References the v-values stored in the context"}}, @@ -18128,15 +18706,6 @@ std::shared_ptr GRM::Render::createDrawGraphics(const std::string return element; } -std::shared_ptr GRM::Render::createIsoSurfaceRenderElement(int drawable_type) -{ - auto element = createElement("isosurface_render"); - - element->setAttribute("drawable_type", drawable_type); - - return element; -} - std::shared_ptr GRM::Render::createLayoutGrid(const grm::Grid &grid) { auto element = createElement("layout_grid"); @@ -18170,10 +18739,14 @@ std::shared_ptr GRM::Render::createLayoutGridElement(const grm::Gr if (grid_element.relative_height != -1) element->setAttribute("relative_height", grid_element.relative_height); if (grid_element.relative_width != -1) element->setAttribute("relative_width", grid_element.relative_width); if (grid_element.aspect_ratio != -1) element->setAttribute("aspect_ratio", grid_element.aspect_ratio); - element->setAttribute("start_row", slice.row_start); - element->setAttribute("stop_row", slice.row_stop); - element->setAttribute("start_col", slice.col_start); - element->setAttribute("stop_col", slice.col_stop); + element->setAttribute("_start_row", slice.row_start); + element->setAttribute("_stop_row", slice.row_stop); + element->setAttribute("_start_col", slice.col_start); + element->setAttribute("_stop_col", slice.col_stop); + element->setAttribute("row_span", slice.row_stop - slice.row_start); + element->setAttribute("col_span", slice.col_stop - slice.col_start); + element->setAttribute("keep_size_if_swapped", true); + element->setAttribute("position", std::to_string(slice.row_start) + " " + std::to_string(slice.col_start)); double *plot = grid_element.plot; GRM::Render::setPlot(element, plot[0], plot[1], plot[2], plot[3]); @@ -19185,7 +19758,7 @@ void updateFilter(const std::shared_ptr &element, const std::strin element->remove(); new_series->setAttribute("_update_required", true); new_series->setAttribute("_delete_children", 2); - auto legend = global_render->querySelectors("legend"); + auto legend = plot_parent->querySelectors("legend"); if (legend != nullptr) { legend->setAttribute("_delete_children", 2); @@ -19492,12 +20065,15 @@ void updateFilter(const std::shared_ptr &element, const std::strin // f.e. surface plots are smaller than heatmap plots so the diag_factor isn't the same if (old_kind != new_kind) { - for (const auto &elem : global_root->querySelectorsAll("[_default_diag_factor]")) + for (const auto &elem : plot_parent->querySelectorsAll("[_default_diag_factor]")) { elem->removeAttribute("_default_diag_factor"); } + if (plot_parent->parentElement()->localName() == "layout_grid_element") + plot_parent->removeAttribute("_default_diag_factor"); if (new_type == "3d" && !central_region->hasAttribute("_diag_factor_set_by_user")) central_region->removeAttribute("diag_factor"); + active_figure->setAttribute("_kind_changed", true); } if (coordinate_system) @@ -19935,6 +20511,7 @@ void updateFilter(const std::shared_ptr &element, const std::strin { // when the ranges gets reseted the bounding boxes of the series can be wrong, to solve this problem // they get calculated again out of their children + if (str_equals_any(attr, "keep_aspect_ratio", "only_quadratic_aspect_ratio")) gr_clearws(); if (attr == "reset_ranges") { for (const auto &child : element->children()) @@ -20516,6 +21093,643 @@ void updateFilter(const std::shared_ptr &element, const std::strin resetOldBoundingBoxes(element); removeBoundingBoxId(*element); } + else if (element->localName() == "layout_grid_element") + { + if (attr == "col_span") + { + auto new_num = static_cast(element->getAttribute(attr)); + auto diff = new_num - std::stoi(value); + auto stop_col = static_cast(element->getAttribute("_stop_col")); + auto start_col = static_cast(element->getAttribute("_start_col")); + auto start_row = static_cast(element->getAttribute("_start_row")); + element->setAttribute("_stop_col", stop_col + diff); + + auto num_col = static_cast(element->parentElement()->getAttribute("num_col")); + auto max = stop_col + diff; + + for (const auto &grid_element : element->parentElement()->children()) + { + if (grid_element != element) + { + auto grid_stop_col = static_cast(grid_element->getAttribute("_stop_col")); + auto grid_start_col = static_cast(grid_element->getAttribute("_start_col")); + auto grid_start_row = static_cast(grid_element->getAttribute("_start_row")); + if (grid_start_col > start_col && grid_start_row >= start_row) + { + grid_element->setAttribute("_stop_col", grid_stop_col + diff); + grid_element->setAttribute("_start_col", grid_start_col + diff); + grid_element->setAttribute("position", std::to_string(grid_start_row) + " " + + std::to_string(grid_start_col + diff)); + max = grm_max(max, grid_stop_col + diff); + } + } + } + element->parentElement()->setAttribute("num_col", grm_max(num_col, max)); + } + else if (attr == "row_span") + { + auto new_num = static_cast(element->getAttribute(attr)); + auto diff = new_num - std::stoi(value); + auto stop_row = static_cast(element->getAttribute("_stop_row")); + auto start_row = static_cast(element->getAttribute("_start_row")); + auto start_col = static_cast(element->getAttribute("_start_col")); + element->setAttribute("_stop_row", stop_row + diff); + + auto num_row = static_cast(element->parentElement()->getAttribute("num_row")); + auto max = stop_row + diff; + + for (const auto &grid_element : element->parentElement()->children()) + { + if (grid_element != element) + { + auto grid_stop_row = static_cast(grid_element->getAttribute("_stop_row")); + auto grid_start_row = static_cast(grid_element->getAttribute("_start_row")); + auto grid_start_col = static_cast(grid_element->getAttribute("_start_col")); + if (grid_start_row > start_row && grid_start_col >= start_col) + { + grid_element->setAttribute("_start_row", grid_start_row + diff); + grid_element->setAttribute("_stop_row", grid_stop_row + diff); + grid_element->setAttribute("position", std::to_string(grid_start_row + diff) + " " + + std::to_string(grid_start_col)); + max = grm_max(max, grid_stop_row + diff); + } + } + } + element->parentElement()->setAttribute("num_row", grm_max(num_row, stop_row + diff)); + } + else if (attr == "position") + { + auto new_pos = static_cast(element->getAttribute("position")); + if (element->parentElement() + ->querySelectorsAll("layout_grid_element[position=\"" + new_pos + "\"]") + .size() > 1) + { + for (const auto &grid_element : element->parentElement()->querySelectorsAll( + "layout_grid_element[position=\"" + new_pos + "\"]")) + { + if (grid_element != element) + { + auto elem_start_row = static_cast(element->getAttribute("_start_row")); + auto elem_stop_row = static_cast(element->getAttribute("_stop_row")); + auto elem_start_col = static_cast(element->getAttribute("_start_col")); + auto elem_stop_col = static_cast(element->getAttribute("_stop_col")); + auto elem_col_span = static_cast(element->getAttribute("col_span")); + auto elem_row_span = static_cast(element->getAttribute("row_span")); + auto grid_start_row = static_cast(grid_element->getAttribute("_start_row")); + auto grid_stop_row = static_cast(grid_element->getAttribute("_stop_row")); + auto grid_start_col = static_cast(grid_element->getAttribute("_start_col")); + auto grid_stop_col = static_cast(grid_element->getAttribute("_stop_col")); + auto grid_col_span = static_cast(grid_element->getAttribute("col_span")); + auto grid_row_span = static_cast(grid_element->getAttribute("row_span")); + + element->setAttribute("_start_row", grid_start_row); + element->setAttribute("_start_col", grid_start_col); + grid_element->setAttribute("_start_row", elem_start_row); + grid_element->setAttribute("_start_col", elem_start_col); + + if (element->hasAttribute("keep_size_if_swapped") && + static_cast(element->getAttribute("keep_size_if_swapped"))) + { + element->setAttribute("_stop_row", grid_start_row + elem_row_span); + element->setAttribute("_stop_col", grid_start_col + elem_col_span); + grid_element->setAttribute("_stop_row", elem_start_row + grid_row_span); + grid_element->setAttribute("_stop_col", elem_start_col + grid_col_span); + + auto max_col = + grm_max(grid_start_col + elem_col_span, elem_start_col + grid_col_span); + auto max_row = + grm_max(grid_start_row + elem_row_span, elem_start_row + grid_row_span); + for (const auto &elem : element->parentElement()->children()) + { + if (elem == grid_element || elem == element) continue; + auto stop_row = static_cast(elem->getAttribute("_stop_row")); + auto start_row = static_cast(elem->getAttribute("_start_row")); + auto start_col = static_cast(elem->getAttribute("_start_col")); + auto stop_col = static_cast(elem->getAttribute("_stop_col")); + if (start_row > elem_start_row && start_col >= elem_start_col) + { + elem->setAttribute("_start_row", start_row + (start_row - elem_start_row)); + elem->setAttribute("_stop_row", stop_row + (start_row - elem_start_row)); + elem->setAttribute("position", + std::to_string(start_row + (start_row - elem_start_row)) + + " " + std::to_string(start_col)); + max_row = grm_max(max_row, stop_row + (start_row - elem_start_row)); + } + if (start_col > elem_start_col && start_row >= elem_start_row) + { + elem->setAttribute("_stop_col", stop_col + (start_col - elem_start_col)); + elem->setAttribute("_start_col", start_col + (start_col - elem_start_col)); + elem->setAttribute( + "position", std::to_string(start_row) + " " + + std::to_string(start_col + (start_col - elem_start_col))); + max_col = grm_max(max_col, stop_col + (start_col - elem_start_col)); + } + if (start_row > grid_start_row && start_col >= grid_start_col) + { + elem->setAttribute("_start_row", start_row + (start_row - grid_start_row)); + elem->setAttribute("_stop_row", stop_row + (start_row - grid_start_row)); + elem->setAttribute("position", + std::to_string(start_row + (start_row - grid_start_row)) + + " " + std::to_string(start_col)); + max_row = grm_max(max_row, stop_row + (start_row - grid_start_row)); + } + if (start_col > grid_start_col && start_row >= grid_start_row) + { + elem->setAttribute("_stop_col", stop_col + (start_col - grid_start_col)); + elem->setAttribute("_start_col", start_col + (start_col - grid_start_col)); + elem->setAttribute( + "position", std::to_string(start_row) + " " + + std::to_string(start_col + (start_col - grid_start_col))); + max_col = grm_max(max_col, stop_col + (start_col - grid_start_col)); + } + } + + auto num_col = static_cast(element->parentElement()->getAttribute("num_col")); + auto num_row = static_cast(element->parentElement()->getAttribute("num_row")); + element->parentElement()->setAttribute("num_col", grm_max(num_col, max_col)); + element->parentElement()->setAttribute("num_row", grm_max(num_row, max_row)); + } + else + { + element->setAttribute("_stop_row", grid_stop_row); + element->setAttribute("_stop_col", grid_stop_col); + element->setAttribute("col_span", grid_col_span); + element->setAttribute("row_span", grid_row_span); + grid_element->setAttribute("_stop_row", elem_stop_row); + grid_element->setAttribute("_stop_col", elem_stop_col); + grid_element->setAttribute("col_span", elem_col_span); + grid_element->setAttribute("row_span", elem_row_span); + } + + grid_element->setAttribute("position", value); + break; + } + } + } + else + { + auto new_row = std::stoi(GRM::split(new_pos, " ")[0]); + auto new_col = std::stoi(GRM::split(new_pos, " ")[1]); + auto num_col = static_cast(element->parentElement()->getAttribute("num_col")); + auto num_row = static_cast(element->parentElement()->getAttribute("num_row")); + auto col_span = static_cast(element->getAttribute("col_span")); + auto row_span = static_cast(element->getAttribute("row_span")); + + // check if number of rows or cols must be adjusted + if (new_row + row_span > num_row) + element->parentElement()->setAttribute("num_row", new_row + row_span); + if (new_col + col_span > num_col) + element->parentElement()->setAttribute("num_col", new_col + col_span); + + element->setAttribute("_start_row", new_row); + element->setAttribute("_start_col", new_col); + element->setAttribute("_stop_row", new_row + row_span); + element->setAttribute("_stop_col", new_col + col_span); + } + } + if (element->parentElement()->hasAttribute("trim_col") && + static_cast(element->parentElement()->getAttribute("trim_col"))) + { + const auto num_col = static_cast(element->parentElement()->getAttribute("num_col")); + const auto num_row = static_cast(element->parentElement()->getAttribute("num_row")); + std::vector> grid_matrix; + // min num of empty space which all cols/rows contains + int min_empty_col_num = num_col; + + // initialize grid_matrix with layout grid + for (int i = 0; i < num_row; i++) + { + std::vector row; + for (int j = 0; j < num_col; j++) + { + row.push_back(-1); + } + grid_matrix.push_back(row); + } + for (const auto &grid_elem : element->parentElement()->children()) + { + auto start_col = static_cast(grid_elem->getAttribute("_start_col")); + auto stop_col = static_cast(grid_elem->getAttribute("_stop_col")); + auto start_row = static_cast(grid_elem->getAttribute("_start_row")); + auto stop_row = static_cast(grid_elem->getAttribute("_stop_row")); + auto pos = static_cast(grid_elem->getAttribute("position")); + + for (int i = start_row; i < stop_row; i++) + { + for (int j = start_col; j < stop_col; j++) + { + grid_matrix[i][j] = 1; + } + } + } + + // calculate amount of columns which can be removed in each row + for (int i = 0; i < num_row; i++) + { + int cnt = 0; + for (int j = 0; j < num_col; j++) + { + if (grid_matrix[i][j] == -1) cnt += 1; + } + min_empty_col_num = grm_min(min_empty_col_num, cnt); + } + + // remove min num of empty grid elements in each row + /* a bit tricky is "a x x b c" with min_empty_col = 1, cause b and c must be moved 1 to the + left and then num_col on the parent element has to be changed to remove to useless col */ + if (min_empty_col_num > 0) + { + for (int i = 0; i < num_row; i++) + { + int removed_spaces = 0; + for (int j = 0; j < num_col; j++) + { + if (grid_matrix[i][j] == -1 && removed_spaces < min_empty_col_num) + { + removed_spaces += 1; + } + else if (removed_spaces != 0 && grid_matrix[i][j] != -1) + { + auto grid_elem = element->parentElement()->querySelectors( + "layout_grid_element[position=\"" + std::to_string(i) + " " + std::to_string(j) + + "\"]"); + if (grid_elem != nullptr) + { + auto start_col = static_cast(grid_elem->getAttribute("_start_col")); + auto stop_col = static_cast(grid_elem->getAttribute("_stop_col")); + auto start_row = static_cast(grid_elem->getAttribute("_start_row")); + + grid_elem->setAttribute("_start_col", start_col - removed_spaces); + grid_elem->setAttribute("_stop_col", stop_col - removed_spaces); + grid_elem->setAttribute("position", + std::to_string(start_row) + " " + + std::to_string(start_col - removed_spaces)); + } + } + } + } + automatic_update = false; + element->parentElement()->setAttribute("num_col", num_col - min_empty_col_num); + automatic_update = true; + } + } + if (element->parentElement()->hasAttribute("trim_row") && + static_cast(element->parentElement()->getAttribute("trim_row"))) + { + const auto num_col = static_cast(element->parentElement()->getAttribute("num_col")); + const auto num_row = static_cast(element->parentElement()->getAttribute("num_row")); + std::vector> grid_matrix; + // min num of empty space which all cols/rows contains + int min_empty_row_num = num_row; + + // initialize grid_matrix with layout grid + for (int i = 0; i < num_row; i++) + { + std::vector row; + for (int j = 0; j < num_col; j++) + { + row.push_back(-1); + } + grid_matrix.push_back(row); + } + for (const auto &grid_elem : element->parentElement()->children()) + { + auto start_col = static_cast(grid_elem->getAttribute("_start_col")); + auto stop_col = static_cast(grid_elem->getAttribute("_stop_col")); + auto start_row = static_cast(grid_elem->getAttribute("_start_row")); + auto stop_row = static_cast(grid_elem->getAttribute("_stop_row")); + auto pos = static_cast(grid_elem->getAttribute("position")); + + for (int i = start_row; i < stop_row; i++) + { + for (int j = start_col; j < stop_col; j++) + { + grid_matrix[i][j] = 1; + } + } + } + + // calculate amount of rows which can be removed in each col + for (int j = 0; j < num_col; j++) + { + int cnt = 0; + for (int i = 0; i < num_row; i++) + { + if (grid_matrix[i][j] == -1) cnt += 1; + } + min_empty_row_num = grm_min(min_empty_row_num, cnt); + } + + // remove min num of empty grid elements in each col + if (min_empty_row_num > 0) + { + for (int j = 0; j < num_col; j++) + { + int removed_spaces = 0; + for (int i = 0; i < num_row; i++) + { + if (grid_matrix[i][j] == -1 && removed_spaces < min_empty_row_num) + { + removed_spaces += 1; + } + else if (removed_spaces != 0 && grid_matrix[i][j] != -1) + { + auto grid_elem = element->parentElement()->querySelectors( + "layout_grid_element[position=\"" + std::to_string(i) + " " + std::to_string(j) + + "\"]"); + if (grid_elem != nullptr) + { + auto start_row = static_cast(grid_elem->getAttribute("_start_row")); + auto stop_row = static_cast(grid_elem->getAttribute("_stop_row")); + auto start_col = static_cast(grid_elem->getAttribute("_start_col")); + + grid_elem->setAttribute("_start_row", start_row - removed_spaces); + grid_elem->setAttribute("_stop_row", stop_row - removed_spaces); + grid_elem->setAttribute("position", std::to_string(start_row - removed_spaces) + + " " + std::to_string(start_col)); + } + } + } + } + automatic_update = false; + element->parentElement()->setAttribute("num_row", num_row - min_empty_row_num); + automatic_update = true; + } + } + if (str_equals_any(attr, "fit_parents_height", "fit_parents_width", "col_span", "row_span", "position")) + { + for (const auto &grid_element : element->parentElement()->children()) + { + auto plot_x_min = static_cast(grid_element->getAttribute("plot_x_min")); + auto plot_x_max = static_cast(grid_element->getAttribute("plot_x_max")); + auto plot_y_min = static_cast(grid_element->getAttribute("plot_y_min")); + auto plot_y_max = static_cast(grid_element->getAttribute("plot_y_max")); + + grid_element->setAttribute("plot_x_min", plot_y_min); + grid_element->setAttribute("plot_x_max", plot_y_max); + grid_element->setAttribute("plot_y_min", plot_x_min); + grid_element->setAttribute("plot_y_max", plot_x_max); + grid_element->querySelectors("plot")->removeAttribute("_start_aspect_ratio"); + for (const auto &elem : grid_element->querySelectorsAll("[_default_diag_factor]")) + { + elem->removeAttribute("_default_diag_factor"); + resetOldBoundingBoxes(elem); + } + resetOldBoundingBoxes(grid_element); + } + + // imshow needs vp which gets inflicted by the size attributes in figure, this means when the size is + // changed the imshow_series must be recalculated + auto imshow = global_render->querySelectorsAll("series_imshow"); + for (const auto &imshow_elem : imshow) + { + imshow_elem->setAttribute("_update_required", true); + } + } + } + else if (element->localName() == "layout_grid") + { + if (attr == "flip_col_and_row" && static_cast(element->getAttribute(attr))) + { + auto num_col = static_cast(element->getAttribute("num_col")); + auto num_row = static_cast(element->getAttribute("num_row")); + auto fit_parents_height = static_cast(element->getAttribute("fit_parents_height")); + auto fit_parents_width = static_cast(element->getAttribute("fit_parents_width")); + element->setAttribute("num_col", num_row); + element->setAttribute("num_row", num_col); + element->setAttribute("fit_parents_height", fit_parents_width); + element->setAttribute("fit_parents_width", fit_parents_height); + + for (const auto &grid_element : element->children()) + { + auto legend = grid_element->querySelectors("legend"); + auto start_col = static_cast(grid_element->getAttribute("_start_col")); + auto stop_col = static_cast(grid_element->getAttribute("_stop_col")); + auto start_row = static_cast(grid_element->getAttribute("_start_row")); + auto stop_row = static_cast(grid_element->getAttribute("_stop_row")); + auto plot_x_min = static_cast(grid_element->getAttribute("plot_x_min")); + auto plot_x_max = static_cast(grid_element->getAttribute("plot_x_max")); + auto plot_y_min = static_cast(grid_element->getAttribute("plot_y_min")); + auto plot_y_max = static_cast(grid_element->getAttribute("plot_y_max")); + fit_parents_height = static_cast(grid_element->getAttribute("fit_parents_height")); + fit_parents_width = static_cast(grid_element->getAttribute("fit_parents_width")); + + grid_element->setAttribute("_start_col", start_row); + grid_element->setAttribute("_stop_col", stop_row); + grid_element->setAttribute("_start_row", start_col); + grid_element->setAttribute("_stop_row", stop_col); + grid_element->setAttribute("plot_x_min", plot_y_min); + grid_element->setAttribute("plot_x_max", plot_y_max); + grid_element->setAttribute("plot_y_min", plot_x_min); + grid_element->setAttribute("plot_y_max", plot_x_max); + grid_element->setAttribute("fit_parents_height", fit_parents_width); + grid_element->setAttribute("fit_parents_width", fit_parents_height); + + grid_element->querySelectors("plot")->removeAttribute("_start_aspect_ratio"); + for (const auto &elem : grid_element->querySelectorsAll("[_default_diag_factor]")) + { + elem->removeAttribute("_default_diag_factor"); + } + if (legend != nullptr) + { + legend->removeAttribute("_initial_scale_factor"); + legend->removeAttribute("_scale_factor"); + legend->removeAttribute("_start_w"); + legend->removeAttribute("_start_h"); + } + resetOldBoundingBoxes(grid_element); + } + + element->setAttribute("flip_col_and_row", false); + } + else if (str_equals_any(attr, "num_col", "num_row")) + { + for (const auto &grid_element : element->children()) + { + auto legend = grid_element->querySelectors("legend"); + grid_element->querySelectors("plot")->removeAttribute("_start_aspect_ratio"); + for (const auto &elem : grid_element->querySelectorsAll("[_default_diag_factor]")) + { + elem->removeAttribute("_default_diag_factor"); + } + if (legend != nullptr) + { + legend->removeAttribute("_initial_scale_factor"); + legend->removeAttribute("_scale_factor"); + legend->removeAttribute("_start_w"); + legend->removeAttribute("_start_h"); + } + resetOldBoundingBoxes(grid_element); + } + } + else if (str_equals_any(attr, "start_col", "start_col", "stop_col", "stop_row")) + { + if (str_equals_any(attr, "stop_col", "stop_row")) + { + auto num_col = static_cast(element->getAttribute("num_col")); + auto num_row = static_cast(element->getAttribute("num_row")); + auto end_val = static_cast(element->getAttribute(attr)); + if (attr == "stop_col") + element->setAttribute("num_col", grm_max(num_col, end_val)); + else + element->setAttribute("num_row", grm_max(num_row, end_val)); + } + else if (str_equals_any(attr, "start_col", "start_row")) + { + auto start_val = static_cast(element->getAttribute(attr)); + if (attr == "start_col") + element->setAttribute("col_span", + static_cast(element->getAttribute("stop_col")) - start_val); + else + element->setAttribute("row_span", + static_cast(element->getAttribute("stop_row")) - start_val); + } + + for (const auto &grid_element : element->parentElement()->children()) + { + grid_element->querySelectors("plot")->removeAttribute("_start_aspect_ratio"); + for (const auto &elem : grid_element->querySelectorsAll("[_default_diag_factor]")) + { + elem->removeAttribute("_default_diag_factor"); + resetOldBoundingBoxes(elem); + } + resetOldBoundingBoxes(grid_element); + } + } + else if (attr == "trim_row" || attr == "trim_col") + { + const auto num_col = static_cast(element->getAttribute("num_col")); + const auto num_row = static_cast(element->getAttribute("num_row")); + std::vector> grid_matrix; + // min num of empty space which all cols/rows contains + int min_empty_num = attr == "trim_row" ? num_row : num_col; + + // initialize grid_matrix with layout grid + for (int i = 0; i < num_row; i++) + { + std::vector row; + for (int j = 0; j < num_col; j++) + { + row.push_back(-1); + } + grid_matrix.push_back(row); + } + for (const auto &grid_elem : element->children()) + { + auto start_col = static_cast(grid_elem->getAttribute("_start_col")); + auto stop_col = static_cast(grid_elem->getAttribute("_stop_col")); + auto start_row = static_cast(grid_elem->getAttribute("_start_row")); + auto stop_row = static_cast(grid_elem->getAttribute("_stop_row")); + + for (int i = start_row; i < stop_row; i++) + { + for (int j = start_col; j < stop_col; j++) + { + grid_matrix[i][j] = 1; + } + } + } + + // calculate amount of rows/cols which can be removed in each col/row + for (int j = 0; j < (attr == "trim_row" ? num_col : num_row); j++) + { + int cnt = 0; + for (int i = 0; i < (attr == "trim_row" ? num_row : num_col); i++) + { + if ((attr == "trim_row" && grid_matrix[i][j] == -1) || + (attr == "trim_col" && grid_matrix[j][i] == -1)) + cnt += 1; + } + min_empty_num = grm_min(min_empty_num, cnt); + } + + // remove min num of empty grid elements in each col/row + if (min_empty_num > 0) + { + for (int j = 0; j < (attr == "trim_row" ? num_col : num_row); j++) + { + int removed_spaces = 0; + for (int i = 0; i < (attr == "trim_row" ? num_row : num_col); i++) + { + int matrix_entry; + if (attr == "trim_row") + matrix_entry = grid_matrix[i][j]; + else + matrix_entry = grid_matrix[j][i]; + if (matrix_entry == -1 && removed_spaces < min_empty_num) + { + removed_spaces += 1; + } + else if (removed_spaces != 0 && matrix_entry != -1) + { + std::shared_ptr grid_elem; + if (attr == "trim_row") + { + grid_elem = + element->querySelectors("layout_grid_element[position=\"" + + std::to_string(i) + " " + std::to_string(j) + "\"]"); + } + else + { + grid_elem = + element->querySelectors("layout_grid_element[position=\"" + + std::to_string(j) + " " + std::to_string(i) + "\"]"); + } + if (grid_elem != nullptr) + { + auto start_row = static_cast(grid_elem->getAttribute("_start_row")); + auto stop_row = static_cast(grid_elem->getAttribute("_stop_row")); + auto start_col = static_cast(grid_elem->getAttribute("_start_col")); + auto stop_col = static_cast(grid_elem->getAttribute("_stop_col")); + + if (attr == "trim_row") + { + grid_elem->setAttribute("_start_row", start_row - removed_spaces); + grid_elem->setAttribute("_stop_row", stop_row - removed_spaces); + grid_elem->setAttribute("position", + std::to_string(start_row - removed_spaces) + " " + + std::to_string(start_col)); + } + else + { + grid_elem->setAttribute("_start_col", start_col - removed_spaces); + grid_elem->setAttribute("_stop_col", stop_col - removed_spaces); + grid_elem->setAttribute("position", + std::to_string(start_row) + " " + + std::to_string(start_col - removed_spaces)); + } + } + } + } + } + automatic_update = false; + if (attr == "trim_row") + element->setAttribute("num_row", num_row - min_empty_num); + else + element->setAttribute("num_col", num_col - min_empty_num); + automatic_update = true; + + for (const auto &grid_element : element->children()) + { + grid_element->querySelectors("plot")->removeAttribute("_start_aspect_ratio"); + for (const auto &elem : grid_element->querySelectorsAll("[_default_diag_factor]")) + { + elem->removeAttribute("_default_diag_factor"); + resetOldBoundingBoxes(elem); + } + resetOldBoundingBoxes(grid_element); + } + } + } + + // imshow needs vp which gets inflicted by the size attributes in figure, this means when the size is + // changed the imshow_series must be recalculated + auto imshow = global_render->querySelectorsAll("series_imshow"); + for (const auto &imshow_elem : imshow) + { + imshow_elem->setAttribute("_update_required", true); + } + } } global_root->setAttribute("_modified", true); diff --git a/lib/grm/src/grm/import.cxx b/lib/grm/src/grm/import.cxx index 42fd49e24..38ab4aa7a 100644 --- a/lib/grm/src/grm/import.cxx +++ b/lib/grm/src/grm/import.cxx @@ -582,615 +582,355 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) */ { std::string s; - size_t row, col, rows, cols, depth; + size_t row, col, rows, cols, depth, plot_num; std::vector>> file_data; - std::vector x_data, y_data, error_data; + std::vector x_data, y_data, error_data, plot_idx; std::list bottom_series, left_series, right_series, top_series, twin_x_series, twin_y_series; - std::vector labels; - std::vector labels_c; std::vector series; char *env, *wstype; void *handle = nullptr; const char *kind; - int grplot; + int grplot, divisor = 1; grm_file_args_t *file_args = grm_file_args_new(); grm_special_axis_series_t *special_axis_series = grm_special_axis_series_new(); - PlotRange ranges = {INFINITY, INFINITY, INFINITY, INFINITY, INFINITY, INFINITY}; - - if (!convert_inputstream_into_args(args, file_args, argc, argv, &ranges, special_axis_series)) return 0; - - if (file_args->file_path != "-" && !file_exists(file_args->file_path)) - { - fprintf(stderr, "File not found (%s)\n", file_args->file_path.c_str()); - return 0; - } - - grm_args_values(args, "kind", "s", &kind); - if (!str_equals_any(kind, "barplot", "histogram", "line", "scatter", "stairs", "stem")) - { - file_args->file_x_columns.clear(); - file_args->file_y_columns.clear(); - file_args->file_error_columns.clear(); - xye_file = 0; - } - if (xye_file) - { - file_args->file_x_columns.clear(); - file_args->file_x_columns = "0"; - file_args->file_y_columns.clear(); - file_args->file_y_columns = "1"; - file_args->file_error_columns.clear(); - file_args->file_error_columns = equal_up_and_down_error ? "2" : "2,3"; - } - if (read_data_file(file_args->file_path, file_data, x_data, y_data, error_data, labels, args, - file_args->file_columns.c_str(), file_args->file_x_columns.c_str(), - file_args->file_y_columns.c_str(), file_args->file_error_columns.c_str(), &ranges)) - { - return 0; - } - // convert grm_special_axis_series_t entries into int vector - if (parse_columns(&bottom_series, special_axis_series->bottom.c_str()) != ERROR_NONE) return 0; - if (parse_columns(&left_series, special_axis_series->left.c_str()) != ERROR_NONE) return 0; - if (parse_columns(&right_series, special_axis_series->right.c_str()) != ERROR_NONE) return 0; - if (parse_columns(&top_series, special_axis_series->top.c_str()) != ERROR_NONE) return 0; - if (parse_columns(&twin_x_series, special_axis_series->twin_x.c_str()) != ERROR_NONE) return 0; - if (parse_columns(&twin_y_series, special_axis_series->twin_y.c_str()) != ERROR_NONE) return 0; - - if (!file_data.empty()) - { - depth = file_data.size(); - cols = file_data[0].size(); - if (str_equals_any(kind, "barplot", "histogram", "line", "scatter", "stairs", "stem")) - { - cols -= x_data.size() + error_data.size(); // less y columns if x or error data given - } - rows = file_data[0][0].size(); - depth = (depth == 1) ? 0 : depth; - } - else - { - fprintf(stderr, "File is empty\n"); - return 0; - } - if (cols != labels.size()) + for (int i = 1; i < argc; i++) { - fprintf(stderr, "The number of columns (%zu) doesn't fit the number of labels (%zu)\n", cols, labels.size()); + if (i == 1 && strcmp(argv[i], "--plot") != 0) plot_idx.push_back(i - 1); + if (strcmp(argv[i], "--plot") == 0) plot_idx.push_back(i); } - - series.resize(cols); - - wstype = getenv("GKS_WSTYPE"); - if (wstype != nullptr && strcmp(wstype, "381") == 0 && (env = getenv("GR_DISPLAY")) != nullptr) + plot_num = plot_idx.size(); + for (int i = 2; i <= plot_num; i++) { - handle = grm_open(GRM_SENDER, env, 8002, nullptr, nullptr); - if (handle == nullptr) + if (plot_num % i == 0) { - fprintf(stderr, "GRM connection to '%s' could not be established\n", env); + divisor = i; + break; } } + if (plot_num >= 4) divisor = 2; + if (plot_num >= 9) divisor = 3; + if (plot_num >= 16) divisor = 4; - if ((strcmp(kind, "line") == 0 || (strcmp(kind, "scatter") == 0 && !scatter_with_z)) && - ((rows >= 50 && cols >= 50) || (use_bins && rows >= 49 && cols >= 49))) - { - fprintf(stderr, "Too much data for %s plot - use heatmap instead\n", kind); - kind = "heatmap"; - grm_args_push(args, "kind", "s", kind); - } - if (!str_equals_any(kind, "isosurface", "quiver", "volume") && depth >= 1) - { - fprintf(stderr, "Too much data for %s plot - use volume instead\n", kind); - kind = "volume"; - grm_args_push(args, "kind", "s", kind); - } - - if (!str_equals_any(kind, "contour", "contourf", "heatmap", "imshow", "marginal_heatmap", "surface", "wireframe")) + plot_idx.push_back(argc); + std::vector plot(plot_num); + for (int plot_i = 0; plot_i < plot_num; plot_i++) { - // these parameters are only for surface and similar types - use_bins = 0; - xyz_file = 0; - } - - if (str_equals_any(kind, "contour", "contourf", "heatmap", "imshow", "marginal_heatmap", "surface", "wireframe")) - { - int x_dim = cols, y_dim = rows, z_dim = rows * cols; - double xmin, xmax, ymin, ymax; - if (cols <= 1 || (xyz_file && cols < 3)) - { - fprintf(stderr, "Insufficient data for plot type (%s)\n", kind); + std::vector labels; + std::vector labels_c; + std::vector tmp; + PlotRange ranges = {INFINITY, INFINITY, INFINITY, INFINITY, INFINITY, INFINITY}; + auto start = plot_idx[plot_i]; + auto end = plot_idx[plot_i + 1]; + for (int j = start; j < end; j++) tmp.push_back(argv[j]); + + file_data.clear(); + series.clear(); + y_data.clear(); + x_data.clear(); + error_data.clear(); + file_args = grm_file_args_new(); + plot[plot_i] = grm_args_new(); + if (!convert_inputstream_into_args(plot[plot_i], file_args, end - start, reinterpret_cast(tmp.data()), + &ranges, special_axis_series)) + return 0; + + if (file_args->file_path != "-" && !file_exists(file_args->file_path)) + { + fprintf(stderr, "File not found (%s)\n", file_args->file_path.c_str()); return 0; } - if (xyz_file) - { - x_dim = 1, z_dim = rows; - for (int i = 1; i < rows; i++) - { - if (file_data[depth][1][i] == file_data[depth][1][i - 1]) - { - x_dim += 1; - } - else - { - y_dim = rows / x_dim; - break; - } - } - ranges.xmin = file_data[depth][0][0]; - ranges.xmax = file_data[depth][0][x_dim - 1]; - ranges.ymin = file_data[depth][1][0]; - ranges.ymax = file_data[depth][1][rows - 1]; - } - - std::vector xi(x_dim), yi(y_dim), zi(z_dim); - if (!grm_args_values(args, "x_range", "dd", &xmin, &xmax)) + grm_args_values(plot[plot_i], "kind", "s", &kind); + if (plot_num == 1) grm_args_push(args, "kind", "s", kind); + if (!str_equals_any(kind, "barplot", "histogram", "line", "scatter", "stairs", "stem")) { - xmin = 0.0; - xmax = x_dim - 1.0; + file_args->file_x_columns.clear(); + file_args->file_y_columns.clear(); + file_args->file_error_columns.clear(); + xye_file = 0; } - if (!grm_args_values(args, "y_range", "dd", &ymin, &ymax) && !use_bins) + if (xye_file) { - ymin = 0.0; - ymax = y_dim - 1.0; + file_args->file_x_columns.clear(); + file_args->file_x_columns = "0"; + file_args->file_y_columns.clear(); + file_args->file_y_columns = "1"; + file_args->file_error_columns.clear(); + file_args->file_error_columns = equal_up_and_down_error ? "2" : "2,3"; } - adjust_ranges(&ranges.xmin, &ranges.xmax, xmin, xmax); - adjust_ranges(&ranges.ymin, &ranges.ymax, ymin, ymax); - - if (use_bins) + if (read_data_file(file_args->file_path, file_data, x_data, y_data, error_data, labels, plot[plot_i], + file_args->file_columns.c_str(), file_args->file_x_columns.c_str(), + file_args->file_y_columns.c_str(), file_args->file_error_columns.c_str(), &ranges)) { - try - { - ranges.xmin = std::stod(labels[0]); - ranges.xmax = std::stod(labels[cols - 1]); - } - catch (std::invalid_argument &e) - { - fprintf(stderr, "Invalid argument for x_range parameter (%s, %s) while using option use_bins\n", - labels[0].c_str(), labels[cols - 1].c_str()); - } + return 0; } - ranges.ymax = (ranges.ymax <= ranges.ymin) ? ranges.ymax + ranges.ymin : ranges.ymax; - if (xyz_file) + // convert grm_special_axis_series_t entries into int vector + if (parse_columns(&bottom_series, special_axis_series->bottom.c_str()) != ERROR_NONE) return 0; + if (parse_columns(&left_series, special_axis_series->left.c_str()) != ERROR_NONE) return 0; + if (parse_columns(&right_series, special_axis_series->right.c_str()) != ERROR_NONE) return 0; + if (parse_columns(&top_series, special_axis_series->top.c_str()) != ERROR_NONE) return 0; + if (parse_columns(&twin_x_series, special_axis_series->twin_x.c_str()) != ERROR_NONE) return 0; + if (parse_columns(&twin_y_series, special_axis_series->twin_y.c_str()) != ERROR_NONE) return 0; + + if (!file_data.empty()) { - for (row = 0; row < rows; ++row) + depth = file_data.size(); + cols = file_data[0].size(); + if (str_equals_any(kind, "barplot", "histogram", "line", "scatter", "stairs", "stem")) { - if (row < x_dim) - { - xi[row] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)row / ((double)x_dim - 1)); - } - if (row % x_dim == 0) - { - yi[row / x_dim] = - ranges.ymin + (ranges.ymax - ranges.ymin) * ((double)(row / x_dim) / ((double)y_dim - 1)); - } - zi[row] = file_data[depth][2][row]; + cols -= x_data.size() + error_data.size(); // less y columns if x or error data given } + rows = file_data[0][0].size(); + depth = (depth == 1) ? 0 : depth; } else { - for (col = 0; col < cols; ++col) - { - xi[col] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)col / ((double)x_dim - 1)); - for (row = 0; row < rows; ++row) - { - if (col == 0) - { - yi[row] = ranges.ymin + (ranges.ymax - ranges.ymin) * ((double)row / ((double)y_dim - 1)); - } - zi[row * cols + col] = file_data[depth][col][row]; - } - } + fprintf(stderr, "File is empty\n"); + return 0; } - - if (ranges.zmax != INFINITY) + if (cols != labels.size()) { - int elem; - double min_val = *std::min_element(std::begin(zi), std::end(zi)); - double max_val = *std::max_element(std::begin(zi), std::end(zi)); - - for (elem = 0; elem < z_dim; ++elem) - { - zi[elem] = ranges.zmin + (ranges.zmax - ranges.zmin) * (zi[elem] - min_val) / (max_val - min_val); - } + fprintf(stderr, "The number of columns (%zu) doesn't fit the number of labels (%zu)\n", cols, labels.size()); } - /* for imshow plot */ - grm_args_push(args, "c", "nD", z_dim, zi.data()); - grm_args_push(args, "c_dims", "ii", x_dim, y_dim); - - grm_args_push(args, "x", "nD", x_dim, xi.data()); - grm_args_push(args, "y", "nD", y_dim, yi.data()); - grm_args_push(args, "z", "nD", z_dim, zi.data()); - } - else if (strcmp(kind, "line") == 0 || (strcmp(kind, "scatter") == 0 && !scatter_with_z)) - { - grm_args_t *error = nullptr; - std::vector error_vec; - std::vector x(rows); - std::vector filtered_error_columns; - int err = 0, col_group_elem = 3, down_err_off = 2; - const char *spec; - int series_num; - int y_cnt = 0, x_cnt = 0, err_cnt = 0; - double xmin, xmax; - - if (!grm_args_values(args, "x_range", "dd", &xmin, &xmax)) - { - xmin = 0.0; - xmax = rows - 1.0; - } - adjust_ranges(&ranges.xmin, &ranges.xmax, xmin, xmax); - cols += x_data.size() + error_data.size(); + series.resize(cols); - // calculate x-data if x_data is empty which means no x given - if (x_data.empty()) + wstype = getenv("GKS_WSTYPE"); + if (wstype != nullptr && strcmp(wstype, "381") == 0 && (env = getenv("GR_DISPLAY")) != nullptr) { - for (row = 0; row < rows; row++) + handle = grm_open(GRM_SENDER, env, 8002, nullptr, nullptr); + if (handle == nullptr) { - x[row] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)row / ((double)rows - 1)); + fprintf(stderr, "GRM connection to '%s' could not be established\n", env); } } - // precalculate the number of error columns, so they can be removed from the y-data in the following step - if (grm_args_values(args, "error", "a", &error) || xye_file || equal_up_and_down_error) - { - if (error == nullptr) - { - error = grm_args_new(); - grm_args_push(args, "error", "a", error); - } - if (equal_up_and_down_error) - { - col_group_elem -= 1; - down_err_off -= 1; - } - err = floor(cols / col_group_elem) * down_err_off; + if ((strcmp(kind, "line") == 0 || (strcmp(kind, "scatter") == 0 && !scatter_with_z)) && + ((rows >= 50 && cols >= 50) || (use_bins && rows >= 49 && cols >= 49))) + { + fprintf(stderr, "Too much data for %s plot - use heatmap instead\n", kind); + kind = "heatmap"; + grm_args_push(plot[plot_i], "kind", "s", kind); } - // find min and max value of all y-data and make sure the all data points are inside that range - if (ranges.ymax != INFINITY && ranges.ymin != INFINITY) + if (!str_equals_any(kind, "isosurface", "quiver", "volume") && depth >= 1) { - double min_val = INFINITY, max_val = -INFINITY; - for (col = 0; col < cols - err; col++) - { - if (std::find(x_data.begin(), x_data.end(), col) != x_data.end()) continue; - if (std::find(error_data.begin(), error_data.end(), col) != error_data.end()) continue; - min_val = std::min( - min_val, - *std::min_element( - std::begin(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]), - std::end(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]))); - max_val = std::max( - max_val, - *std::max_element( - std::begin(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]), - std::end(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]))); - } - - for (col = 0; col < cols; ++col) - { - if (std::find(x_data.begin(), x_data.end(), col) != x_data.end()) continue; - if (std::find(error_data.begin(), error_data.end(), col) != error_data.end()) continue; - for (row = 0; row < rows; row++) - { - file_data[depth][col][row] = ranges.ymin + (ranges.ymax - ranges.ymin) * - (file_data[depth][col][row] - min_val) / - (max_val - min_val); - } - } + fprintf(stderr, "Too much data for %s plot - use volume instead\n", kind); + kind = "volume"; + grm_args_push(plot[plot_i], "kind", "s", kind); } - // calculate the error data - if (grm_args_values(args, "error", "a", &error)) + if (!str_equals_any(kind, "contour", "contourf", "heatmap", "imshow", "marginal_heatmap", "surface", "wireframe")) { - int i; - int color_up, color_down, color; - std::vector errors_up(rows); - std::vector errors_down(rows); + // these parameters are only for surface and similar types + use_bins = 0; + xyz_file = 0; + } - if ((cols < col_group_elem && error_data.empty()) || - (!error_data.empty() && error_data.size() < down_err_off)) + if (str_equals_any(kind, "contour", "contourf", "heatmap", "imshow", "marginal_heatmap", "surface", "wireframe")) + { + int x_dim = cols, y_dim = rows, z_dim = rows * cols; + double xmin, xmax, ymin, ymax; + if (cols <= 1 || (xyz_file && cols < 3)) { - fprintf(stderr, "Not enough data for error parameter\n"); + fprintf(stderr, "Insufficient data for plot type (%s)\n", kind); + return 0; } - else + if (xyz_file) { - if (error_data.empty()) - { - err = floor(cols / col_group_elem); - error_vec.resize(err); - for (col = 0; col < err; col++) - { - error_vec[col] = grm_args_new(); - for (i = 0; i < rows; i++) - { - errors_up[i] = file_data[depth][col + 1 + col * down_err_off][i]; - errors_down[i] = file_data[depth][col + down_err_off + col * down_err_off][i]; - } - grm_args_push(error_vec[col], "relative", "nDD", rows, errors_up.data(), errors_down.data()); - if (grm_args_values(error, "error_bar_color", "i", &color)) - grm_args_push(error_vec[col], "error_bar_color", "i", color); - if (grm_args_values(error, "downwards_cap_color", "i", &color_down)) - grm_args_push(error_vec[col], "downwards_cap_color", "i", color_down); - if (grm_args_values(error, "upwards_cap_color", "i", &color_up)) - grm_args_push(error_vec[col], "upwards_cap_color", "i", color_up); - } - err *= down_err_off; - } - else + x_dim = 1, z_dim = rows; + for (int i = 1; i < rows; i++) { - int cnt = 0; - err = 0; - error_vec.resize(equal_up_and_down_error ? error_data.size() : error_data.size() / 2); - for (i = 0; i < error_vec.size(); i++) + if (file_data[depth][1][i] == file_data[depth][1][i - 1]) { - error_vec[i] = grm_args_new(); - if (grm_args_values(error, "error_bar_color", "i", &color)) - grm_args_push(error_vec[i], "error_bar_color", "i", color); - if (grm_args_values(error, "downwards_cap_color", "i", &color_down)) - grm_args_push(error_vec[i], "downwards_cap_color", "i", color_down); - if (grm_args_values(error, "upwards_cap_color", "i", &color_up)) - grm_args_push(error_vec[i], "upwards_cap_color", "i", color_up); + x_dim += 1; } - for (int error_col : error_data) + else { - for (i = 0; i < rows; i++) - { - if (equal_up_and_down_error) - { - errors_up[i] = file_data[depth][error_col][i]; - errors_down[i] = file_data[depth][error_col][i]; - } - else if (cnt % 2 == 0) - { - errors_up[i] = file_data[depth][error_col][i]; - } - else if (cnt % 2 != 0) - { - errors_down[i] = file_data[depth][error_col][i]; - } - } - if (cnt % 2 != 0 || equal_up_and_down_error) - { - grm_args_push(error_vec[floor(cnt / (equal_up_and_down_error ? 1 : 2))], "relative", "nDD", - rows, errors_up.data(), errors_down.data()); - } - else - { - filtered_error_columns.push_back(error_col); - } - cnt += 1; + y_dim = rows / x_dim; + break; } } + ranges.xmin = file_data[depth][0][0]; + ranges.xmax = file_data[depth][0][x_dim - 1]; + ranges.ymin = file_data[depth][1][0]; + ranges.ymax = file_data[depth][1][rows - 1]; } - } - if (!x_data.empty() || !error_data.empty()) - { - series_num = y_data.size(); - for (col = 0; col < series_num; col++) + std::vector xi(x_dim), yi(y_dim), zi(z_dim); + + if (!grm_args_values(plot[plot_i], "x_range", "dd", &xmin, &xmax)) { - series[col] = grm_args_new(); - setSeriesLocation(series, col, bottom_series, left_series, right_series, top_series, twin_x_series, - twin_y_series); + xmin = 0.0; + xmax = x_dim - 1.0; } - } - else - { - series_num = cols - err; - } - for (col = 0; col < cols - err; col++) - { - if (x_data.empty() && y_data.empty() && error_data.empty()) + if (!grm_args_values(plot[plot_i], "y_range", "dd", &ymin, &ymax) && !use_bins) { - series[col] = grm_args_new(); - setSeriesLocation(series, col, bottom_series, left_series, right_series, top_series, twin_x_series, - twin_y_series); - grm_args_push(series[col], "x", "nD", rows, x.data()); - grm_args_push(series[col], "y", "nD", rows, - file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)].data()); - if (col < err / down_err_off) - { - int error_bar_style; - grm_args_push(series[col], "error", "a", error_vec[col]); - if (grm_args_values(args, "error_bar_style", "i", &error_bar_style)) - grm_args_push(series[col], "error_bar_style", "i", error_bar_style); - } - if (!labels.empty() && labels.size() > col) labels_c.push_back(labels[col].c_str()); - if (grm_args_values(args, "line_spec", "s", &spec)) grm_args_push(series[col], "line_spec", "s", spec); + ymin = 0.0; + ymax = y_dim - 1.0; } - else + adjust_ranges(&ranges.xmin, &ranges.xmax, xmin, xmax); + adjust_ranges(&ranges.ymin, &ranges.ymax, ymin, ymax); + + if (use_bins) { - if (std::find(x_data.begin(), x_data.end(), col) != x_data.end()) + try { - int error_bar_style; - grm_args_push(series[x_cnt], "x", "nD", rows, file_data[depth][col].data()); - if (grm_args_values(args, "error_bar_style", "i", &error_bar_style)) - grm_args_push(series[x_cnt], "error_bar_style", "i", error_bar_style); - x_cnt += 1; + ranges.xmin = std::stod(labels[0]); + ranges.xmax = std::stod(labels[cols - 1]); } - else if (std::find(y_data.begin(), y_data.end(), col) != y_data.end()) + catch (std::invalid_argument &e) { - grm_args_push(series[y_cnt], "y", "nD", rows, file_data[depth][col].data()); - if (!labels.empty() && labels.size() > y_cnt) labels_c.push_back(labels[y_cnt].c_str()); - if (grm_args_values(args, "line_spec", "s", &spec)) - grm_args_push(series[y_cnt], "line_spec", "s", spec); - y_cnt += 1; + fprintf(stderr, "Invalid argument for x_range parameter (%s, %s) while using option use_bins\n", + labels[0].c_str(), labels[cols - 1].c_str()); } - else if (!equal_up_and_down_error && error != nullptr && - std::find(filtered_error_columns.begin(), filtered_error_columns.end(), col) == - filtered_error_columns.end()) + } + ranges.ymax = (ranges.ymax <= ranges.ymin) ? ranges.ymax + ranges.ymin : ranges.ymax; + + if (xyz_file) + { + for (row = 0; row < rows; ++row) { - grm_args_push(series[err_cnt], "error", "a", error_vec[err_cnt]); - err_cnt += 1; + if (row < x_dim) + { + xi[row] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)row / ((double)x_dim - 1)); + } + if (row % x_dim == 0) + { + yi[row / x_dim] = + ranges.ymin + (ranges.ymax - ranges.ymin) * ((double)(row / x_dim) / ((double)y_dim - 1)); + } + zi[row] = file_data[depth][2][row]; } - else if (equal_up_and_down_error && - std::find(error_data.begin(), error_data.end(), col) != error_data.end()) + } + else + { + for (col = 0; col < cols; ++col) { - grm_args_push(series[err_cnt], "error", "a", error_vec[err_cnt]); - err_cnt += 1; + xi[col] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)col / ((double)x_dim - 1)); + for (row = 0; row < rows; ++row) + { + if (col == 0) + { + yi[row] = ranges.ymin + (ranges.ymax - ranges.ymin) * ((double)row / ((double)y_dim - 1)); + } + zi[row * cols + col] = file_data[depth][col][row]; + } } } - } - cols -= x_data.size() + error_data.size(); - grm_args_push(args, "series", "nA", series_num, series.data()); - if (!labels_c.empty()) grm_args_push(args, "labels", "nS", grm_min(labels_c.size(), series_num), labels_c.data()); - } - else if (str_equals_any(kind, "isosurface", "volume")) - { - int i, j, k; - std::vector data(rows * cols * depth); - int n = (int)rows * (int)cols * (int)depth; - std::vector dims = {(int)cols, (int)rows, (int)depth}; - for (i = 0; i < rows; ++i) - { - for (j = 0; j < cols; ++j) + + if (ranges.zmax != INFINITY) { - for (k = 0; k < depth; ++k) + int elem; + double min_val = *std::min_element(std::begin(zi), std::end(zi)); + double max_val = *std::max_element(std::begin(zi), std::end(zi)); + + for (elem = 0; elem < z_dim; ++elem) { - data[k * cols * rows + j * rows + i] = file_data[k][j][i]; + zi[elem] = ranges.zmin + (ranges.zmax - ranges.zmin) * (zi[elem] - min_val) / (max_val - min_val); } } - } - grm_args_push(args, "c", "nD", n, data.data()); - grm_args_push(args, "c_dims", "nI", 3, dims.data()); - } - else if (str_equals_any(kind, "plot3", "scatter3", "tricontour", "trisurface") || - (strcmp(kind, "scatter") == 0 && scatter_with_z)) - { - double min_x, max_x, min_y, max_y, min_z, max_z; - double xmin, xmax, ymin, ymax, zmin, zmax; - if (cols < 3) - { - fprintf(stderr, "Insufficient data for plot type (%s)\n", kind); - return 0; - } - if (cols > 3) fprintf(stderr, "Only the first 3 columns get displayed\n"); + /* for imshow plot */ + grm_args_push(plot[plot_i], "c", "nD", z_dim, zi.data()); + grm_args_push(plot[plot_i], "c_dims", "ii", x_dim, y_dim); - /* apply the ranges to the data */ - if (ranges.xmax != INFINITY) - { - min_x = *std::min_element(std::begin(file_data[depth][0]), std::end(file_data[depth][0])); - max_x = *std::max_element(std::begin(file_data[depth][0]), std::end(file_data[depth][0])); - if (!grm_args_values(args, "x_range", "dd", &xmin, &xmax)) - { - xmin = min_x; - xmax = max_x; - } - adjust_ranges(&ranges.xmin, &ranges.xmax, xmin, xmax); + grm_args_push(plot[plot_i], "x", "nD", x_dim, xi.data()); + grm_args_push(plot[plot_i], "y", "nD", y_dim, yi.data()); + grm_args_push(plot[plot_i], "z", "nD", z_dim, zi.data()); } - if (ranges.ymax != INFINITY) + else if (strcmp(kind, "line") == 0 || (strcmp(kind, "scatter") == 0 && !scatter_with_z)) { - min_y = *std::min_element(std::begin(file_data[depth][1]), std::end(file_data[depth][1])); - max_y = *std::max_element(std::begin(file_data[depth][1]), std::end(file_data[depth][1])); - if (!grm_args_values(args, "y_range", "dd", &ymin, &ymax)) - { - ymin = min_y; - ymax = max_y; - } - adjust_ranges(&ranges.ymin, &ranges.ymax, ymin, ymax); - ranges.ymax = (ranges.ymax <= ranges.ymin) ? ranges.ymax + ranges.ymin : ranges.ymax; - } - if (ranges.zmax != INFINITY) - { - min_z = *std::min_element(std::begin(file_data[depth][2]), std::end(file_data[depth][2])); - max_z = *std::max_element(std::begin(file_data[depth][2]), std::end(file_data[depth][2])); - if (!grm_args_values(args, "z_range", "dd", &zmin, &zmax)) - { - zmin = min_z; - zmax = max_z; - } - adjust_ranges(&ranges.zmin, &ranges.zmax, zmin, zmax); - ranges.zmax = (ranges.zmax <= ranges.zmin) ? ranges.zmax + ranges.zmin : ranges.zmax; - } - for (row = 0; row < rows; ++row) - { - if (ranges.xmax != INFINITY) - file_data[depth][0][row] = ranges.xmin + (ranges.xmax - ranges.xmin) * - (((double)file_data[depth][0][row]) - min_x) / (max_x - min_x); - if (ranges.ymax != INFINITY) - file_data[depth][1][row] = ranges.ymin + (ranges.ymax - ranges.ymin) * - (((double)file_data[depth][1][row]) - min_y) / (max_y - min_y); - if (ranges.zmax != INFINITY) - file_data[depth][2][row] = ranges.zmin + (ranges.zmax - ranges.zmin) * - (((double)file_data[depth][2][row]) - min_z) / (max_z - min_z); - } + grm_args_t *error = nullptr; + std::vector error_vec; + std::vector x(rows); + std::vector filtered_error_columns; + int err = 0, col_group_elem = 3, down_err_off = 2; + const char *spec; + int series_num; + int y_cnt = 0, x_cnt = 0, err_cnt = 0; + double xmin, xmax; - grm_args_push(args, "x", "nD", rows, file_data[depth][0].data()); - grm_args_push(args, "y", "nD", rows, file_data[depth][1].data()); - grm_args_push(args, "z", "nD", rows, file_data[depth][2].data()); - } - else if (str_equals_any(kind, "barplot", "histogram", "stem", "stairs")) - { - std::vector x(rows); - double xmin, xmax, ymin, ymax; - grm_args_t *error = nullptr; - const char *spec; - std::vector error_vec; - std::vector filtered_error_columns; - int err = 0, col_group_elem = 3, down_err_off = 2; - int series_num = 0; - int err_cnt = 0, y_cnt = 0, x_cnt = 0; - if (!grm_args_values(args, "x_range", "dd", &xmin, &xmax)) - { - if (strcmp(kind, "barplot") == 0) - { - xmin = 1; - xmax = rows; - } - else + if (!grm_args_values(plot[plot_i], "x_range", "dd", &xmin, &xmax)) { xmin = 0.0; xmax = rows - 1.0; } - } - adjust_ranges(&ranges.xmin, &ranges.xmax, xmin, xmax); - cols += x_data.size() + error_data.size(); + adjust_ranges(&ranges.xmin, &ranges.xmax, xmin, xmax); + cols += x_data.size() + error_data.size(); - // precalculate the number of error columns, so they can be removed from the y-data in the following step - if (str_equals_any(kind, "barplot", "histogram") && - (grm_args_values(args, "error", "a", &error) || xye_file || equal_up_and_down_error)) - { - if (error == nullptr) + // calculate x-data if x_data is empty which means no x given + if (x_data.empty()) { - error = grm_args_new(); - grm_args_push(args, "error", "a", error); + for (row = 0; row < rows; row++) + { + x[row] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)row / ((double)rows - 1)); + } } - - if (equal_up_and_down_error) + // precalculate the number of error columns, so they can be removed from the y-data in the following step + if (grm_args_values(plot[plot_i], "error", "a", &error) || xye_file || equal_up_and_down_error) { - col_group_elem -= 1; - down_err_off -= 1; - } - err = floor(cols / col_group_elem) * down_err_off; - } - - /* the needed calculation to get the errorbars out of the data */ - if (grm_args_values(args, "error", "a", &error) || xye_file || equal_up_and_down_error) - { - int nbins, i; - int color_up, color_down, color; - std::vector errors_up(rows); - std::vector errors_down(rows); - std::vector bins; + if (error == nullptr) + { + error = grm_args_new(); + grm_args_push(plot[plot_i], "error", "a", error); + } - if ((cols < col_group_elem && error_data.empty()) || - (!error_data.empty() && error_data.size() < down_err_off)) - { - fprintf(stderr, "Not enough data for error parameter\n"); + if (equal_up_and_down_error) + { + col_group_elem -= 1; + down_err_off -= 1; + } + err = floor(cols / col_group_elem) * down_err_off; } - else if (str_equals_any(kind, "barplot", "histogram")) + // find min and max value of all y-data and make sure the all data points are inside that range + if (ranges.ymax != INFINITY && ranges.ymin != INFINITY) { - if (!grm_args_values(args, "num_bins", "i", &nbins)) + double min_val = INFINITY, max_val = -INFINITY; + for (col = 0; col < cols - err; col++) { - if (!grm_args_values(args, "bins", "i", &nbins, &bins)) + if (std::find(x_data.begin(), x_data.end(), col) != x_data.end()) continue; + if (std::find(error_data.begin(), error_data.end(), col) != error_data.end()) continue; + min_val = std::min( + min_val, + *std::min_element( + std::begin(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]), + std::end(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]))); + max_val = std::max( + max_val, + *std::max_element( + std::begin(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]), + std::end(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]))); + } + + for (col = 0; col < cols; ++col) + { + if (std::find(x_data.begin(), x_data.end(), col) != x_data.end()) continue; + if (std::find(error_data.begin(), error_data.end(), col) != error_data.end()) continue; + for (row = 0; row < rows; row++) { - if (strcmp(kind, "histogram") == 0) nbins = (int)(3.3 * log10((int)rows) + 0.5) + 1; + file_data[depth][col][row] = ranges.ymin + (ranges.ymax - ranges.ymin) * + (file_data[depth][col][row] - min_val) / + (max_val - min_val); } } - if (strcmp(kind, "barplot") == 0) nbins = (int)rows; - if (nbins <= rows) + } + + // calculate the error data + if (grm_args_values(plot[plot_i], "error", "a", &error)) + { + int i; + int color_up, color_down, color; + std::vector errors_up(rows); + std::vector errors_down(rows); + + if ((cols < col_group_elem && error_data.empty()) || + (!error_data.empty() && error_data.size() < down_err_off)) + { + fprintf(stderr, "Not enough data for error parameter\n"); + } + else { if (error_data.empty()) { @@ -1199,12 +939,12 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) for (col = 0; col < err; col++) { error_vec[col] = grm_args_new(); - for (i = 0; i < nbins; i++) + for (i = 0; i < rows; i++) { errors_up[i] = file_data[depth][col + 1 + col * down_err_off][i]; errors_down[i] = file_data[depth][col + down_err_off + col * down_err_off][i]; } - grm_args_push(error_vec[col], "relative", "nDD", nbins, errors_up.data(), errors_down.data()); + grm_args_push(error_vec[col], "relative", "nDD", rows, errors_up.data(), errors_down.data()); if (grm_args_values(error, "error_bar_color", "i", &color)) grm_args_push(error_vec[col], "error_bar_color", "i", color); if (grm_args_values(error, "downwards_cap_color", "i", &color_down)) @@ -1231,7 +971,7 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) } for (int error_col : error_data) { - for (i = 0; i < nbins; i++) + for (i = 0; i < rows; i++) { if (equal_up_and_down_error) { @@ -1250,7 +990,7 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) if (cnt % 2 != 0 || equal_up_and_down_error) { grm_args_push(error_vec[floor(cnt / (equal_up_and_down_error ? 1 : 2))], "relative", - "nDD", nbins, errors_up.data(), errors_down.data()); + "nDD", rows, errors_up.data(), errors_down.data()); } else { @@ -1259,406 +999,724 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) cnt += 1; } } - grm_args_push(error, "relative", "nDD", nbins, errors_up.data(), errors_down.data()); - } - else - { - fprintf(stderr, "Not enough data for error parameter\n"); } } - } - - series_num = (!x_data.empty() || !error_data.empty()) ? y_data.size() : cols - err; - for (col = 0; col < series_num; col++) - { - series[col] = grm_args_new(); - setSeriesLocation(series, col, bottom_series, left_series, right_series, top_series, twin_x_series, - twin_y_series); - } - // find min and max value of all x-data and make sure the all data points are inside that range - if (x_data.empty()) - { - for (row = 0; row < rows; row++) + if (!x_data.empty() || !error_data.empty()) { - x[row] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)row / ((double)rows - 1)); + series_num = y_data.size(); + for (col = 0; col < series_num; col++) + { + series[col] = grm_args_new(); + setSeriesLocation(series, col, bottom_series, left_series, right_series, top_series, twin_x_series, + twin_y_series); + } } - for (col = 0; col < series_num; col++) + else { - grm_args_push(series[col], "x_range", "dd", ranges.xmin, ranges.xmax); + series_num = cols - err; } + for (col = 0; col < cols - err; col++) + { + if (x_data.empty() && y_data.empty() && error_data.empty()) + { + series[col] = grm_args_new(); + setSeriesLocation(series, col, bottom_series, left_series, right_series, top_series, twin_x_series, + twin_y_series); + grm_args_push(series[col], "x", "nD", rows, x.data()); + grm_args_push(series[col], "y", "nD", rows, + file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)].data()); + if (col < err / down_err_off) + { + int error_bar_style; + grm_args_push(series[col], "error", "a", error_vec[col]); + if (grm_args_values(plot[plot_i], "error_bar_style", "i", &error_bar_style)) + grm_args_push(series[col], "error_bar_style", "i", error_bar_style); + } + if (!labels.empty() && labels.size() > col) labels_c.push_back(labels[col].c_str()); + if (grm_args_values(plot[plot_i], "line_spec", "s", &spec)) + grm_args_push(series[col], "line_spec", "s", spec); + } + else + { + if (std::find(x_data.begin(), x_data.end(), col) != x_data.end()) + { + int error_bar_style; + grm_args_push(series[x_cnt], "x", "nD", rows, file_data[depth][col].data()); + if (grm_args_values(plot[plot_i], "error_bar_style", "i", &error_bar_style)) + grm_args_push(series[x_cnt], "error_bar_style", "i", error_bar_style); + x_cnt += 1; + } + else if (std::find(y_data.begin(), y_data.end(), col) != y_data.end()) + { + grm_args_push(series[y_cnt], "y", "nD", rows, file_data[depth][col].data()); + if (!labels.empty() && labels.size() > y_cnt) labels_c.push_back(labels[y_cnt].c_str()); + if (grm_args_values(plot[plot_i], "line_spec", "s", &spec)) + grm_args_push(series[y_cnt], "line_spec", "s", spec); + y_cnt += 1; + } + else if (!equal_up_and_down_error && error != nullptr && + std::find(filtered_error_columns.begin(), filtered_error_columns.end(), col) == + filtered_error_columns.end()) + { + grm_args_push(series[err_cnt], "error", "a", error_vec[err_cnt]); + err_cnt += 1; + } + else if (equal_up_and_down_error && + std::find(error_data.begin(), error_data.end(), col) != error_data.end()) + { + grm_args_push(series[err_cnt], "error", "a", error_vec[err_cnt]); + err_cnt += 1; + } + } + } + cols -= x_data.size() + error_data.size(); + grm_args_push(plot[plot_i], "series", "nA", series_num, series.data()); + if (!labels_c.empty()) + grm_args_push(plot[plot_i], "labels", "nS", grm_min(labels_c.size(), series_num), labels_c.data()); + } + else if (str_equals_any(kind, "isosurface", "volume")) + { + int i, j, k; + std::vector data(rows * cols * depth); + int n = (int)rows * (int)cols * (int)depth; + std::vector dims = {(int)cols, (int)rows, (int)depth}; + for (i = 0; i < rows; ++i) + { + for (j = 0; j < cols; ++j) + { + for (k = 0; k < depth; ++k) + { + data[k * cols * rows + j * rows + i] = file_data[k][j][i]; + } + } + } + + grm_args_push(plot[plot_i], "c", "nD", n, data.data()); + grm_args_push(plot[plot_i], "c_dims", "nI", 3, dims.data()); } - else + else if (str_equals_any(kind, "plot3", "scatter3", "tricontour", "trisurface") || + (strcmp(kind, "scatter") == 0 && scatter_with_z)) { - if (!grm_args_values(args, "x_range", "dd", &xmin, &xmax)) + double min_x, max_x, min_y, max_y, min_z, max_z; + double xmin, xmax, ymin, ymax, zmin, zmax; + if (cols < 3) { - double x_min = INFINITY, x_max = -INFINITY; - for (col = 0; col < x_data.size(); col++) + fprintf(stderr, "Insufficient data for plot type (%s)\n", kind); + return 0; + } + if (cols > 3) fprintf(stderr, "Only the first 3 columns get displayed\n"); + + /* apply the ranges to the data */ + if (ranges.xmax != INFINITY) + { + min_x = *std::min_element(std::begin(file_data[depth][0]), std::end(file_data[depth][0])); + max_x = *std::max_element(std::begin(file_data[depth][0]), std::end(file_data[depth][0])); + if (!grm_args_values(plot[plot_i], "x_range", "dd", &xmin, &xmax)) { - xmin = *std::min_element(std::begin(file_data[depth][x_data[col]]), - std::end(file_data[depth][x_data[col]])); - xmax = *std::max_element(std::begin(file_data[depth][x_data[col]]), - std::end(file_data[depth][x_data[col]])); - x_min = grm_min(x_min, xmin); - x_max = grm_max(x_max, xmax); - if (!x_data.empty() && col < series_num) grm_args_push(series[col], "x_range", "dd", xmin, xmax); + xmin = min_x; + xmax = max_x; } - adjust_ranges(&ranges.xmin, &ranges.xmax, x_min, x_max); + adjust_ranges(&ranges.xmin, &ranges.xmax, xmin, xmax); } - else + if (ranges.ymax != INFINITY) { - for (col = 0; col < series_num; ++col) + min_y = *std::min_element(std::begin(file_data[depth][1]), std::end(file_data[depth][1])); + max_y = *std::max_element(std::begin(file_data[depth][1]), std::end(file_data[depth][1])); + if (!grm_args_values(plot[plot_i], "y_range", "dd", &ymin, &ymax)) { - grm_args_push(series[col], "x_range", "dd", xmin, xmax); + ymin = min_y; + ymax = max_y; } - xmin = INFINITY; - xmax = -INFINITY; - for (col = 0; col < x_data.size(); ++col) + adjust_ranges(&ranges.ymin, &ranges.ymax, ymin, ymax); + ranges.ymax = (ranges.ymax <= ranges.ymin) ? ranges.ymax + ranges.ymin : ranges.ymax; + } + if (ranges.zmax != INFINITY) + { + min_z = *std::min_element(std::begin(file_data[depth][2]), std::end(file_data[depth][2])); + max_z = *std::max_element(std::begin(file_data[depth][2]), std::end(file_data[depth][2])); + if (!grm_args_values(plot[plot_i], "z_range", "dd", &zmin, &zmax)) { - xmin = grm_min(xmin, *std::min_element(std::begin(file_data[depth][x_data[col]]), - std::end(file_data[depth][x_data[col]]))); - xmax = grm_max(xmax, *std::max_element(std::begin(file_data[depth][x_data[col]]), - std::end(file_data[depth][x_data[col]]))); + zmin = min_z; + zmax = max_z; } - for (col = 0; col < x_data.size(); ++col) + adjust_ranges(&ranges.zmin, &ranges.zmax, zmin, zmax); + ranges.zmax = (ranges.zmax <= ranges.zmin) ? ranges.zmax + ranges.zmin : ranges.zmax; + } + for (row = 0; row < rows; ++row) + { + if (ranges.xmax != INFINITY) + file_data[depth][0][row] = ranges.xmin + (ranges.xmax - ranges.xmin) * + (((double)file_data[depth][0][row]) - min_x) / + (max_x - min_x); + if (ranges.ymax != INFINITY) + file_data[depth][1][row] = ranges.ymin + (ranges.ymax - ranges.ymin) * + (((double)file_data[depth][1][row]) - min_y) / + (max_y - min_y); + if (ranges.zmax != INFINITY) + file_data[depth][2][row] = ranges.zmin + (ranges.zmax - ranges.zmin) * + (((double)file_data[depth][2][row]) - min_z) / + (max_z - min_z); + } + + grm_args_push(plot[plot_i], "x", "nD", rows, file_data[depth][0].data()); + grm_args_push(plot[plot_i], "y", "nD", rows, file_data[depth][1].data()); + grm_args_push(plot[plot_i], "z", "nD", rows, file_data[depth][2].data()); + } + else if (str_equals_any(kind, "barplot", "histogram", "stem", "stairs")) + { + std::vector x(rows); + double xmin, xmax, ymin, ymax; + grm_args_t *error = nullptr; + const char *spec; + std::vector error_vec; + std::vector filtered_error_columns; + int err = 0, col_group_elem = 3, down_err_off = 2; + int series_num = 0; + int err_cnt = 0, y_cnt = 0, x_cnt = 0; + if (!grm_args_values(plot[plot_i], "x_range", "dd", &xmin, &xmax)) + { + if (strcmp(kind, "barplot") == 0) { - for (row = 0; row < rows; row++) - { - file_data[depth][x_data[col]][row] = - ranges.xmin + (ranges.xmax - ranges.xmin) / (xmax - xmin) * - ((double)file_data[depth][x_data[col]][row] - xmin); - } + xmin = 1; + xmax = rows; } - // special case for barplot and histogram cause the x-values and bar_width gets calculated via x_range_min - // and max; without the following code block all series will always have the same x and same width - if (str_equals_any(kind, "barplot", "histogram")) + else { - for (col = 0; col < x_data.size(); ++col) + xmin = 0.0; + xmax = rows - 1.0; + } + } + adjust_ranges(&ranges.xmin, &ranges.xmax, xmin, xmax); + cols += x_data.size() + error_data.size(); + + // precalculate the number of error columns, so they can be removed from the y-data in the following step + if (str_equals_any(kind, "barplot", "histogram") && + (grm_args_values(plot[plot_i], "error", "a", &error) || xye_file || equal_up_and_down_error)) + { + if (error == nullptr) + { + error = grm_args_new(); + grm_args_push(plot[plot_i], "error", "a", error); + } + + if (equal_up_and_down_error) + { + col_group_elem -= 1; + down_err_off -= 1; + } + err = floor(cols / col_group_elem) * down_err_off; + } + + /* the needed calculation to get the errorbars out of the data */ + if (grm_args_values(plot[plot_i], "error", "a", &error) || xye_file || equal_up_and_down_error) + { + int nbins, i; + int color_up, color_down, color; + std::vector errors_up(rows); + std::vector errors_down(rows); + std::vector bins; + + if ((cols < col_group_elem && error_data.empty()) || + (!error_data.empty() && error_data.size() < down_err_off)) + { + fprintf(stderr, "Not enough data for error parameter\n"); + } + else if (str_equals_any(kind, "barplot", "histogram")) + { + if (!grm_args_values(plot[plot_i], "num_bins", "i", &nbins)) { - xmin = *std::min_element(std::begin(file_data[depth][x_data[col]]), - std::end(file_data[depth][x_data[col]])); - xmax = *std::max_element(std::begin(file_data[depth][x_data[col]]), - std::end(file_data[depth][x_data[col]])); - if (col < series_num) grm_args_push(series[col], "x_range", "dd", xmin, xmax); + if (!grm_args_values(plot[plot_i], "bins", "i", &nbins, &bins)) + { + if (strcmp(kind, "histogram") == 0) nbins = (int)(3.3 * log10((int)rows) + 0.5) + 1; + } + } + if (strcmp(kind, "barplot") == 0) nbins = (int)rows; + if (nbins <= rows) + { + if (error_data.empty()) + { + err = floor(cols / col_group_elem); + error_vec.resize(err); + for (col = 0; col < err; col++) + { + error_vec[col] = grm_args_new(); + for (i = 0; i < nbins; i++) + { + errors_up[i] = file_data[depth][col + 1 + col * down_err_off][i]; + errors_down[i] = file_data[depth][col + down_err_off + col * down_err_off][i]; + } + grm_args_push(error_vec[col], "relative", "nDD", nbins, errors_up.data(), + errors_down.data()); + if (grm_args_values(error, "error_bar_color", "i", &color)) + grm_args_push(error_vec[col], "error_bar_color", "i", color); + if (grm_args_values(error, "downwards_cap_color", "i", &color_down)) + grm_args_push(error_vec[col], "downwards_cap_color", "i", color_down); + if (grm_args_values(error, "upwards_cap_color", "i", &color_up)) + grm_args_push(error_vec[col], "upwards_cap_color", "i", color_up); + } + err *= down_err_off; + } + else + { + int cnt = 0; + err = 0; + error_vec.resize(equal_up_and_down_error ? error_data.size() : error_data.size() / 2); + for (i = 0; i < error_vec.size(); i++) + { + error_vec[i] = grm_args_new(); + if (grm_args_values(error, "error_bar_color", "i", &color)) + grm_args_push(error_vec[i], "error_bar_color", "i", color); + if (grm_args_values(error, "downwards_cap_color", "i", &color_down)) + grm_args_push(error_vec[i], "downwards_cap_color", "i", color_down); + if (grm_args_values(error, "upwards_cap_color", "i", &color_up)) + grm_args_push(error_vec[i], "upwards_cap_color", "i", color_up); + } + for (int error_col : error_data) + { + for (i = 0; i < nbins; i++) + { + if (equal_up_and_down_error) + { + errors_up[i] = file_data[depth][error_col][i]; + errors_down[i] = file_data[depth][error_col][i]; + } + else if (cnt % 2 == 0) + { + errors_up[i] = file_data[depth][error_col][i]; + } + else if (cnt % 2 != 0) + { + errors_down[i] = file_data[depth][error_col][i]; + } + } + if (cnt % 2 != 0 || equal_up_and_down_error) + { + grm_args_push(error_vec[floor(cnt / (equal_up_and_down_error ? 1 : 2))], "relative", + "nDD", nbins, errors_up.data(), errors_down.data()); + } + else + { + filtered_error_columns.push_back(error_col); + } + cnt += 1; + } + } + grm_args_push(error, "relative", "nDD", nbins, errors_up.data(), errors_down.data()); + } + else + { + fprintf(stderr, "Not enough data for error parameter\n"); } } } - } - // find min and max value of all y-data and make sure the all data points are inside that range - if (!grm_args_values(args, "y_range", "dd", &ymin, &ymax)) - { - double y_min = INFINITY, y_max = -INFINITY; - int tmp_cnt = 0; - for (col = 0; col < cols - err; col++) - { - ymin = grm_min( - 0, *std::min_element( - std::begin(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]), - std::end(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]))); - ymax = *std::max_element( - std::begin(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]), - std::end(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)])); - y_min = grm_min(y_min, ymin); - y_max = grm_max(y_max, ymax); - if (cols - err == series_num) - grm_args_push(series[col], "y_range", "dd", ymin, ymax); - else if (std::find(y_data.begin(), y_data.end(), col) != y_data.end()) - grm_args_push(series[tmp_cnt++], "y_range", "dd", ymin, ymax); - } - adjust_ranges(&ranges.ymin, &ranges.ymax, std::min(0.0, y_min), y_max); - grm_args_push(args, "y_line_pos", "d", 0.0); - } - else - { - /* apply y_range to the data */ - for (col = 0; col < series_num; ++col) + series_num = (!x_data.empty() || !error_data.empty()) ? y_data.size() : cols - err; + for (col = 0; col < series_num; col++) { - grm_args_push(series[col], "y_range", "dd", ymin, ymax); + series[col] = grm_args_new(); + setSeriesLocation(series, col, bottom_series, left_series, right_series, top_series, twin_x_series, + twin_y_series); } - ymin = INFINITY; - ymax = -INFINITY; - for (col = 0; col < cols; ++col) + + // find min and max value of all x-data and make sure the all data points are inside that range + if (x_data.empty()) { - if (std::find(x_data.begin(), x_data.end(), col) != x_data.end()) continue; - if (std::find(error_data.begin(), error_data.end(), col) != error_data.end()) continue; - ymin = grm_min( - ymin, *std::min_element( - std::begin(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]), - std::end(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]))); - ymax = grm_max( - ymax, *std::max_element( - std::begin(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]), - std::end(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]))); - } - if (str_equals_any(kind, "barplot", "histogram", "stem")) ymin = grm_min(ymin, 0); - for (col = 0; col < cols; ++col) + for (row = 0; row < rows; row++) + { + x[row] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)row / ((double)rows - 1)); + } + for (col = 0; col < series_num; col++) + { + grm_args_push(series[col], "x_range", "dd", ranges.xmin, ranges.xmax); + } + } + else { - if (std::find(x_data.begin(), x_data.end(), col) != x_data.end()) continue; - if (std::find(error_data.begin(), error_data.end(), col) != error_data.end()) continue; - for (row = 0; row < rows; ++row) + if (!grm_args_values(plot[plot_i], "x_range", "dd", &xmin, &xmax)) + { + double x_min = INFINITY, x_max = -INFINITY; + for (col = 0; col < x_data.size(); col++) + { + xmin = *std::min_element(std::begin(file_data[depth][x_data[col]]), + std::end(file_data[depth][x_data[col]])); + xmax = *std::max_element(std::begin(file_data[depth][x_data[col]]), + std::end(file_data[depth][x_data[col]])); + x_min = grm_min(x_min, xmin); + x_max = grm_max(x_max, xmax); + if (!x_data.empty() && col < series_num) grm_args_push(series[col], "x_range", "dd", xmin, xmax); + } + adjust_ranges(&ranges.xmin, &ranges.xmax, x_min, x_max); + } + else { - file_data[depth][col][row] = ranges.ymin + (ranges.ymax - ranges.ymin) / (ymax - ymin) * - ((double)file_data[depth][col][row] - ymin); + for (col = 0; col < series_num; ++col) + { + grm_args_push(series[col], "x_range", "dd", xmin, xmax); + } + xmin = INFINITY; + xmax = -INFINITY; + for (col = 0; col < x_data.size(); ++col) + { + xmin = grm_min(xmin, *std::min_element(std::begin(file_data[depth][x_data[col]]), + std::end(file_data[depth][x_data[col]]))); + xmax = grm_max(xmax, *std::max_element(std::begin(file_data[depth][x_data[col]]), + std::end(file_data[depth][x_data[col]]))); + } + for (col = 0; col < x_data.size(); ++col) + { + for (row = 0; row < rows; row++) + { + file_data[depth][x_data[col]][row] = + ranges.xmin + (ranges.xmax - ranges.xmin) / (xmax - xmin) * + ((double)file_data[depth][x_data[col]][row] - xmin); + } + } + // special case for barplot and histogram cause the x-values and bar_width gets calculated via + // x_range_min and max; without the following code block all series will always have the same x and + // same width + if (str_equals_any(kind, "barplot", "histogram")) + { + for (col = 0; col < x_data.size(); ++col) + { + xmin = *std::min_element(std::begin(file_data[depth][x_data[col]]), + std::end(file_data[depth][x_data[col]])); + xmax = *std::max_element(std::begin(file_data[depth][x_data[col]]), + std::end(file_data[depth][x_data[col]])); + if (col < series_num) grm_args_push(series[col], "x_range", "dd", xmin, xmax); + } + } } } - grm_args_push(args, "y_line_pos", "d", - ranges.ymin + (ranges.ymax - ranges.ymin) / (ymax - ymin) * (0.0 - ymin)); - } - // push the data into the container structure - for (col = 0; col < cols - err; col++) - { - if (x_data.empty() && y_data.empty() && error_data.empty()) - { - if (!labels.empty() && labels.size() > col) labels_c.push_back(labels[col].c_str()); - grm_args_push(series[col], "x", "nD", rows, x.data()); - /* for barplot */ - grm_args_push(series[col], "y", "nD", rows, - file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)].data()); - /* for histogram */ - grm_args_push(series[col], "weights", "nD", rows, - file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)].data()); - /* for stairs */ - grm_args_push(series[col], "z", "nD", rows, - file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)].data()); - if (grm_args_values(args, "line_spec", "s", &spec)) grm_args_push(series[col], "line_spec", "s", spec); - if (str_equals_any(kind, "barplot", "histogram") && series_num > 1) - grm_args_push(series[col], "transparency", "d", 0.5); - if (col < err / down_err_off) + // find min and max value of all y-data and make sure the all data points are inside that range + if (!grm_args_values(plot[plot_i], "y_range", "dd", &ymin, &ymax)) + { + double y_min = INFINITY, y_max = -INFINITY; + int tmp_cnt = 0; + for (col = 0; col < cols - err; col++) { - int error_bar_style; - grm_args_push(series[col], "error", "a", error_vec[col]); - if (grm_args_values(args, "error_bar_style", "i", &error_bar_style)) - grm_args_push(series[err_cnt], "error_bar_style", "i", error_bar_style); + ymin = grm_min( + 0, + *std::min_element( + std::begin(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]), + std::end(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]))); + ymax = *std::max_element( + std::begin(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]), + std::end(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)])); + y_min = grm_min(y_min, ymin); + y_max = grm_max(y_max, ymax); + if (cols - err == series_num) + grm_args_push(series[col], "y_range", "dd", ymin, ymax); + else if (std::find(y_data.begin(), y_data.end(), col) != y_data.end()) + grm_args_push(series[tmp_cnt++], "y_range", "dd", ymin, ymax); } + adjust_ranges(&ranges.ymin, &ranges.ymax, std::min(0.0, y_min), y_max); + grm_args_push(plot[plot_i], "y_line_pos", "d", 0.0); } else { - if (std::find(x_data.begin(), x_data.end(), col) != x_data.end()) + /* apply y_range to the data */ + for (col = 0; col < series_num; ++col) + { + grm_args_push(series[col], "y_range", "dd", ymin, ymax); + } + ymin = INFINITY; + ymax = -INFINITY; + for (col = 0; col < cols; ++col) { - int error_bar_style; - if (grm_args_values(args, "error_bar_style", "i", &error_bar_style)) - grm_args_push(series[x_cnt], "error_bar_style", "i", error_bar_style); - grm_args_push(series[x_cnt], "x", "nD", rows, file_data[depth][col].data()); - x_cnt += 1; + if (std::find(x_data.begin(), x_data.end(), col) != x_data.end()) continue; + if (std::find(error_data.begin(), error_data.end(), col) != error_data.end()) continue; + ymin = grm_min( + ymin, + *std::min_element( + std::begin(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]), + std::end(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]))); + ymax = grm_max( + ymax, + *std::max_element( + std::begin(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]), + std::end(file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)]))); } - else if (std::find(y_data.begin(), y_data.end(), col) != y_data.end()) + if (str_equals_any(kind, "barplot", "histogram", "stem")) ymin = grm_min(ymin, 0); + for (col = 0; col < cols; ++col) { - if (!labels.empty() && labels.size() > y_cnt) labels_c.push_back(labels[y_cnt].c_str()); - grm_args_push(series[y_cnt], "y", "nD", rows, file_data[depth][col].data()); + if (std::find(x_data.begin(), x_data.end(), col) != x_data.end()) continue; + if (std::find(error_data.begin(), error_data.end(), col) != error_data.end()) continue; + for (row = 0; row < rows; ++row) + { + file_data[depth][col][row] = ranges.ymin + (ranges.ymax - ranges.ymin) / (ymax - ymin) * + ((double)file_data[depth][col][row] - ymin); + } + } + grm_args_push(plot[plot_i], "y_line_pos", "d", + ranges.ymin + (ranges.ymax - ranges.ymin) / (ymax - ymin) * (0.0 - ymin)); + } + + // push the data into the container structure + for (col = 0; col < cols - err; col++) + { + if (x_data.empty() && y_data.empty() && error_data.empty()) + { + if (!labels.empty() && labels.size() > col) labels_c.push_back(labels[col].c_str()); + grm_args_push(series[col], "x", "nD", rows, x.data()); + /* for barplot */ + grm_args_push(series[col], "y", "nD", rows, + file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)].data()); /* for histogram */ - grm_args_push(series[y_cnt], "weights", "nD", rows, file_data[depth][col].data()); + grm_args_push(series[col], "weights", "nD", rows, + file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)].data()); /* for stairs */ - grm_args_push(series[y_cnt], "z", "nD", rows, file_data[depth][col].data()); - if (grm_args_values(args, "line_spec", "s", &spec)) - grm_args_push(series[y_cnt], "line_spec", "s", spec); + grm_args_push(series[col], "z", "nD", rows, + file_data[depth][col + ((col < err / down_err_off) ? col * down_err_off : err)].data()); + if (grm_args_values(plot[plot_i], "line_spec", "s", &spec)) + grm_args_push(series[col], "line_spec", "s", spec); if (str_equals_any(kind, "barplot", "histogram") && series_num > 1) - grm_args_push(series[y_cnt], "transparency", "d", 0.5); - y_cnt += 1; - } - else if (!equal_up_and_down_error && str_equals_any(kind, "barplot", "histogram") && error != nullptr && - std::find(filtered_error_columns.begin(), filtered_error_columns.end(), col) == - filtered_error_columns.end()) - { - grm_args_push(series[err_cnt], "error", "a", error_vec[err_cnt]); - err_cnt += 1; + grm_args_push(series[col], "transparency", "d", 0.5); + if (col < err / down_err_off) + { + int error_bar_style; + grm_args_push(series[col], "error", "a", error_vec[col]); + if (grm_args_values(plot[plot_i], "error_bar_style", "i", &error_bar_style)) + grm_args_push(series[err_cnt], "error_bar_style", "i", error_bar_style); + } } - else if (equal_up_and_down_error && - std::find(error_data.begin(), error_data.end(), col) != error_data.end()) + else { - grm_args_push(series[err_cnt], "error", "a", error_vec[err_cnt]); - err_cnt += 1; + if (std::find(x_data.begin(), x_data.end(), col) != x_data.end()) + { + int error_bar_style; + if (grm_args_values(plot[plot_i], "error_bar_style", "i", &error_bar_style)) + grm_args_push(series[x_cnt], "error_bar_style", "i", error_bar_style); + grm_args_push(series[x_cnt], "x", "nD", rows, file_data[depth][col].data()); + x_cnt += 1; + } + else if (std::find(y_data.begin(), y_data.end(), col) != y_data.end()) + { + if (!labels.empty() && labels.size() > y_cnt) labels_c.push_back(labels[y_cnt].c_str()); + grm_args_push(series[y_cnt], "y", "nD", rows, file_data[depth][col].data()); + /* for histogram */ + grm_args_push(series[y_cnt], "weights", "nD", rows, file_data[depth][col].data()); + /* for stairs */ + grm_args_push(series[y_cnt], "z", "nD", rows, file_data[depth][col].data()); + if (grm_args_values(plot[plot_i], "line_spec", "s", &spec)) + grm_args_push(series[y_cnt], "line_spec", "s", spec); + if (str_equals_any(kind, "barplot", "histogram") && series_num > 1) + grm_args_push(series[y_cnt], "transparency", "d", 0.5); + y_cnt += 1; + } + else if (!equal_up_and_down_error && str_equals_any(kind, "barplot", "histogram") && + error != nullptr && + std::find(filtered_error_columns.begin(), filtered_error_columns.end(), col) == + filtered_error_columns.end()) + { + grm_args_push(series[err_cnt], "error", "a", error_vec[err_cnt]); + err_cnt += 1; + } + else if (equal_up_and_down_error && + std::find(error_data.begin(), error_data.end(), col) != error_data.end()) + { + grm_args_push(series[err_cnt], "error", "a", error_vec[err_cnt]); + err_cnt += 1; + } } } - } - cols -= x_data.size() + error_data.size(); - grm_args_push(args, "series", "nA", series_num, series.data()); + cols -= x_data.size() + error_data.size(); + grm_args_push(plot[plot_i], "series", "nA", series_num, series.data()); - if (!labels_c.empty()) grm_args_push(args, "labels", "nS", grm_min(labels_c.size(), series_num), labels_c.data()); - } - else if (strcmp(kind, "pie") == 0) - { - std::vector x(cols); - std::vector c(cols * 3); - for (col = 0; col < cols; col++) + if (!labels_c.empty()) + grm_args_push(plot[plot_i], "labels", "nS", grm_min(labels_c.size(), series_num), labels_c.data()); + } + else if (strcmp(kind, "pie") == 0) { - x[col] = file_data[depth][col][0]; - if (!labels.empty()) + std::vector x(cols); + std::vector c(cols * 3); + for (col = 0; col < cols; col++) { - labels_c.push_back(labels[col].c_str()); + x[col] = file_data[depth][col][0]; + if (!labels.empty()) + { + labels_c.push_back(labels[col].c_str()); + } } - } - grm_args_push(args, "x", "nD", cols, x.data()); - if (!labels_c.empty()) - { - grm_args_push(args, "labels", "nS", cols, labels_c.data()); - } - if (rows >= 4) - { - for (col = 0; col < cols; col++) + grm_args_push(plot[plot_i], "x", "nD", cols, x.data()); + if (!labels_c.empty()) + { + grm_args_push(plot[plot_i], "labels", "nS", cols, labels_c.data()); + } + if (rows >= 4) { - for (row = 1; row < 4; row++) + for (col = 0; col < cols; col++) { - c[(row - 1) * cols + col] = (double)file_data[depth][col][row]; + for (row = 1; row < 4; row++) + { + c[(row - 1) * cols + col] = (double)file_data[depth][col][row]; + } } + grm_args_push(plot[plot_i], "c", "nD", c.size(), c.data()); + } + else if (rows > 1) + { + fprintf(stderr, "Insufficient data for custom colors\n"); } - grm_args_push(args, "c", "nD", c.size(), c.data()); - } - else if (rows > 1) - { - fprintf(stderr, "Insufficient data for custom colors\n"); - } - } - else if (strcmp(kind, "polar_histogram") == 0) - { - if (cols > 1) fprintf(stderr, "Only the first column gets displayed\n"); - grm_args_push(args, "x", "nD", rows, file_data[depth][0].data()); - } - else if (strcmp(kind, "polar_line") == 0 || strcmp(kind, "polar_scatter") == 0) - { - if (cols % 2 == 1) - { - fprintf(stderr, "For polar_line and polar_scatter plots x and y must always be given, but in this case the " - "number of columns is odd -> 1 column is missing.\n"); - cols -= 1; } - for (col = 0; col < cols; col++) + else if (strcmp(kind, "polar_histogram") == 0) { - if (!labels.empty() && col < labels.size()) labels_c.push_back(labels[col].c_str()); + if (cols > 1) fprintf(stderr, "Only the first column gets displayed\n"); + grm_args_push(plot[plot_i], "x", "nD", rows, file_data[depth][0].data()); } - if (!labels_c.empty()) grm_args_push(args, "labels", "nS", grm_min(labels_c.size(), cols), labels_c.data()); - - for (col = 0; col <= cols / 2; col += 2) + else if (strcmp(kind, "polar_line") == 0 || strcmp(kind, "polar_scatter") == 0) { - series[col / 2] = grm_args_new(); - grm_args_push(series[col / 2], "x", "nD", rows, file_data[depth][col].data()); - grm_args_push(series[col / 2], "y", "nD", rows, file_data[depth][col + 1].data()); - } - grm_args_push(args, "series", "nA", cols / 2, series.data()); - } - else if (strcmp(kind, "polar_heatmap") == 0) - { - std::vector xi(cols), yi(rows), zi(rows * cols); - double xmin, xmax, ymin, ymax; + if (cols % 2 == 1) + { + fprintf(stderr, "For polar_line and polar_scatter plots x and y must always be given, but in this case " + "the number of columns is odd -> 1 column is missing.\n"); + cols -= 1; + } + for (col = 0; col < cols; col++) + { + if (!labels.empty() && col < labels.size()) labels_c.push_back(labels[col].c_str()); + } + if (!labels_c.empty()) + grm_args_push(plot[plot_i], "labels", "nS", grm_min(labels_c.size(), cols), labels_c.data()); - if (cols <= 1) - { - fprintf(stderr, "Insufficient data for plot type (%s)\n", kind); - return 0; - } - if (!grm_args_values(args, "x_range", "dd", &xmin, &xmax)) - { - xmin = 0.0; - xmax = 360.0; + for (col = 0; col <= cols / 2; col += 2) + { + series[col / 2] = grm_args_new(); + grm_args_push(series[col / 2], "x", "nD", rows, file_data[depth][col].data()); + grm_args_push(series[col / 2], "y", "nD", rows, file_data[depth][col + 1].data()); + } + grm_args_push(plot[plot_i], "series", "nA", cols / 2, series.data()); } - if (!grm_args_values(args, "y_range", "dd", &ymin, &ymax)) + else if (strcmp(kind, "polar_heatmap") == 0) { - ymin = 0.0; - ymax = 3.0; - } - adjust_ranges(&ranges.xmin, &ranges.xmax, xmin, xmax); - adjust_ranges(&ranges.ymin, &ranges.ymax, ymin, ymax); - ranges.ymax = (ranges.ymax <= ranges.ymin) ? ranges.ymax + ranges.ymin : ranges.ymax; + std::vector xi(cols), yi(rows), zi(rows * cols); + double xmin, xmax, ymin, ymax; - for (col = 0; col < cols; ++col) - { - xi[col] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)col / ((double)cols - 1)); - for (row = 0; row < rows; ++row) + if (cols <= 1) { - if (col == 0) + fprintf(stderr, "Insufficient data for plot type (%s)\n", kind); + return 0; + } + if (!grm_args_values(plot[plot_i], "x_range", "dd", &xmin, &xmax)) + { + xmin = 0.0; + xmax = 360.0; + } + if (!grm_args_values(plot[plot_i], "y_range", "dd", &ymin, &ymax)) + { + ymin = 0.0; + ymax = 3.0; + } + adjust_ranges(&ranges.xmin, &ranges.xmax, xmin, xmax); + adjust_ranges(&ranges.ymin, &ranges.ymax, ymin, ymax); + ranges.ymax = (ranges.ymax <= ranges.ymin) ? ranges.ymax + ranges.ymin : ranges.ymax; + + for (col = 0; col < cols; ++col) + { + xi[col] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)col / ((double)cols - 1)); + for (row = 0; row < rows; ++row) { - yi[row] = ranges.ymin + (ranges.ymax - ranges.ymin) * ((double)row / ((double)rows - 1)); + if (col == 0) + { + yi[row] = ranges.ymin + (ranges.ymax - ranges.ymin) * ((double)row / ((double)rows - 1)); + } + zi[row * cols + col] = file_data[depth][col][row]; } - zi[row * cols + col] = file_data[depth][col][row]; } + grm_args_push(plot[plot_i], "x", "nD", cols, xi.data()); + grm_args_push(plot[plot_i], "y", "nD", rows, yi.data()); + grm_args_push(plot[plot_i], "z", "nD", cols * rows, zi.data()); } - grm_args_push(args, "x", "nD", cols, xi.data()); - grm_args_push(args, "y", "nD", rows, yi.data()); - grm_args_push(args, "z", "nD", cols * rows, zi.data()); - } - else if (strcmp(kind, "quiver") == 0) - { - std::vector x(cols); - std::vector y(rows); - std::vector u(cols * rows); - std::vector v(cols * rows); - double xmin, xmax, ymin, ymax; - - if (depth < 2) - { - fprintf(stderr, "Not enough data for quiver plot\n"); - return 0; - } - - if (!grm_args_values(args, "x_range", "dd", &xmin, &xmax)) - { - xmin = 0.0; - xmax = cols - 1.0; - } - if (!grm_args_values(args, "y_range", "dd", &ymin, &ymax)) + else if (strcmp(kind, "quiver") == 0) { - ymin = 0.0; - ymax = rows - 1.0; - } - adjust_ranges(&ranges.xmin, &ranges.xmax, xmin, xmax); - adjust_ranges(&ranges.ymin, &ranges.ymax, ymin, ymax); - ranges.ymax = (ranges.ymax <= ranges.ymin) ? ranges.ymax + ranges.ymin : ranges.ymax; + std::vector x(cols); + std::vector y(rows); + std::vector u(cols * rows); + std::vector v(cols * rows); + double xmin, xmax, ymin, ymax; - for (col = 0; col < cols; ++col) - { - x[col] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)col / ((double)cols - 1)); + if (depth < 2) + { + fprintf(stderr, "Not enough data for quiver plot\n"); + return 0; + } - for (row = 0; row < rows; ++row) + if (!grm_args_values(plot[plot_i], "x_range", "dd", &xmin, &xmax)) { - if (col == 0) y[row] = ranges.ymin + (ranges.ymax - ranges.ymin) * ((double)row / ((double)rows - 1)); - u[row * cols + col] = file_data[0][col][row]; - v[row * cols + col] = file_data[1][col][row]; + xmin = 0.0; + xmax = cols - 1.0; } - } + if (!grm_args_values(plot[plot_i], "y_range", "dd", &ymin, &ymax)) + { + ymin = 0.0; + ymax = rows - 1.0; + } + adjust_ranges(&ranges.xmin, &ranges.xmax, xmin, xmax); + adjust_ranges(&ranges.ymin, &ranges.ymax, ymin, ymax); + ranges.ymax = (ranges.ymax <= ranges.ymin) ? ranges.ymax + ranges.ymin : ranges.ymax; - grm_args_push(args, "x", "nD", cols, x.data()); - grm_args_push(args, "y", "nD", rows, y.data()); - grm_args_push(args, "u", "nD", cols * row, u.data()); - grm_args_push(args, "v", "nD", cols * row, v.data()); - } - else if (str_equals_any(kind, "hexbin", "shade")) - { - double min_x, min_y, max_x, max_y; - double xmin, xmax, ymin, ymax; - if (cols > 2) fprintf(stderr, "Only the first 2 columns get displayed\n"); + for (col = 0; col < cols; ++col) + { + x[col] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)col / ((double)cols - 1)); - min_x = *std::min_element(std::begin(file_data[depth][0]), std::end(file_data[depth][0])); - max_x = *std::max_element(std::begin(file_data[depth][0]), std::end(file_data[depth][0])); - min_y = *std::min_element(std::begin(file_data[depth][1]), std::end(file_data[depth][1])); - max_y = *std::max_element(std::begin(file_data[depth][1]), std::end(file_data[depth][1])); + for (row = 0; row < rows; ++row) + { + if (col == 0) y[row] = ranges.ymin + (ranges.ymax - ranges.ymin) * ((double)row / ((double)rows - 1)); + u[row * cols + col] = file_data[0][col][row]; + v[row * cols + col] = file_data[1][col][row]; + } + } - if (!grm_args_values(args, "x_range", "dd", &xmin, &xmax)) - { - xmin = min_x; - xmax = max_x; + grm_args_push(plot[plot_i], "x", "nD", cols, x.data()); + grm_args_push(plot[plot_i], "y", "nD", rows, y.data()); + grm_args_push(plot[plot_i], "u", "nD", cols * row, u.data()); + grm_args_push(plot[plot_i], "v", "nD", cols * row, v.data()); } - if (!grm_args_values(args, "y_range", "dd", &ymin, &ymax)) + else if (str_equals_any(kind, "hexbin", "shade")) { - ymin = min_y; - ymax = max_y; - } - adjust_ranges(&ranges.xmin, &ranges.xmax, xmin, xmax); - adjust_ranges(&ranges.ymin, &ranges.ymax, ymin, ymax); - ranges.ymax = (ranges.ymax <= ranges.ymin) ? ranges.ymax + ranges.ymin : ranges.ymax; + double min_x, min_y, max_x, max_y; + double xmin, xmax, ymin, ymax; + if (cols > 2) fprintf(stderr, "Only the first 2 columns get displayed\n"); + + min_x = *std::min_element(std::begin(file_data[depth][0]), std::end(file_data[depth][0])); + max_x = *std::max_element(std::begin(file_data[depth][0]), std::end(file_data[depth][0])); + min_y = *std::min_element(std::begin(file_data[depth][1]), std::end(file_data[depth][1])); + max_y = *std::max_element(std::begin(file_data[depth][1]), std::end(file_data[depth][1])); + + if (!grm_args_values(plot[plot_i], "x_range", "dd", &xmin, &xmax)) + { + xmin = min_x; + xmax = max_x; + } + if (!grm_args_values(plot[plot_i], "y_range", "dd", &ymin, &ymax)) + { + ymin = min_y; + ymax = max_y; + } + adjust_ranges(&ranges.xmin, &ranges.xmax, xmin, xmax); + adjust_ranges(&ranges.ymin, &ranges.ymax, ymin, ymax); + ranges.ymax = (ranges.ymax <= ranges.ymin) ? ranges.ymax + ranges.ymin : ranges.ymax; - for (row = 0; row < rows; row++) + for (row = 0; row < rows; row++) + { + file_data[depth][0][row] = + ranges.xmin + (ranges.xmax - ranges.xmin) * (file_data[depth][0][row] - min_x) / (max_x - min_x); + file_data[depth][1][row] = + ranges.ymin + (ranges.ymax - ranges.ymin) * (file_data[depth][1][row] - min_y) / (max_y - min_y); + } + grm_args_push(plot[plot_i], "x", "nD", rows, file_data[depth][0].data()); + grm_args_push(plot[plot_i], "y", "nD", rows, file_data[depth][1].data()); + } + if (plot_num > 1) { - file_data[depth][0][row] = - ranges.xmin + (ranges.xmax - ranges.xmin) * (file_data[depth][0][row] - min_x) / (max_x - min_x); - file_data[depth][1][row] = - ranges.ymin + (ranges.ymax - ranges.ymin) * (file_data[depth][1][row] - min_y) / (max_y - min_y); + grm_args_push(plot[plot_i], "row", "i", plot_i % divisor); + grm_args_push(plot[plot_i], "col", "i", plot_i / divisor); } - grm_args_push(args, "x", "nD", rows, file_data[depth][0].data()); - grm_args_push(args, "y", "nD", rows, file_data[depth][1].data()); + if (!grm_args_values(plot[plot_i], "grplot", "i", &grplot)) grm_args_push(plot[plot_i], "grplot", "i", 1); } - if (!grm_args_values(args, "grplot", "i", &grplot)) grm_args_push(args, "grplot", "i", 1); + grm_args_push(args, "subplots", "nA", plot_num, plot.data()); grm_merge(args); if (handle != nullptr) diff --git a/lib/grm/src/grm/interaction.cxx b/lib/grm/src/grm/interaction.cxx index 724afb586..906ce3b96 100644 --- a/lib/grm/src/grm/interaction.cxx +++ b/lib/grm/src/grm/interaction.cxx @@ -479,11 +479,14 @@ int grm_input_(const grm_args_t *input_args) max_width_height = grm_max(width, height); logger((stderr, "Using size (%d, %d)\n", width, height)); - auto marginal_heatmap = grm_get_document_root()->querySelectorsAll("marginal_heatmap_plot"); - if (!marginal_heatmap.empty()) + auto marginal_heatmaps = grm_get_document_root()->querySelectorsAll("marginal_heatmap_plot"); + if (!marginal_heatmaps.empty()) { - if (marginal_heatmap[0]->hasAttribute("x_ind")) marginal_heatmap[0]->setAttribute("x_ind", -1); - if (marginal_heatmap[0]->hasAttribute("y_ind")) marginal_heatmap[0]->setAttribute("y_ind", -1); + for (const auto &mheatmap : marginal_heatmaps) + { + if (mheatmap->hasAttribute("x_ind")) mheatmap->setAttribute("x_ind", -1); + if (mheatmap->hasAttribute("y_ind")) mheatmap->setAttribute("y_ind", -1); + } } if (grm_args_values(input_args, "clear_locked_state", "i", &clear_locked_state) && clear_locked_state) @@ -558,6 +561,7 @@ int grm_input_(const grm_args_t *input_args) viewport[2] = static_cast(central_region->getAttribute("viewport_y_min")); viewport[3] = static_cast(central_region->getAttribute("viewport_y_max")); + gr_savestate(); kind = static_cast(subplot_element->getAttribute("_kind")); if (kind == "marginal_heatmap") { @@ -613,8 +617,8 @@ int grm_input_(const grm_args_t *input_args) GRM::Render::processLimits(subplot_element); GRM::Render::processWindow(central_region); - gr_savestate(); + for (const auto &elem : subplot_element->parentElement()->children()) { if (elem->hasAttribute("scale")) @@ -709,15 +713,16 @@ int grm_input_(const grm_args_t *input_args) yind = (int)yind_d; } - auto old_xind = static_cast(marginal_heatmap[0]->getAttribute("x_ind")); - auto old_yind = static_cast(marginal_heatmap[0]->getAttribute("y_ind")); - marginal_heatmap[0]->setAttribute("x_ind", xind); - marginal_heatmap[0]->setAttribute("y_ind", yind); - if (static_cast(marginal_heatmap[0]->getAttribute("marginal_heatmap_kind")) == "line" && + auto marginal_heatmap = subplot_element->querySelectors("marginal_heatmap_plot"); + auto old_xind = static_cast(marginal_heatmap->getAttribute("x_ind")); + auto old_yind = static_cast(marginal_heatmap->getAttribute("y_ind")); + marginal_heatmap->setAttribute("x_ind", xind); + marginal_heatmap->setAttribute("y_ind", yind); + if (static_cast(marginal_heatmap->getAttribute("marginal_heatmap_kind")) == "line" && ((old_xind == -1 || old_yind == -1) && xind != -1 && yind != -1)) - marginal_heatmap[0]->setAttribute("_update_required", true); + marginal_heatmap->setAttribute("_update_required", true); - for (auto &side_region : marginal_heatmap[0]->children()) + for (auto &side_region : marginal_heatmap->children()) { if (side_region->localName() == "side_region") { @@ -790,9 +795,11 @@ int grm_input_(const grm_args_t *input_args) auto panzoom_element = grm_get_render()->createPanzoom(focus_x, focus_y, zoom, zoom); subplot_element->append(panzoom_element); subplot_element->setAttribute("panzoom", true); + gr_savestate(); GRM::Render::processLimits(subplot_element); + gr_restorestate(); } - + gr_restorestate(); return 1; } else if (grm_args_values(input_args, "factor", "d", &factor)) @@ -821,8 +828,11 @@ int grm_input_(const grm_args_t *input_args) auto panzoom_element = grm_get_render()->createPanzoom(focus_x, focus_y, factor, factor); subplot_element->append(panzoom_element); subplot_element->setAttribute("panzoom", true); + gr_savestate(); GRM::Render::processLimits(subplot_element); + gr_restorestate(); } + gr_restorestate(); return 1; } @@ -830,10 +840,10 @@ int grm_input_(const grm_args_t *input_args) grm_args_values(input_args, "y_shift", "i", &yshift) && grm_args_values(input_args, "move_selection", "i", &selection_status)) { + gr_savestate(); GRM::Render::processLimits(subplot_element); GRM::Render::processWindow(central_region); - gr_savestate(); for (const auto &elem : subplot_element->parentElement()->children()) { if (elem->hasAttribute("scale")) @@ -899,8 +909,11 @@ int grm_input_(const grm_args_t *input_args) auto panzoom_element = grm_get_render()->createPanzoom(ndc_xshift, ndc_yshift, 0, 0); subplot_element->append(panzoom_element); subplot_element->setAttribute("panzoom", true); + gr_savestate(); GRM::Render::processLimits(subplot_element); + gr_restorestate(); } + gr_restorestate(); return 1; } else if (grm_args_values(input_args, "x_shift", "i", &xshift) && @@ -939,10 +952,10 @@ int grm_input_(const grm_args_t *input_args) { movable_obj_ref = movable; + gr_savestate(); GRM::Render::processLimits(subplot_element); GRM::Render::processWindow(central_region); - gr_savestate(); for (const auto &elem : subplot_element->parentElement()->children()) { if (elem->hasAttribute("scale")) @@ -965,6 +978,7 @@ int grm_input_(const grm_args_t *input_args) gr_restorestate(); } } + gr_restorestate(); } } @@ -991,7 +1005,9 @@ int grm_input_(const grm_args_t *input_args) auto panzoom_element = grm_get_render()->createPanzoom(focus_x, focus_y, factor_x, factor_y); subplot_element->append(panzoom_element); subplot_element->setAttribute("panzoom", true); + gr_savestate(); GRM::Render::processLimits(subplot_element); + gr_restorestate(); return 1; } @@ -1319,9 +1335,8 @@ err_t get_tooltips_(int mouse_x, int mouse_y, err_t (*tooltip_callback)(int, int return ERROR_NONE; } - GRM::Render::processLimits(subplot_element); - gr_savestate(); + GRM::Render::processLimits(subplot_element); auto central_region = subplot_element->querySelectors("central_region"); if (central_region->hasAttribute("orientation")) orientation = static_cast(central_region->getAttribute("orientation")); diff --git a/lib/grm/src/grm/plot.cxx b/lib/grm/src/grm/plot.cxx index c6baea332..a5d29ad4d 100644 --- a/lib/grm/src/grm/plot.cxx +++ b/lib/grm/src/grm/plot.cxx @@ -289,7 +289,7 @@ static int pre_plot_text_encoding = -1; * flat object that mixes keys of different hierarchies */ const char *valid_root_keys[] = {"plots", "append_plots", "hold_plots", nullptr}; -const char *valid_plot_keys[] = {"clear", "fig_size", "raw", "size", "subplots", "update", nullptr}; +const char *valid_plot_keys[] = {"clear", "raw", "size", "subplots", "update", nullptr}; const char *valid_subplot_keys[] = {"abs_height", "abs_width", @@ -445,7 +445,6 @@ static string_map_entry_t key_to_formats[] = {{"a", "A"}, {"edge_width", "d"}, {"error", "a"}, {"error_bar_style", "i"}, - {"fig_size", "D"}, {"fit_parents_height", "i"}, {"fit_parents_width", "i"}, {"font", "i"}, @@ -632,6 +631,7 @@ err_t plot_init_static_variables(void) static std::shared_ptr getCentralRegion() { auto plot_parent = edit_figure->lastChildElement(); + plot_parent = plot_parent->querySelectors("plot"); for (const auto &child : plot_parent->children()) { if (child->localName() == "central_region") @@ -788,7 +788,7 @@ err_t plot_merge_args(grm_args_t *args, const grm_args_t *merge_args, const char grm_args_clear(current_args); if (cleared_args == nullptr) { - cleared_args = args_set_new(10); /* FIXME: do not use a magic number, use a growable set instead! */ + cleared_args = args_set_new(100); /* FIXME: do not use a magic number, use a growable set instead! */ cleanup_and_set_error_if(cleared_args == nullptr, ERROR_MALLOC); cleanup_and_set_error_if( !args_set_map_insert(key_to_cleared_args, *current_hierarchy_name_ptr, cleared_args), @@ -1069,11 +1069,7 @@ void plot_set_attribute_defaults(grm_args_t *plot_args) logger((stderr, "Set plot attribute defaults\n")); - if (!grm_args_contains(plot_args, "fig_size")) - { - // TODO: Remove this default - args_setdefault(plot_args, "size", "dd", PLOT_DEFAULT_WIDTH, PLOT_DEFAULT_HEIGHT); - } + args_setdefault(plot_args, "size", "dd", PLOT_DEFAULT_WIDTH, PLOT_DEFAULT_HEIGHT); grm_args_values(plot_args, "subplots", "A", ¤t_subplot); while (*current_subplot != nullptr) @@ -1327,7 +1323,8 @@ void plot_process_resample_method(grm_args_t *subplot_args) { int resample_method_flag; auto group = edit_figure->lastChildElement(); - auto central_region = getCentralRegion(); + auto central_region = + (!current_central_region_element.expired()) ? current_central_region_element.lock() : getCentralRegion(); if (!grm_args_values(subplot_args, "resample_method", "i", &resample_method_flag)) { @@ -1350,7 +1347,8 @@ void plot_process_window(grm_args_t *subplot_args) double rotation, tilt; auto group = edit_figure->lastChildElement(); - auto central_region = getCentralRegion(); + auto central_region = + (!current_central_region_element.expired()) ? current_central_region_element.lock() : getCentralRegion(); grm_args_values(subplot_args, "kind", "s", &kind); if (strcmp(kind, "hist") == 0) @@ -1358,16 +1356,18 @@ void plot_process_window(grm_args_t *subplot_args) kind = "histogram"; grm_args_push(subplot_args, "kind", "s", kind); } - if (grm_args_values(subplot_args, "x_log", "i", &x_log)) group->setAttribute("x_log", x_log); - if (grm_args_values(subplot_args, "y_log", "i", &y_log)) group->setAttribute("y_log", y_log); - if (grm_args_values(subplot_args, "z_log", "i", &z_log)) group->setAttribute("z_log", z_log); - if (grm_args_values(subplot_args, "x_flip", "i", &x_flip)) group->setAttribute("x_flip", x_flip); - if (grm_args_values(subplot_args, "y_flip", "i", &y_flip)) group->setAttribute("y_flip", y_flip); - if (grm_args_values(subplot_args, "z_flip", "i", &z_flip)) group->setAttribute("z_flip", z_flip); + auto plot = strcmp(kind, "marginal_heatmap") != 0 ? central_region->parentElement() + : central_region->parentElement()->parentElement(); + if (grm_args_values(subplot_args, "x_log", "i", &x_log)) plot->setAttribute("x_log", x_log); + if (grm_args_values(subplot_args, "y_log", "i", &y_log)) plot->setAttribute("y_log", y_log); + if (grm_args_values(subplot_args, "z_log", "i", &z_log)) plot->setAttribute("z_log", z_log); + if (grm_args_values(subplot_args, "x_flip", "i", &x_flip)) plot->setAttribute("x_flip", x_flip); + if (grm_args_values(subplot_args, "y_flip", "i", &y_flip)) plot->setAttribute("y_flip", y_flip); + if (grm_args_values(subplot_args, "z_flip", "i", &z_flip)) plot->setAttribute("z_flip", z_flip); if (str_equals_any(kind, "wireframe", "surface", "plot3", "scatter3", "trisurface", "volume")) { - group->setAttribute("adjust_z_lim", true); + plot->setAttribute("adjust_z_lim", true); global_render->setSpace3d(central_region, 30.0, 0.0); if (grm_args_values(subplot_args, "rotation", "d", &rotation)) central_region->setAttribute("space_3d_phi", rotation); @@ -1383,9 +1383,8 @@ void plot_process_window(grm_args_t *subplot_args) } if (grm_args_values(subplot_args, "orientation", "s", &orientation)) - getCentralRegion()->setAttribute("orientation", orientation); - if (grm_args_values(subplot_args, "scale", "i", scale)) - global_render->setScale(edit_figure->lastChildElement(), scale); + central_region->setAttribute("orientation", orientation); + if (grm_args_values(subplot_args, "scale", "i", scale)) global_render->setScale(plot, scale); } err_t plot_store_coordinate_ranges(grm_args_t *subplot_args) @@ -1864,7 +1863,7 @@ err_t plot_quiver(grm_args_t *subplot_args) temp->setAttribute("ref_y_axis_location", y_axis_ref); group->append(temp); - global_root->setAttribute("_id", id++); + global_root->setAttribute("_id", ++id); ++current_series; } error = plot_draw_colorbar(subplot_args, 0.0, 256); @@ -2499,7 +2498,7 @@ err_t plot_hexbin(grm_args_t *subplot_args) plot_draw_colorbar(subplot_args, 0.0, 256); - global_root->setAttribute("_id", id++); + global_root->setAttribute("_id", ++id); ++current_series; } @@ -2709,7 +2708,7 @@ err_t plot_marginal_heatmap(grm_args_t *subplot_args) unsigned int num_bins_x, num_bins_y, n; auto group = (!current_dom_element.expired()) ? current_dom_element.lock() : edit_figure->lastChildElement(); - auto subGroup = global_render->querySelectors("marginal_heatmap_plot"); + auto subGroup = group->querySelectors("marginal_heatmap_plot"); if (subGroup == nullptr) { subGroup = global_render->createElement("marginal_heatmap_plot"); @@ -2901,7 +2900,7 @@ err_t plot_surface(grm_args_t *subplot_args) (*context)["z" + str] = z_vec; subGroup->setAttribute("z", "z" + str); - global_root->setAttribute("_id", id++); + global_root->setAttribute("_id", ++id); ++current_series; } plot_draw_axes(subplot_args, 2); @@ -3029,7 +3028,7 @@ err_t plot_scatter3(grm_args_t *subplot_args) } } - global_root->setAttribute("_id", id++); + global_root->setAttribute("_id", ++id); ++current_series; } plot_draw_axes(subplot_args, 2); @@ -3073,7 +3072,7 @@ err_t plot_imshow(grm_args_t *subplot_args) (*context)["z_dims" + str] = shape_vec; subGroup->setAttribute("z_dims", "z_dims" + str); - global_root->setAttribute("_id", id++); + global_root->setAttribute("_id", ++id); ++current_series; } @@ -3487,7 +3486,7 @@ err_t plot_polar_histogram(grm_args_t *subplot_args) { series_group->setAttribute("y_colormap", y_colormap); } - global_root->setAttribute("_id", id++); + global_root->setAttribute("_id", ++id); return ERROR_NONE; } @@ -3550,7 +3549,7 @@ err_t plot_pie(grm_args_t *subplot_args) side_region->setAttribute("location", "top"); side_region->setAttribute("text_is_title", true); } - global_root->setAttribute("_id", id++); + global_root->setAttribute("_id", ++id); return ERROR_NONE; } @@ -3594,7 +3593,7 @@ err_t plot_trisurface(grm_args_t *subplot_args) group->append(temp); - global_root->setAttribute("_id", id++); + global_root->setAttribute("_id", ++id); ++current_series; } plot_draw_axes(subplot_args, 2); @@ -3661,7 +3660,7 @@ err_t plot_tricontour(grm_args_t *subplot_args) if (has_levels) subGroup->setAttribute("levels", num_levels); - global_root->setAttribute("_id", id++); + global_root->setAttribute("_id", ++id); ++current_series; } plot_draw_colorbar(subplot_args, 0.0, 256); @@ -3722,7 +3721,7 @@ err_t plot_shade(grm_args_t *subplot_args) subGroup->setAttribute("y_range_min", y_min); subGroup->setAttribute("y_range_max", y_max); } - global_root->setAttribute("_id", id++); + global_root->setAttribute("_id", ++id); return ERROR_NONE; } @@ -3780,7 +3779,10 @@ err_t plot_draw_axes(grm_args_t *args, unsigned int pass) } else { - group = global_render->getElementsByTagName("coordinate_system")[0]; + if (current_central_region_element_locked) + group = current_central_region_element.lock()->getElementsByTagName("coordinate_system")[0]; + else + group = getCentralRegion()->getElementsByTagName("coordinate_system")[0]; } grm_args_values(args, "kind", "s", &kind); grm_args_values(args, "x_grid", "i", &x_grid); @@ -4230,15 +4232,20 @@ err_t plot_draw_polar_axes(grm_args_t *args) std::shared_ptr group, subGroup; group = (!current_central_region_element.expired()) ? current_central_region_element.lock() : getCentralRegion(); + auto current_central_region_element_locked = current_central_region_element.lock(); - if (global_render->getElementsByTagName("coordinate_system").empty()) + if (!current_central_region_element_locked || + current_central_region_element_locked->getElementsByTagName("coordinate_system").empty()) { subGroup = global_render->createElement("coordinate_system"); group->append(subGroup); } else { - subGroup = global_render->getElementsByTagName("coordinate_system")[0]; + if (current_central_region_element_locked) + subGroup = current_central_region_element_locked->getElementsByTagName("coordinate_system")[0]; + else + subGroup = getCentralRegion()->getElementsByTagName("coordinate_system")[0]; } subGroup->setAttribute("plot_type", "polar"); @@ -4332,7 +4339,7 @@ err_t plot_draw_pie_legend(grm_args_t *subplot_args) grm_args_values(subplot_args, "series", "a", &series); /* series exists always */ int id = static_cast(global_root->getAttribute("_id")); - global_root->setAttribute("_id", id++); + global_root->setAttribute("_id", ++id); std::string labels_key = "labels" + std::to_string(id); std::vector labels_vec(labels, labels + num_labels); @@ -4732,7 +4739,7 @@ err_t classes_polar_histogram(grm_args_t *subplot_args) auto id = static_cast(global_root->getAttribute("_id")); - global_root->setAttribute("_id", id++); + global_root->setAttribute("_id", ++id); auto str = std::to_string(id); grm_args_values(subplot_args, "series", "A", &series); @@ -6379,7 +6386,8 @@ int plot_process_subplot_args(grm_args_t *subplot_args) if (plot_pre_subplot(subplot_args) != ERROR_NONE) return 0; - auto central_region = getCentralRegion(); + auto central_region = + (!current_central_region_element.expired()) ? current_central_region_element.lock() : getCentralRegion(); if (grm_args_values(subplot_args, "keep_aspect_ratio", "i", &keep_aspect_ratio)) { group->setAttribute("keep_aspect_ratio", keep_aspect_ratio); @@ -6584,11 +6592,6 @@ int grm_plot(const grm_args_t *args) // TODO: rename this method so the name dis } } } - if (grm_args_values(edit_plot_args, "fig_size", "dd", &fig_size_x, &fig_size_y)) - { - edit_figure->setAttribute("fig_size_x", fig_size_x); - edit_figure->setAttribute("fig_size_y", fig_size_y); - } if (!edit_figure->hasChildNodes() || !hold_figures || (append_figures && !figure_id_given)) { if (plot_process_grid_arguments(edit_plot_args) != ERROR_NONE) return 0; @@ -6880,7 +6883,7 @@ int grm_iterate_grid(grm::Grid *grid, const std::shared_ptr &paren { for (const auto &element : row) { - if (!processedGridElements.count(element)) + if (!processedGridElements.count(element) && element != nullptr) { processedGridElements.insert(element); auto slice = elementsToPosition.at(element); @@ -6899,19 +6902,30 @@ int grm_plot_helper(grm::GridElement *gridElement, grm::Slice *slice, if (gridElement == nullptr) { - std::cout << "Error: gridElement is nullptr\n"; + std::cout << "Error: grid element is nullptr\n"; return 0; } if (!gridElement->isGrid()) { + const char *kind; grm_args_t **current_subplot_args = &gridElement->plot_args; auto layoutGridElement = global_render->createLayoutGridElement(*gridElement, *slice); parentDomElement->append(layoutGridElement); auto plot = global_render->createPlot(plotId); auto central_region = global_render->createCentralRegion(); layoutGridElement->append(plot); - plot->append(central_region); + grm_args_values(*current_subplot_args, "kind", "s", &kind); + if (strcmp(kind, "marginal_heatmap") == 0) + { + auto marginal_heatmap = global_render->createElement("marginal_heatmap_plot"); + plot->append(marginal_heatmap); + marginal_heatmap->append(central_region); + } + else + { + plot->append(central_region); + } current_dom_element = plot; current_central_region_element = central_region; @@ -6963,38 +6977,25 @@ int get_free_id_from_figure_elements() std::shared_ptr get_subplot_from_ndc_point_using_dom_helper(std::shared_ptr element, double x, double y) { - bool elementIsSubplotGroup = + bool element_is_plot_group = (element->hasAttribute("plot_group") && static_cast(element->getAttribute("plot_group"))); - if (element->localName() == "layout_grid_element" || elementIsSubplotGroup) + if (element_is_plot_group) { double viewport[4]; - viewport[0] = static_cast(element->getAttribute("viewport_x_min")); - viewport[1] = static_cast(element->getAttribute("viewport_x_max")); - viewport[2] = static_cast(element->getAttribute("viewport_y_min")); - viewport[3] = static_cast(element->getAttribute("viewport_y_max")); - if (elementIsSubplotGroup) - { - auto central_region = element->querySelectors("central_region"); - viewport[0] = static_cast(central_region->getAttribute("viewport_x_min")); - viewport[1] = static_cast(central_region->getAttribute("viewport_x_max")); - viewport[2] = static_cast(central_region->getAttribute("viewport_y_min")); - viewport[3] = static_cast(central_region->getAttribute("viewport_y_max")); - } - if (viewport[0] <= x && x <= viewport[1] && viewport[2] <= y && y <= viewport[3]) - { - return element; - } + auto central_region = element->querySelectors("central_region"); + viewport[0] = static_cast(central_region->getAttribute("viewport_x_min")); + viewport[1] = static_cast(central_region->getAttribute("viewport_x_max")); + viewport[2] = static_cast(central_region->getAttribute("viewport_y_min")); + viewport[3] = static_cast(central_region->getAttribute("viewport_y_max")); + if (viewport[0] <= x && x <= viewport[1] && viewport[2] <= y && y <= viewport[3]) return element; } - if (element->localName() == "layout_grid") + if (element->localName() == "layout_grid" || element->localName() == "layout_grid_element") { for (const auto &child : element->children()) { std::shared_ptr subplot_element = get_subplot_from_ndc_point_using_dom_helper(child, x, y); - if (subplot_element != nullptr) - { - return subplot_element; - } + if (subplot_element != nullptr) return subplot_element; } } @@ -7009,10 +7010,7 @@ std::shared_ptr get_subplot_from_ndc_point_using_dom(double x, dou for (const auto &child : edit_figure->children()) { std::shared_ptr subplot_element = get_subplot_from_ndc_point_using_dom_helper(child, x, y); - if (subplot_element != nullptr) - { - return subplot_element; - } + if (subplot_element != nullptr) return subplot_element; } } @@ -7102,19 +7100,17 @@ int get_focus_and_factor_from_dom(const int x1, const int y1, const int x2, cons ndc_box_x[3] = ndc_right; ndc_box_y[3] = ndc_top; subplot_element = get_subplot_from_ndc_points_using_dom(array_size(ndc_box_x), ndc_box_x, ndc_box_y); - if (subplot_element == nullptr) - { - return 0; - } + if (subplot_element == nullptr) return 0; + auto central_region = subplot_element->querySelectors("central_region"); viewport[0] = static_cast(central_region->getAttribute("viewport_x_min")); viewport[1] = static_cast(central_region->getAttribute("viewport_x_max")); viewport[2] = static_cast(central_region->getAttribute("viewport_y_min")); viewport[3] = static_cast(central_region->getAttribute("viewport_y_max")); - wswindow[0] = static_cast(subplot_element->parentElement()->getAttribute("ws_window_x_min")); - wswindow[1] = static_cast(subplot_element->parentElement()->getAttribute("ws_window_x_max")); - wswindow[2] = static_cast(subplot_element->parentElement()->getAttribute("ws_window_y_min")); - wswindow[3] = static_cast(subplot_element->parentElement()->getAttribute("ws_window_y_max")); + wswindow[0] = static_cast(edit_figure->getAttribute("ws_window_x_min")); + wswindow[1] = static_cast(edit_figure->getAttribute("ws_window_x_max")); + wswindow[2] = static_cast(edit_figure->getAttribute("ws_window_y_min")); + wswindow[3] = static_cast(edit_figure->getAttribute("ws_window_y_max")); *factor_x = abs(x1 - x2) / (width * (viewport[1] - viewport[0]) / (wswindow[1] - wswindow[0])); *factor_y = abs(y1 - y2) / (height * (viewport[3] - viewport[2]) / (wswindow[3] - wswindow[2])); From dfb90e8ee0cefcbd989715dbdc178dbc3af1c6ce Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Tue, 7 Jan 2025 14:06:12 +0100 Subject: [PATCH 02/16] GKS: take respect of current nominal size setting --- lib/gks/pdf.c | 22 +++++++++++++++------- lib/gks/plugin/aggplugin.cxx | 23 +++++++++++------------ lib/gks/plugin/cairoplugin.c | 23 +++++++++++------------ lib/gks/plugin/gsplugin.c | 16 ++++++++++------ lib/gks/plugin/pgfplugin.c | 16 ++++++++++------ lib/gks/plugin/qtplugin_impl.cxx | 21 +++++++++++++++------ lib/gks/plugin/svgplugin.c | 16 ++++++++++------ lib/gks/ps.c | 16 ++++++++++------ lib/gks/quartz/GKSView.m | 18 +++++++++--------- 9 files changed, 101 insertions(+), 70 deletions(-) diff --git a/lib/gks/pdf.c b/lib/gks/pdf.c index 19434aabb..c402ba484 100644 --- a/lib/gks/pdf.c +++ b/lib/gks/pdf.c @@ -778,14 +778,11 @@ static void set_xform(void) p->width = nint(p->a * (p->window[1] - p->window[0])); p->height = nint(p->c * (p->window[3] - p->window[2])); + if (gkss->nominal_size > 0) - { - p->nominal_size = 558 / 500.0 * gkss->nominal_size; - } + p->nominal_size = 558 / 500.0 * gkss->nominal_size; else - { - p->nominal_size = min(p->width, p->height) / 500.0; - } + p->nominal_size = min(p->width, p->height) / 500.0; } static void seg_xform(double *x, double *y) @@ -873,7 +870,10 @@ static void open_ws(int fd, int wstype) p->viewport[0] = p->viewport[2] = 0; p->viewport[1] = p->viewport[3] = 0.1984; p->width = p->height = 558; - p->nominal_size = 558 / 500.0; + if (gkss->nominal_size > 0) + p->nominal_size = 558 / 500.0 * gkss->nominal_size; + else + p->nominal_size = min(p->width, p->height) / 500.0; p->empty = 1; @@ -2227,6 +2227,14 @@ void gks_drv_js( init_norm_xform(); break; + case 109: + /* set nominal size */ + if (gkss->nominal_size > 0) + p->nominal_size = 558 / 500.0 * gkss->nominal_size; + else + p->nominal_size = min(p->width, p->height) / 500.0; + break; + case 203: /* set transparency */ p->alpha = (int)(r1[0] * 255.0); diff --git a/lib/gks/plugin/aggplugin.cxx b/lib/gks/plugin/aggplugin.cxx index 94d2bb38f..fb8037118 100644 --- a/lib/gks/plugin/aggplugin.cxx +++ b/lib/gks/plugin/aggplugin.cxx @@ -1445,13 +1445,9 @@ void gks_aggplugin(int fctid, int dx, int dy, int dimx, int *i_arr, int len_f_ar p->viewport[1] = (double)p->width * p->mw / p->w; p->viewport[3] = (double)p->height * p->mh / p->h; if (gkss->nominal_size > 0) - { - p->nominal_size = p->dpi / 100 * gkss->nominal_size; - } + p->nominal_size = p->dpi / 100 * gkss->nominal_size; else - { - p->nominal_size = min(p->width, p->height) / 500.0; - } + p->nominal_size = min(p->width, p->height) / 500.0; init_colors(); open_page(); @@ -1601,19 +1597,22 @@ void gks_aggplugin(int fctid, int dx, int dy, int dimx, int *i_arr, int len_f_ar p->width = (int)(p->viewport[1] * p->w / p->mw); p->height = (int)(p->viewport[3] * p->h / p->mh); if (gkss->nominal_size > 0) - { - p->nominal_size = p->dpi / 100 * gkss->nominal_size; - } + p->nominal_size = p->dpi / 100 * gkss->nominal_size; else - { - p->nominal_size = min(p->width, p->height) / 500.0; - } + p->nominal_size = min(p->width, p->height) / 500.0; close_page(); open_page(); } break; + case 109: + if (gkss->nominal_size > 0) + p->nominal_size = p->dpi / 100 * gkss->nominal_size; + else + p->nominal_size = min(p->width, p->height) / 500.0; + break; + case 203: /* set transparency */ set_transparency(f_arr_1[0]); diff --git a/lib/gks/plugin/cairoplugin.c b/lib/gks/plugin/cairoplugin.c index 1d78c449b..52c64702d 100644 --- a/lib/gks/plugin/cairoplugin.c +++ b/lib/gks/plugin/cairoplugin.c @@ -2337,13 +2337,9 @@ void gks_cairoplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, doub p->mh = p->h * 2.54 / 100 / p->dpi; resize(width, height); if (gkss->nominal_size > 0) - { - p->nominal_size = 2400 / 500.0 * gkss->nominal_size; - } + p->nominal_size = 2400 / 500.0 * gkss->nominal_size; else - { - p->nominal_size = min(p->width, p->height) / 500.0; - } + p->nominal_size = min(p->width, p->height) / 500.0; } else if (p->wtype == 150) { @@ -2589,13 +2585,9 @@ void gks_cairoplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, doub p->width = p->viewport[1] * p->w / p->mw; p->height = p->viewport[3] * p->h / p->mh; if (gkss->nominal_size > 0) - { - p->nominal_size = 2400 / 500.0 * gkss->nominal_size; - } + p->nominal_size = 2400 / 500.0 * gkss->nominal_size; else - { - p->nominal_size = min(p->width, p->height) / 500.0; - } + p->nominal_size = min(p->width, p->height) / 500.0; } close_page(); open_page(); @@ -2607,6 +2599,13 @@ void gks_cairoplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, doub unlock(); break; + case 109: + if (gkss->nominal_size > 0) + p->nominal_size = 2400 / 500.0 * gkss->nominal_size; + else + p->nominal_size = min(p->width, p->height) / 500.0; + break; + case 203: /* set transparency */ lock(); diff --git a/lib/gks/plugin/gsplugin.c b/lib/gks/plugin/gsplugin.c index 58a57b263..46ca2e903 100644 --- a/lib/gks/plugin/gsplugin.c +++ b/lib/gks/plugin/gsplugin.c @@ -503,13 +503,9 @@ static void set_xform(double *wn, double *vp) p->width = p->a; p->height = p->c; if (gkss->nominal_size > 0) - { - p->nominal_size = 72.0 / 600 * gkss->nominal_size; - } + p->nominal_size = 72.0 / 600 * gkss->nominal_size; else - { - p->nominal_size = MIN(p->width, p->height) / 500.0 * 72 / 600; - } + p->nominal_size = MIN(p->width, p->height) / 500.0 * 72 / 600; p->stroke = 0; } @@ -2415,6 +2411,14 @@ void gks_gsplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, double init_norm_xform(); break; + /* set nominal size */ + case 109: + if (gkss->nominal_size > 0) + p->nominal_size = 72.0 / 600 * gkss->nominal_size; + else + p->nominal_size = MIN(p->width, p->height) / 500.0 * 72 / 600; + break; + default:; } } diff --git a/lib/gks/plugin/pgfplugin.c b/lib/gks/plugin/pgfplugin.c index 73e6495d3..29660cada 100644 --- a/lib/gks/plugin/pgfplugin.c +++ b/lib/gks/plugin/pgfplugin.c @@ -1688,13 +1688,9 @@ void gks_pgfplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, double p->width = p->viewport[1] * WIDTH / MWIDTH; p->height = p->viewport[3] * HEIGHT / MHEIGHT; if (gkss->nominal_size > 0) - { - p->nominal_size = gkss->nominal_size; - } + p->nominal_size = gkss->nominal_size; else - { - p->nominal_size = min(p->width, p->height) / 500.0; - } + p->nominal_size = min(p->width, p->height) / 500.0; set_xform(); init_norm_xform(); @@ -1702,6 +1698,14 @@ void gks_pgfplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, double } break; + case 109: + /* set nominal size */ + if (gkss->nominal_size > 0) + p->nominal_size = gkss->nominal_size; + else + p->nominal_size = min(p->width, p->height) / 500.0; + break; + case 203: /* set transparency */ set_transparency(r1[0]); diff --git a/lib/gks/plugin/qtplugin_impl.cxx b/lib/gks/plugin/qtplugin_impl.cxx index 758e0d05b..159380119 100644 --- a/lib/gks/plugin/qtplugin_impl.cxx +++ b/lib/gks/plugin/qtplugin_impl.cxx @@ -228,13 +228,9 @@ static void resize_window(void) p->mheight = (double)p->height / p->device_dpi_y * 0.0254; } if (gkss->nominal_size > 0) - { - p->nominal_size = gkss->nominal_size; - } + p->nominal_size = gkss->nominal_size; else - { - p->nominal_size = min(p->width, p->height) / 500.0; - } + p->nominal_size = min(p->width, p->height) / 500.0; if (p->pixmap) { @@ -1528,6 +1524,12 @@ static void memory_plugin_dl_render(int fctid, int dx, int dy, int dimx, int *ia p->viewport[3] = r2[1]; } break; + case 109: + if (gkss->nominal_size > 0) + p->nominal_size = gkss->nominal_size; + else + p->nominal_size = min(p->width, p->height) / 500.0; + break; } if (p->memory_plugin_initialised) { @@ -1676,6 +1678,13 @@ static void qt_dl_render(int fctid, int dx, int dy, int dimx, int *ia, int lr1, init_norm_xform(); break; + case 109: + if (gkss->nominal_size > 0) + p->nominal_size = gkss->nominal_size; + else + p->nominal_size = min(p->width, p->height) / 500.0; + break; + case 203: p->transparency = (int)(r1[0] * 255); break; diff --git a/lib/gks/plugin/svgplugin.c b/lib/gks/plugin/svgplugin.c index 498526cac..77c102da3 100644 --- a/lib/gks/plugin/svgplugin.c +++ b/lib/gks/plugin/svgplugin.c @@ -383,13 +383,9 @@ static void resize_window(void) p->width = nint((p->viewport[1] - p->viewport[0]) / MWIDTH * WIDTH); p->height = nint((p->viewport[3] - p->viewport[2]) / MHEIGHT * HEIGHT); if (gkss->nominal_size > 0) - { - p->nominal_size = gkss->nominal_size; - } + p->nominal_size = 2000 / 500.0 * gkss->nominal_size; else - { - p->nominal_size = min(p->width, p->height) / 500.0; - } + p->nominal_size = min(p->width, p->height) / 500.0; } static void draw_marker(double xn, double yn, int mtype, double mscale, int mcolor) @@ -1816,6 +1812,14 @@ void gks_drv_js( init_norm_xform(); break; + /* set nominal size */ + case 109: + if (gkss->nominal_size > 0) + p->nominal_size = 2000 / 500.0 * gkss->nominal_size; + else + p->nominal_size = min(p->width, p->height) / 500.0; + break; + case 203: /* set transparency */ p->transparency = r1[0]; diff --git a/lib/gks/ps.c b/lib/gks/ps.c index 66a193593..f5f0a0818 100644 --- a/lib/gks/ps.c +++ b/lib/gks/ps.c @@ -452,13 +452,9 @@ static void set_xform(double *wn, double *vp) p->width = p->a; p->height = p->c; if (gkss->nominal_size > 0) - { - p->nominal_size = 72.0 / 600 * gkss->nominal_size; - } + p->nominal_size = 72.0 / 600 * gkss->nominal_size; else - { - p->nominal_size = MIN(p->width, p->height) / 500.0 * 72 / 600; - } + p->nominal_size = MIN(p->width, p->height) / 500.0 * 72 / 600; p->stroke = 0; } @@ -2239,6 +2235,14 @@ void gks_drv_ps(int fctid, int dx, int dy, int dimx, int *ia, int lr1, double *r init_norm_xform(); break; + /* set nominal size */ + case 109: + if (gkss->nominal_size > 0) + p->nominal_size = 72.0 / 600 * gkss->nominal_size; + else + p->nominal_size = MIN(p->width, p->height) / 500.0 * 72 / 600; + break; + default:; } } diff --git a/lib/gks/quartz/GKSView.m b/lib/gks/quartz/GKSView.m index efd591ebf..c8fc969b7 100644 --- a/lib/gks/quartz/GKSView.m +++ b/lib/gks/quartz/GKSView.m @@ -393,13 +393,9 @@ - (void)interp:(char *)str p->width = [self bounds].size.width; p->height = [self bounds].size.height; if (gkss->nominal_size > 0) - { - p->nominal_size = min(p->width, p->height) / 500.0; - } + p->nominal_size = gkss->nominal_size; else - { - p->nominal_size = 1; - } + p->nominal_size = min(p->width, p->height) / 500.0; p->swidth = NSMaxX([[[NSScreen screens] objectAtIndex:0] frame]); p->sheight = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]); @@ -593,6 +589,10 @@ - (void)interp:(char *)str case 109: gkss->nominal_size = f_arr_1[0]; + if (gkss->nominal_size > 0) + p->nominal_size = gkss->nominal_size; + else + p->nominal_size = min(p->width, p->height) / 500.0; break; case 200: @@ -1114,9 +1114,9 @@ - (void)resize_window p->width = width; p->height = height; if (gkss->nominal_size > 0) - { - p->nominal_size = min(p->width, p->height) / 500.0; - } + p->nominal_size = gkss->nominal_size; + else + p->nominal_size = min(p->width, p->height) / 500.0; [self setNeedsDisplay:YES]; [[self window] setFrame:rect display:YES]; From d702c1f8b7b2e73540c5a1c2d2fddb85809d73bc Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Wed, 8 Jan 2025 11:09:04 +0100 Subject: [PATCH 03/16] GKS: set the nominal size depending on the workstation type --- lib/gks/plugin/cairoplugin.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/gks/plugin/cairoplugin.c b/lib/gks/plugin/cairoplugin.c index 52c64702d..03c8d8239 100644 --- a/lib/gks/plugin/cairoplugin.c +++ b/lib/gks/plugin/cairoplugin.c @@ -2276,7 +2276,10 @@ void gks_cairoplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, doub p->h = 4650; p->dpi = 600; resize(2400, 2400); - p->nominal_size = 2400 / 500.0; + if (gkss->nominal_size > 0) + p->nominal_size = 2400 / 500.0 * gkss->nominal_size; + else + p->nominal_size = 2400 / 500.0; } else if (p->wtype == 143) { @@ -2349,7 +2352,10 @@ void gks_cairoplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, doub p->h = 420; p->dpi = 100; resize(400, 400); - p->nominal_size = 0.8; + if (gkss->nominal_size > 0) + p->nominal_size = 400 / 500.0 * gkss->nominal_size; + else + p->nominal_size = 0.8; } else { @@ -2359,7 +2365,10 @@ void gks_cairoplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, doub p->h = 768; p->dpi = 100; resize(500, 500); - p->nominal_size = 1; + if (gkss->nominal_size > 0) + p->nominal_size = gkss->nominal_size; + else + p->nominal_size = 1; } p->max_points = MAX_POINTS; From 7fdedd70373d92e9b6b82eeb3f13e1ea3b17f8cf Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Fri, 10 Jan 2025 09:33:03 +0100 Subject: [PATCH 04/16] GKS: interpret nominal size as scale factor --- lib/gks/pdf.c | 18 +++++---------- lib/gks/plugin/aggplugin.cxx | 18 +++++---------- lib/gks/plugin/cairoplugin.c | 36 ++++++++++-------------------- lib/gks/plugin/gsplugin.c | 12 ++++------ lib/gks/plugin/pgfplugin.c | 14 +++++------- lib/gks/plugin/qtplugin_impl.cxx | 38 +++++++++----------------------- lib/gks/plugin/svgplugin.c | 13 +++++------ lib/gks/ps.c | 12 ++++------ lib/gks/quartz/GKSView.m | 18 +++++---------- 9 files changed, 59 insertions(+), 120 deletions(-) diff --git a/lib/gks/pdf.c b/lib/gks/pdf.c index c402ba484..d81bf1669 100644 --- a/lib/gks/pdf.c +++ b/lib/gks/pdf.c @@ -779,10 +779,8 @@ static void set_xform(void) p->width = nint(p->a * (p->window[1] - p->window[0])); p->height = nint(p->c * (p->window[3] - p->window[2])); - if (gkss->nominal_size > 0) - p->nominal_size = 558 / 500.0 * gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; } static void seg_xform(double *x, double *y) @@ -870,10 +868,8 @@ static void open_ws(int fd, int wstype) p->viewport[0] = p->viewport[2] = 0; p->viewport[1] = p->viewport[3] = 0.1984; p->width = p->height = 558; - if (gkss->nominal_size > 0) - p->nominal_size = 558 / 500.0 * gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; p->empty = 1; @@ -2229,10 +2225,8 @@ void gks_drv_js( case 109: /* set nominal size */ - if (gkss->nominal_size > 0) - p->nominal_size = 558 / 500.0 * gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; break; case 203: diff --git a/lib/gks/plugin/aggplugin.cxx b/lib/gks/plugin/aggplugin.cxx index fb8037118..955616c31 100644 --- a/lib/gks/plugin/aggplugin.cxx +++ b/lib/gks/plugin/aggplugin.cxx @@ -1444,10 +1444,8 @@ void gks_aggplugin(int fctid, int dx, int dy, int dimx, int *i_arr, int len_f_ar p->viewport[0] = p->viewport[2] = 0; p->viewport[1] = (double)p->width * p->mw / p->w; p->viewport[3] = (double)p->height * p->mh / p->h; - if (gkss->nominal_size > 0) - p->nominal_size = p->dpi / 100 * gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= p->dpi / 100 * gkss->nominal_size; init_colors(); open_page(); @@ -1596,10 +1594,8 @@ void gks_aggplugin(int fctid, int dx, int dy, int dimx, int *i_arr, int len_f_ar p->width = (int)(p->viewport[1] * p->w / p->mw); p->height = (int)(p->viewport[3] * p->h / p->mh); - if (gkss->nominal_size > 0) - p->nominal_size = p->dpi / 100 * gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= p->dpi / 100 * gkss->nominal_size; close_page(); open_page(); @@ -1607,10 +1603,8 @@ void gks_aggplugin(int fctid, int dx, int dy, int dimx, int *i_arr, int len_f_ar break; case 109: - if (gkss->nominal_size > 0) - p->nominal_size = p->dpi / 100 * gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= p->dpi / 100 * gkss->nominal_size; break; case 203: diff --git a/lib/gks/plugin/cairoplugin.c b/lib/gks/plugin/cairoplugin.c index 03c8d8239..02bc0f658 100644 --- a/lib/gks/plugin/cairoplugin.c +++ b/lib/gks/plugin/cairoplugin.c @@ -2276,10 +2276,8 @@ void gks_cairoplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, doub p->h = 4650; p->dpi = 600; resize(2400, 2400); - if (gkss->nominal_size > 0) - p->nominal_size = 2400 / 500.0 * gkss->nominal_size; - else - p->nominal_size = 2400 / 500.0; + p->nominal_size = 2400 / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; } else if (p->wtype == 143) { @@ -2339,10 +2337,8 @@ void gks_cairoplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, doub p->mw = p->w * 2.54 / 100 / p->dpi; p->mh = p->h * 2.54 / 100 / p->dpi; resize(width, height); - if (gkss->nominal_size > 0) - p->nominal_size = 2400 / 500.0 * gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; } else if (p->wtype == 150) { @@ -2352,10 +2348,8 @@ void gks_cairoplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, doub p->h = 420; p->dpi = 100; resize(400, 400); - if (gkss->nominal_size > 0) - p->nominal_size = 400 / 500.0 * gkss->nominal_size; - else - p->nominal_size = 0.8; + p->nominal_size = 400 / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; } else { @@ -2365,10 +2359,8 @@ void gks_cairoplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, doub p->h = 768; p->dpi = 100; resize(500, 500); - if (gkss->nominal_size > 0) - p->nominal_size = gkss->nominal_size; - else - p->nominal_size = 1; + p->nominal_size = 1; + if (gkss->nominal_size > 0) p->nominal_size = gkss->nominal_size; } p->max_points = MAX_POINTS; @@ -2593,10 +2585,8 @@ void gks_cairoplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, doub { p->width = p->viewport[1] * p->w / p->mw; p->height = p->viewport[3] * p->h / p->mh; - if (gkss->nominal_size > 0) - p->nominal_size = 2400 / 500.0 * gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; } close_page(); open_page(); @@ -2609,10 +2599,8 @@ void gks_cairoplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, doub break; case 109: - if (gkss->nominal_size > 0) - p->nominal_size = 2400 / 500.0 * gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; break; case 203: diff --git a/lib/gks/plugin/gsplugin.c b/lib/gks/plugin/gsplugin.c index 46ca2e903..06ff63fc7 100644 --- a/lib/gks/plugin/gsplugin.c +++ b/lib/gks/plugin/gsplugin.c @@ -502,10 +502,8 @@ static void set_xform(double *wn, double *vp) p->width = p->a; p->height = p->c; - if (gkss->nominal_size > 0) - p->nominal_size = 72.0 / 600 * gkss->nominal_size; - else - p->nominal_size = MIN(p->width, p->height) / 500.0 * 72 / 600; + p->nominal_size = MIN(p->width, p->height) / 500.0 * 72 / 600; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; p->stroke = 0; } @@ -2413,10 +2411,8 @@ void gks_gsplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, double /* set nominal size */ case 109: - if (gkss->nominal_size > 0) - p->nominal_size = 72.0 / 600 * gkss->nominal_size; - else - p->nominal_size = MIN(p->width, p->height) / 500.0 * 72 / 600; + p->nominal_size = MIN(p->width, p->height) / 500.0 * 72 / 600; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; break; default:; diff --git a/lib/gks/plugin/pgfplugin.c b/lib/gks/plugin/pgfplugin.c index 29660cada..7ed338ab7 100644 --- a/lib/gks/plugin/pgfplugin.c +++ b/lib/gks/plugin/pgfplugin.c @@ -1494,6 +1494,8 @@ void gks_pgfplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, double p->height = 500; p->width = 500; p->nominal_size = 1; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; + p->window[0] = p->window[2] = 0.0; p->window[1] = p->window[3] = 1.0; p->viewport[0] = p->viewport[2] = 0; @@ -1687,10 +1689,8 @@ void gks_pgfplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, double p->width = p->viewport[1] * WIDTH / MWIDTH; p->height = p->viewport[3] * HEIGHT / MHEIGHT; - if (gkss->nominal_size > 0) - p->nominal_size = gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; set_xform(); init_norm_xform(); @@ -1700,10 +1700,8 @@ void gks_pgfplugin(int fctid, int dx, int dy, int dimx, int *ia, int lr1, double case 109: /* set nominal size */ - if (gkss->nominal_size > 0) - p->nominal_size = gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; break; case 203: diff --git a/lib/gks/plugin/qtplugin_impl.cxx b/lib/gks/plugin/qtplugin_impl.cxx index 159380119..d1112fb52 100644 --- a/lib/gks/plugin/qtplugin_impl.cxx +++ b/lib/gks/plugin/qtplugin_impl.cxx @@ -227,10 +227,8 @@ static void resize_window(void) p->height = 2; p->mheight = (double)p->height / p->device_dpi_y * 0.0254; } - if (gkss->nominal_size > 0) - p->nominal_size = gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; if (p->pixmap) { @@ -278,14 +276,8 @@ static void set_xform(void) p->c = h / (p->window[2] - p->window[3]); p->d = y + p->window[2] * p->c; - if (gkss->nominal_size > 0) - { - p->nominal_size = gkss->nominal_size; - } - else - { - p->nominal_size = min(p->width, p->height) / 500.0; - } + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; } static void seg_xform(double *x, double *y) @@ -1525,10 +1517,8 @@ static void memory_plugin_dl_render(int fctid, int dx, int dy, int dimx, int *ia } break; case 109: - if (gkss->nominal_size > 0) - p->nominal_size = gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; break; } if (p->memory_plugin_initialised) @@ -1679,10 +1669,8 @@ static void qt_dl_render(int fctid, int dx, int dy, int dimx, int *ia, int lr1, break; case 109: - if (gkss->nominal_size > 0) - p->nominal_size = gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; break; case 203: @@ -1912,14 +1900,8 @@ static int get_paint_device(void) p->device_dpi_y = device->physicalDpiY(); p->mwidth = (double)p->width / p->device_dpi_x * 0.0254; p->mheight = (double)p->height / p->device_dpi_y * 0.0254; - if (gkss->nominal_size > 0) - { - p->nominal_size = gkss->nominal_size; - } - else - { - p->nominal_size = min(p->width, p->height) / 500.0; - } + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; return 0; } diff --git a/lib/gks/plugin/svgplugin.c b/lib/gks/plugin/svgplugin.c index 77c102da3..a65cb7076 100644 --- a/lib/gks/plugin/svgplugin.c +++ b/lib/gks/plugin/svgplugin.c @@ -382,10 +382,8 @@ static void resize_window(void) { p->width = nint((p->viewport[1] - p->viewport[0]) / MWIDTH * WIDTH); p->height = nint((p->viewport[3] - p->viewport[2]) / MHEIGHT * HEIGHT); - if (gkss->nominal_size > 0) - p->nominal_size = 2000 / 500.0 * gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; } static void draw_marker(double xn, double yn, int mtype, double mscale, int mcolor) @@ -1638,6 +1636,7 @@ void gks_drv_js( p->viewport[1] = (double)p->width * MWIDTH / WIDTH; p->viewport[3] = (double)p->height * MHEIGHT / HEIGHT; p->nominal_size = 2000 / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; p->stream = svg_alloc_stream(); @@ -1814,10 +1813,8 @@ void gks_drv_js( /* set nominal size */ case 109: - if (gkss->nominal_size > 0) - p->nominal_size = 2000 / 500.0 * gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; break; case 203: diff --git a/lib/gks/ps.c b/lib/gks/ps.c index f5f0a0818..f0a83053d 100644 --- a/lib/gks/ps.c +++ b/lib/gks/ps.c @@ -451,10 +451,8 @@ static void set_xform(double *wn, double *vp) p->width = p->a; p->height = p->c; - if (gkss->nominal_size > 0) - p->nominal_size = 72.0 / 600 * gkss->nominal_size; - else - p->nominal_size = MIN(p->width, p->height) / 500.0 * 72 / 600; + p->nominal_size = MIN(p->width, p->height) / 500.0 * 72 / 600; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; p->stroke = 0; } @@ -2237,10 +2235,8 @@ void gks_drv_ps(int fctid, int dx, int dy, int dimx, int *ia, int lr1, double *r /* set nominal size */ case 109: - if (gkss->nominal_size > 0) - p->nominal_size = 72.0 / 600 * gkss->nominal_size; - else - p->nominal_size = MIN(p->width, p->height) / 500.0 * 72 / 600; + p->nominal_size = MIN(p->width, p->height) / 500.0 * 72 / 600; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; break; default:; diff --git a/lib/gks/quartz/GKSView.m b/lib/gks/quartz/GKSView.m index c8fc969b7..6661749f9 100644 --- a/lib/gks/quartz/GKSView.m +++ b/lib/gks/quartz/GKSView.m @@ -392,10 +392,8 @@ - (void)interp:(char *)str p->width = [self bounds].size.width; p->height = [self bounds].size.height; - if (gkss->nominal_size > 0) - p->nominal_size = gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; p->swidth = NSMaxX([[[NSScreen screens] objectAtIndex:0] frame]); p->sheight = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]); @@ -589,10 +587,8 @@ - (void)interp:(char *)str case 109: gkss->nominal_size = f_arr_1[0]; - if (gkss->nominal_size > 0) - p->nominal_size = gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; break; case 200: @@ -1113,10 +1109,8 @@ - (void)resize_window p->width = width; p->height = height; - if (gkss->nominal_size > 0) - p->nominal_size = gkss->nominal_size; - else - p->nominal_size = min(p->width, p->height) / 500.0; + p->nominal_size = min(p->width, p->height) / 500.0; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; [self setNeedsDisplay:YES]; [[self window] setFrame:rect display:YES]; From 7a8384bd0ee914cc365beba8a8852a45dffe79a1 Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Fri, 10 Jan 2025 13:16:47 +0100 Subject: [PATCH 05/16] GKS: correct scale factor for nominal size --- lib/gks/plugin/aggplugin.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/gks/plugin/aggplugin.cxx b/lib/gks/plugin/aggplugin.cxx index 955616c31..413c82f4c 100644 --- a/lib/gks/plugin/aggplugin.cxx +++ b/lib/gks/plugin/aggplugin.cxx @@ -1445,7 +1445,7 @@ void gks_aggplugin(int fctid, int dx, int dy, int dimx, int *i_arr, int len_f_ar p->viewport[1] = (double)p->width * p->mw / p->w; p->viewport[3] = (double)p->height * p->mh / p->h; p->nominal_size = min(p->width, p->height) / 500.0; - if (gkss->nominal_size > 0) p->nominal_size *= p->dpi / 100 * gkss->nominal_size; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; init_colors(); open_page(); @@ -1595,7 +1595,7 @@ void gks_aggplugin(int fctid, int dx, int dy, int dimx, int *i_arr, int len_f_ar p->width = (int)(p->viewport[1] * p->w / p->mw); p->height = (int)(p->viewport[3] * p->h / p->mh); p->nominal_size = min(p->width, p->height) / 500.0; - if (gkss->nominal_size > 0) p->nominal_size *= p->dpi / 100 * gkss->nominal_size; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; close_page(); open_page(); @@ -1604,7 +1604,7 @@ void gks_aggplugin(int fctid, int dx, int dy, int dimx, int *i_arr, int len_f_ar case 109: p->nominal_size = min(p->width, p->height) / 500.0; - if (gkss->nominal_size > 0) p->nominal_size *= p->dpi / 100 * gkss->nominal_size; + if (gkss->nominal_size > 0) p->nominal_size *= gkss->nominal_size; break; case 203: From 4d993d1ddd7db6476249676b88779dfb5d978310 Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Mon, 13 Jan 2025 10:13:57 +0100 Subject: [PATCH 06/16] [GR3] Fix the context struct initializer in `NO_THREADS` mode --- lib/gr3/gr3.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/gr3/gr3.c b/lib/gr3/gr3.c index 355269e1a..776af7fd6 100644 --- a/lib/gr3/gr3.c +++ b/lib/gr3/gr3.c @@ -78,8 +78,8 @@ const char *gr3_error_file_ = ""; #define GR3_ContextStruct_INITIALIZER \ { \ GR3_InitStruct_INITIALIZER, 0, 0, 0, NULL, 0, NULL, not_initialized_, NULL, NULL, 0, 0, {{0}}, 0, 0, 0, NAN, NAN, \ - NAN, NAN, 0, 0, 0, 0, 0, {0, 0, 0, 1}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, 0, 0, 0, 0, -1, 0, {0}, {0}, {0}, \ - {0}, 0, 0, 0, {0}, {0.2, 0.8, 128, 0.7}, 1, NAN, NAN, NAN, NAN, NAN, NAN, 0, 0 \ + NAN, NAN, 0, 0, 0, 0, 0, {0, 0, 0, 1}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, 0, 0, 0, 0, 4, 0, {0}, {0}, {0}, \ + {0}, 0, 0, 0, 0, {0}, {0.2, 0.8, 128, 0.7}, 1, NAN, NAN, NAN, NAN, NAN, NAN, 0, 0 \ } #endif GR3_ContextStruct_t_ context_struct_ = GR3_ContextStruct_INITIALIZER; From 60999fb10f5c0bd1c64ddfd5a846828b5197e0b9 Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Mon, 13 Jan 2025 13:05:13 +0100 Subject: [PATCH 07/16] Deploy Qt 6 binaries instead of Qt 5 with the Windows build --- .gitlab-ci/build-windows.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci/build-windows.yml b/.gitlab-ci/build-windows.yml index 843ca238a..c5b540f7e 100644 --- a/.gitlab-ci/build-windows.yml +++ b/.gitlab-ci/build-windows.yml @@ -221,12 +221,12 @@ windows-64bit-cmake-msvc: - vcvars_cmd cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/c/gr -DGR_USE_BUNDLED_LIBRARIES=OFF - vcvars_cmd cmake --build build --config Release - vcvars_cmd cmake --install build --config Release - - cd /c/Qt/5.15.2/msvc2019_64/bin - - cp -v Qt5Core.dll Qt5Gui.dll Qt5Widgets.dll Qt5Network.dll /c/gr/bin/ + - cd /c/Qt6/6.8.1/msvc2022_64/bin + - cp -v Qt6Core.dll Qt6Gui.dll Qt6Widgets.dll Qt6Network.dll /c/gr/bin/ - cd - - cd /c/gr/bin - mkdir -p platforms - - cp -v /c/Qt/5.15.2/msvc2019_64/plugins/platforms/qwindows.dll platforms/ + - cp -v /c/Qt6/6.8.1/msvc2022_64/plugins/platforms/qwindows.dll platforms/ - cd - - cd /c/local/bin - cp -v cairo.dll freetype.dll jpeg62.dll libpng16.dll pixman-1-0.dll qhull_r.dll tiff.dll tiffxx.dll turbojpeg.dll xerces-c_3_2.dll zlib.dll /c/gr/bin/ From d1726c7742c88223614caaada0d81be3f064a172 Mon Sep 17 00:00:00 2001 From: Thomas Verbovsek Date: Mon, 6 Jan 2025 14:27:08 +0100 Subject: [PATCH 08/16] fix menubar under linux; fixed an issue with quadratic plots and a different start window; use nominal factor which respects the size of the central_region instead of the min of the window size; improved the plot_element selection in mouseMoveEvent; changed exported file name for multiplots, fixed tooltips alpha value for filled kinds in multiplot case; added contourf to those kinds; fixed an issue with org phi and theta which led to wrong resets --- lib/grm/grplot/grplot_mainwindow.cxx | 14 ++- lib/grm/grplot/grplot_widget.cxx | 110 +++++++++++------- .../graphics_tree/private_schema.xsd | 1 + lib/grm/src/grm/dom_render/render.cxx | 99 ++++++++++++---- 4 files changed, 159 insertions(+), 65 deletions(-) diff --git a/lib/grm/grplot/grplot_mainwindow.cxx b/lib/grm/grplot/grplot_mainwindow.cxx index 7d00a93d5..932a65a59 100644 --- a/lib/grm/grplot/grplot_mainwindow.cxx +++ b/lib/grm/grplot/grplot_mainwindow.cxx @@ -9,12 +9,10 @@ GRPlotMainWindow::GRPlotMainWindow(int argc, char **argv) : QMainWindow() if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) { auto *w = new QWidget(this); - QString s; std::string kind; static char path[MAXPATHLEN]; std::snprintf(path, MAXPATHLEN, "%s/lib", GRDIR); - auto *message = new QTextBrowser(w); message->setSearchPaths(QStringList(path)); message->setSource(QUrl("../share/doc/grplot/grplot.man.md")); @@ -36,12 +34,22 @@ GRPlotMainWindow::GRPlotMainWindow(int argc, char **argv) : QMainWindow() grplot_widget_ = new GRPlotWidget(this, argc, argv); setCentralWidget(grplot_widget_); grplot_widget_->resize(WIDTH, HEIGHT); + grplot_widget_->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); } setWindowTitle("GR Plot"); if (strcmp(argv[1], "--listen") != 0) { - resize(WIDTH, HEIGHT); + if (grplot_widget_) + { + grplot_widget_->setMinimumSize(WIDTH, HEIGHT); + adjustSize(); + grplot_widget_->setMinimumSize(0, 0); + } + else + { + resize(WIDTH, HEIGHT); + } } } diff --git a/lib/grm/grplot/grplot_widget.cxx b/lib/grm/grplot/grplot_widget.cxx index 7a0f008de..833e1528c 100644 --- a/lib/grm/grplot/grplot_widget.cxx +++ b/lib/grm/grplot/grplot_widget.cxx @@ -1041,11 +1041,15 @@ void GRPlotWidget::draw() static bool called_at_least_once = false; if (!file_export.empty()) { + std::string kind; static char file[50]; auto global_root = grm_get_document_root(); - auto plot_elem = global_root->querySelectors("plot"); - auto kind = static_cast(plot_elem->getAttribute("_kind")); + auto plot_elem = global_root->querySelectorsAll("plot"); + if (plot_elem.size() > 1) + kind = "multiplot"; + else + kind = static_cast(plot_elem[0]->getAttribute("_kind")); snprintf(file, 50, "grplot_%s.%s", kind.c_str(), file_export.c_str()); grm_export(file); } @@ -1245,13 +1249,18 @@ void GRPlotWidget::paint(QPaintDevice *paint_device) } label.setDefaultStyleSheet(QString::fromStdString(tooltipStyle)); label.setHtml(QString::fromStdString(info)); - auto global_root = grm_get_document_root(); - auto plot_elem = global_root->querySelectors("plot"); - kind = static_cast(plot_elem->getAttribute("_kind")); - if (kind == "heatmap" || kind == "marginal_heatmap") - { - background.setAlpha(224); - } + + /* tooltips for filled plot kinds gets a higher alpha value to make the values more visible */ + int width = this->size().width(), height = this->size().height(); + GRM::Render::getFigureSize(&width, &height, nullptr, nullptr); + auto max_width_height = std::max(width, height); + auto x = (double)tooltip.x_px() / max_width_height; + auto y = (double)(height - tooltip.y_px()) / max_width_height; + auto plot_element = get_subplot_from_ndc_points_using_dom(1, &x, &y); + kind = static_cast(plot_element->getAttribute("_kind")); + if (kind == "heatmap" || kind == "marginal_heatmap" || kind == "contourf" || kind == "imshow") + background.setAlpha(224); + painter.fillRect(tooltip.x_px() + 8, (int)(tooltip.y_px() - label.size().height() / 2), (int)label.size().width(), (int)label.size().height(), QBrush(background, Qt::SolidPattern)); @@ -1421,9 +1430,6 @@ void GRPlotWidget::mouseMoveEvent(QMouseEvent *event) grm_args_push(args, "y", "i", mouseState.anchor.y()); grm_args_push(args, "x_shift", "i", x - mouseState.anchor.x()); grm_args_push(args, "y_shift", "i", y - mouseState.anchor.y()); - - /* get the correct cursor and sets it */ - int cursor_state = grm_get_hover_mode(x, y, disable_movable_xform); grm_args_push(args, "move_selection", "i", 1); grm_input(args); @@ -1499,15 +1505,36 @@ void GRPlotWidget::mouseMoveEvent(QMouseEvent *event) else { std::string kind; - int x, y; - getMousePos(event, &x, &y); + int mouse_x, mouse_y; + double x, y; + int width = this->size().width(), height = this->size().height(); + getMousePos(event, &mouse_x, &mouse_y); collectTooltips(); + + GRM::Render::getFigureSize(&width, &height, nullptr, nullptr); + auto max_width_height = std::max(width, height); + x = (double)mouse_x / max_width_height; + y = (double)(height - mouse_y) / max_width_height; + auto global_root = grm_get_document_root(); - auto plot_elems = global_root->querySelectorsAll("plot"); - // TODO: Select only the plot_elem where the mouse is (active_plot) - for (const auto &plot_elem : plot_elems) + auto plot_element = get_subplot_from_ndc_points_using_dom(1, &x, &y); + if (plot_element) { - if (plot_elem) + kind = static_cast(plot_element->getAttribute("_kind")); + if (kind == "marginal_heatmap") + { + grm_args_t *input_args; + input_args = grm_args_new(); + + grm_args_push(input_args, "x", "i", mouse_x); + grm_args_push(input_args, "y", "i", mouse_y); + grm_input(input_args); + redraw(); + } + } + else + { + for (const auto &plot_elem : global_root->querySelectorsAll("plot")) { kind = static_cast(plot_elem->getAttribute("_kind")); if (kind == "marginal_heatmap") @@ -1515,30 +1542,30 @@ void GRPlotWidget::mouseMoveEvent(QMouseEvent *event) grm_args_t *input_args; input_args = grm_args_new(); - grm_args_push(input_args, "x", "i", x); - grm_args_push(input_args, "y", "i", y); + grm_args_push(input_args, "x", "i", mouse_x); + grm_args_push(input_args, "y", "i", mouse_y); grm_input(input_args); + redraw(); } - - /* get the correct cursor and sets it */ - int cursor_state = grm_get_hover_mode(x, y, disable_movable_xform); - if (cursor_state == DEFAULT_HOVER_MODE) - { - csr->setShape(Qt::ArrowCursor); - } - else if (cursor_state == MOVABLE_HOVER_MODE) - { - csr->setShape(Qt::OpenHandCursor); - } - else if (cursor_state == INTEGRAL_SIDE_HOVER_MODE) - { - csr->setShape(Qt::SizeHorCursor); - } - setCursor(*csr); - - redraw(); } } + + /* get the correct cursor and sets it */ + int cursor_state = grm_get_hover_mode(mouse_x, mouse_y, disable_movable_xform); + if (cursor_state == DEFAULT_HOVER_MODE) + { + csr->setShape(Qt::ArrowCursor); + } + else if (cursor_state == MOVABLE_HOVER_MODE) + { + csr->setShape(Qt::OpenHandCursor); + } + else if (cursor_state == INTEGRAL_SIDE_HOVER_MODE) + { + csr->setShape(Qt::SizeHorCursor); + } + setCursor(*csr); + update(); } } @@ -1697,7 +1724,7 @@ void GRPlotWidget::mouseReleaseEvent(QMouseEvent *event) void GRPlotWidget::resizeEvent(QResizeEvent *event) { auto global_root = grm_get_document_root(); - auto figure = global_root->querySelectors("[active=1]"); + auto figure = global_root->querySelectors("figure[active=1]"); if (figure != nullptr) { figure->setAttribute("size_x", (double)event->size().width()); @@ -2944,7 +2971,10 @@ void GRPlotWidget::processTestCommandsFile() int height = words[2].toInt(&height_flag); if (width_flag && height_flag) { - window()->resize(width, height); + this->resize(width, height); + this->setMinimumSize(width, height); + window()->adjustSize(); + this->setMinimumSize(0, 0); tooltips.clear(); QTimer::singleShot(100, this, &GRPlotWidget::processTestCommandsFile); diff --git a/lib/grm/src/grm/dom_render/graphics_tree/private_schema.xsd b/lib/grm/src/grm/dom_render/graphics_tree/private_schema.xsd index 9ff782ac8..22067be45 100644 --- a/lib/grm/src/grm/dom_render/graphics_tree/private_schema.xsd +++ b/lib/grm/src/grm/dom_render/graphics_tree/private_schema.xsd @@ -82,6 +82,7 @@ + diff --git a/lib/grm/src/grm/dom_render/render.cxx b/lib/grm/src/grm/dom_render/render.cxx index a3107c276..e3e0d428f 100644 --- a/lib/grm/src/grm/dom_render/render.cxx +++ b/lib/grm/src/grm/dom_render/render.cxx @@ -873,7 +873,8 @@ static void legendSize(const std::vector &labels, double *w, double } static void sidePlotMargin(const std::shared_ptr &side_region, double *margin, double inc, - bool aspect_ratio_scale, double aspect_ratio_ws, double start_aspect_ratio_ws) + bool aspect_ratio_scale, double aspect_ratio_ws, double start_aspect_ratio_ws, + bool quadratic) { if (side_region->querySelectors("side_plot_region") || (side_region->hasAttribute("marginal_heatmap_side_plot") && @@ -882,10 +883,14 @@ static void sidePlotMargin(const std::shared_ptr &side_region, dou *margin += inc; if (aspect_ratio_scale) { - if (aspect_ratio_ws > start_aspect_ratio_ws) + if (!quadratic && aspect_ratio_ws > start_aspect_ratio_ws) { *margin /= (start_aspect_ratio_ws / aspect_ratio_ws); } + else if (quadratic && aspect_ratio_ws > 1) + { + *margin *= aspect_ratio_ws; + } else { if (aspect_ratio_ws < 1) *margin /= aspect_ratio_ws; @@ -1097,13 +1102,13 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptr &element) { element->setAttribute("space_3d_theta", theta); } + // save the original plot rotation so it can be restored + if (element->hasAttribute("space_3d_phi") && !element->hasAttribute("_space_3d_phi_org")) + element->setAttribute("_space_3d_phi_org", static_cast(element->getAttribute("space_3d_phi"))); + if (element->hasAttribute("space_3d_theta") && !element->hasAttribute("_space_3d_theta_org")) + element->setAttribute("_space_3d_theta_org", static_cast(element->getAttribute("space_3d_theta"))); fov = static_cast(element->getAttribute("space_3d_fov")); camera_distance = static_cast(element->getAttribute("space_3d_camera_distance")); @@ -16322,14 +16332,6 @@ static void processPlot(const std::shared_ptr &element, const std: // from 0 to length. The cases for log can be ignored cause log gets ignored on imshow plots. if (child->localName() == "series_imshow") continue; - // save the original plot rotation so it can be restored - if (central_region->hasAttribute("space_3d_phi") && !central_region->hasAttribute("_space_3d_phi_org")) - central_region->setAttribute("_space_3d_phi_org", - static_cast(central_region->getAttribute("space_3d_phi"))); - if (central_region->hasAttribute("space_3d_theta") && !central_region->hasAttribute("_space_3d_theta_org")) - central_region->setAttribute("_space_3d_theta_org", - static_cast(central_region->getAttribute("space_3d_theta"))); - bool x_log = static_cast(element->getAttribute("x_log")) && child->hasAttribute("_x_org"); bool y_log = static_cast(element->getAttribute("y_log")) && child->hasAttribute("_y_org"); bool z_log = static_cast(element->getAttribute("z_log")) && child->hasAttribute("_z_org"); @@ -16682,9 +16684,13 @@ static void processElement(const std::shared_ptr &element, const s { if (!static_cast(element->getAttribute("active"))) return; if (element->hasAttribute("size_x") && !element->hasAttribute("_initial_width")) - element->setAttribute("_initial_width", static_cast(element->getAttribute("size_x"))); - if (element->hasAttribute("size_y") && !element->hasAttribute("_initial_height")) - element->setAttribute("_initial_height", static_cast(element->getAttribute("size_y"))); + { + int mwidth, mheight; + GRM::Render::getFigureSize(&mwidth, &mheight, nullptr, nullptr); + + element->setAttribute("_initial_width", static_cast(mwidth)); + element->setAttribute("_initial_height", static_cast(mheight)); + } if (element->querySelectorsAll("draw_graphics").empty()) plotProcessWsWindowWsViewport(element, context); } if (element->localName() == "plot") @@ -16693,7 +16699,7 @@ static void processElement(const std::shared_ptr &element, const s processPlot(element, context); if (static_cast(element->getAttribute("_kind")) == "marginal_heatmap") central_region_parent = element->children()[0]; // if the kind is marginal_heatmap plot can only has 1 child - // and this child is the marginal_heatmap_plot + // and this child is the marginal_heatmap_plot if (central_region_parent != element) calculateViewport(central_region_parent); @@ -16803,10 +16809,59 @@ static void processElement(const std::shared_ptr &element, const s automatic_update = old_state; } } + + // use the correct nominal factor for each plot respecting the actual size of the central_region + if (element->localName() == "plot") + { + double vp[4], viewport[4]; + double metric_width, metric_height; + int px_width, px_height; + double initial_factor; + std::shared_ptr plot_parent = element; + + auto central_region_elem = plot_parent->querySelectors("central_region"); + if (central_region_elem == nullptr) return; + auto figure_vp_element = plot_parent->parentElement()->localName() == "layout_grid_element" + ? plot_parent->parentElement() + : plot_parent; + + vp[0] = static_cast(figure_vp_element->getAttribute("plot_x_min")); + vp[1] = static_cast(figure_vp_element->getAttribute("plot_x_max")); + vp[2] = static_cast(figure_vp_element->getAttribute("plot_y_min")); + vp[3] = static_cast(figure_vp_element->getAttribute("plot_y_max")); + viewport[0] = static_cast(central_region_elem->getAttribute("viewport_x_min")); + viewport[1] = static_cast(central_region_elem->getAttribute("viewport_x_max")); + viewport[2] = static_cast(central_region_elem->getAttribute("viewport_y_min")); + viewport[3] = static_cast(central_region_elem->getAttribute("viewport_y_max")); + + GRM::Render::getFigureSize(&px_width, &px_height, &metric_width, &metric_height); + auto aspect_ratio_ws = metric_width / metric_height; + auto initial_width = static_cast(active_figure->getAttribute("_initial_width")) * (vp[1] - vp[0]); + auto initial_height = static_cast(active_figure->getAttribute("_initial_height")) * (vp[3] - vp[2]); + double central_region_width = (viewport[1] - viewport[0]) * px_width; + double central_region_height = (viewport[3] - viewport[2]) * px_height; + if (aspect_ratio_ws > 1) + central_region_height *= aspect_ratio_ws; + else + central_region_width /= aspect_ratio_ws; + + if (!plot_parent->hasAttribute("_initial_factor")) + { + initial_factor = (central_region_width * central_region_height) / (initial_width * initial_height); + plot_parent->setAttribute("_initial_factor", initial_factor); + } + else + { + initial_factor = static_cast(plot_parent->getAttribute("_initial_factor")); + } + double factor = (central_region_width * central_region_height) / (initial_width * initial_height); + + gr_setnominalsize(sqrt(factor / initial_factor) * + (grm_min(initial_width, initial_height) / grm_min(px_width, px_height))); + } } -/* ~~~~~~~~~~~~~~~~~~~~~~~~~ render functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ +/* ~~~~~~~~~~~~~~~~~~~~~~~~~ render functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ static void renderHelper(const std::shared_ptr &element, const std::shared_ptr &context) { @@ -18862,8 +18917,7 @@ std::shared_ptr GRM::Render::createArcGridLine(double value, return element; } -/* ~~~~~~~~~~~~~~~~~~~~~~~~~ modifier functions~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ +/* ~~~~~~~~~~~~~~~~~~~~~~~~~ modifier functions~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ void GRM::Render::setClipRegion(const std::shared_ptr &element, int region) { @@ -20074,6 +20128,7 @@ void updateFilter(const std::shared_ptr &element, const std::strin if (new_type == "3d" && !central_region->hasAttribute("_diag_factor_set_by_user")) central_region->removeAttribute("diag_factor"); active_figure->setAttribute("_kind_changed", true); + plot_parent->removeAttribute("_initial_factor"); } if (coordinate_system) From 078622c23af263838f9edb38c65e086306ccd157 Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Tue, 14 Jan 2025 17:07:51 +0100 Subject: [PATCH 09/16] [GR3] Add native Windows threading support --- lib/gr3/gr3_internals.h | 2 +- lib/gr3/gr3_sr.c | 113 ++++++++++++++++++++++------------------ lib/gr3/gr3_sr.h | 21 +++++--- 3 files changed, 79 insertions(+), 57 deletions(-) diff --git a/lib/gr3/gr3_internals.h b/lib/gr3/gr3_internals.h index 565e1e19b..85d2e5010 100644 --- a/lib/gr3/gr3_internals.h +++ b/lib/gr3/gr3_internals.h @@ -307,7 +307,7 @@ typedef struct _GR3_ContextStruct_t_ float *depth_buffers[MAX_NUM_THREADS]; TransparencyVector *transparency_buffer[MAX_NUM_THREADS]; #ifndef NO_THREADS - pthread_t threads[MAX_NUM_THREADS]; + thread_t threads[MAX_NUM_THREADS]; #endif queue *queues[MAX_NUM_THREADS]; int last_width; diff --git a/lib/gr3/gr3_sr.c b/lib/gr3/gr3_sr.c index 9a41c5cb7..190ed82f6 100644 --- a/lib/gr3/gr3_sr.c +++ b/lib/gr3/gr3_sr.c @@ -11,20 +11,9 @@ #include #include "gks.h" -#ifdef _MSC_VER -#define NO_THREADS 1 -#endif -#ifndef NO_THREADS -#include -#endif #include "gr3_internals.h" #include "gr3_sr.h" -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN 1 -#include -#else -#include -#endif + #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MINTHREE(a, b, c) MIN(MIN(a, b), c) @@ -33,6 +22,40 @@ /* the following macro enables BACKFACE_CULLING */ /*#define BACKFACE_CULLING*/ +#ifndef NO_THREADS +#ifndef _MSC_VER +#define thread_create(thread, callback, arg) pthread_create(thread, NULL, callback, arg) +#define thread_join(thread) pthread_join(thread, NULL) +#define thread_mutex_init(lock) pthread_mutex_init(lock, NULL) +#define thread_mutex_lock(lock) pthread_mutex_lock(lock) +#define thread_mutex_unlock(lock) pthread_mutex_unlock(lock) +#define thread_cond_init(cond) pthread_cond_init(cond, NULL) +#define thread_cond_wait(cond, lock) pthread_cond_wait(cond, lock) +#define thread_cond_signal(cond) pthread_cond_signal(cond) +#define thread_cond_broadcast(cond) pthread_cond_broadcast(cond) +#else +#define thread_create(thread, callback, arg) (*thread = CreateThread(NULL, 0, callback, arg, 0, NULL)) +#define thread_join(thread) WaitForSingleObject(thread, INFINITE) +#define thread_mutex_init(lock) InitializeCriticalSection(lock) +#define thread_mutex_lock(lock) EnterCriticalSection(lock) +#define thread_mutex_unlock(lock) LeaveCriticalSection(lock) +#define thread_cond_init(cond) InitializeConditionVariable(cond) +#define thread_cond_wait(cond, lock) SleepConditionVariableCS(cond, lock, INFINITE) +#define thread_cond_signal(cond) WakeConditionVariable(cond) +#define thread_cond_broadcast(cond) WakeAllConditionVariable(cond) +#endif +#else +#define thread_create(thread, callback, arg) +#define thread_join(thread) +#define thread_mutex_init(lock) +#define thread_mutex_lock(lock) +#define thread_mutex_unlock(lock) +#define thread_cond_init(cond) +#define thread_cond_wait(cond, lock) +#define thread_cond_signal(cond) +#define thread_cond_broadcast(cond) +#endif + static int queue_destroy(queue *queue); static queue *queue_new(void); static void *queue_dequeue(queue *queue); @@ -120,10 +143,10 @@ static void merge(_TransparencyObject *pixel_transparency_buffer, int l, int m, * to finish so that the main thread can finally return the pixmap containing the final image.*/ #ifndef NO_THREADS static volatile int threads_done = 0; -static pthread_mutex_t lock; -static pthread_mutex_t lock_main; -static pthread_cond_t wait_for_merge; -static pthread_cond_t wait_after_merge; +static thread_mutex_t lock; +static thread_mutex_t lock_main; +static thread_cond_t wait_for_merge; +static thread_cond_t wait_after_merge; #endif @@ -160,10 +183,8 @@ static queue *queue_new(void) { return NULL; } -#ifndef NO_THREADS - pthread_mutex_init(&queue->lock, NULL); - pthread_cond_init(&queue->cond, NULL); -#endif + thread_mutex_init(&queue->lock); + thread_cond_init(&queue->cond); queue->front = queue->back = NULL; return queue; @@ -173,13 +194,11 @@ static void *queue_dequeue(queue *queue) { struct queue_node_s *node; void *argument; -#ifndef NO_THREADS - pthread_mutex_lock(&queue->lock); + thread_mutex_lock(&queue->lock); if (queue == NULL || queue->front == NULL) { - pthread_cond_wait(&queue->cond, &queue->lock); + thread_cond_wait(&queue->cond, &queue->lock); } -#endif node = queue->front; if (node == NULL) { @@ -192,18 +211,14 @@ static void *queue_dequeue(queue *queue) queue->back = NULL; } free(node); -#ifndef NO_THREADS - pthread_mutex_unlock(&queue->lock); -#endif + thread_mutex_unlock(&queue->lock); return argument; } static int queue_enqueue(queue *queue, void *data) { struct queue_node_s *node; -#ifndef NO_THREADS - pthread_mutex_lock(&queue->lock); -#endif + thread_mutex_lock(&queue->lock); if (queue == NULL) { abort(); @@ -226,10 +241,8 @@ static int queue_enqueue(queue *queue, void *data) queue->back->next = node; queue->back = node; } -#ifndef NO_THREADS - pthread_mutex_unlock(&queue->lock); - pthread_cond_signal(&queue->cond); -#endif + thread_mutex_unlock(&queue->lock); + thread_cond_signal(&queue->cond); return SUCCESS; } @@ -529,27 +542,27 @@ static void *draw_and_merge(void *queue_and_merge_area) if (((args *)argument)->id == 1) { #ifndef NO_THREADS - pthread_mutex_lock(&lock); + thread_mutex_lock(&lock); threads_done += 1; if (threads_done == context_struct_.num_threads) { - pthread_cond_broadcast(&wait_for_merge); + thread_cond_broadcast(&wait_for_merge); } else { - pthread_cond_wait(&wait_for_merge, &lock); + thread_cond_wait(&wait_for_merge, &lock); } - pthread_mutex_unlock(&lock); + thread_mutex_unlock(&lock); #endif merge_pixmaps(queue_and_merge_area_s.width, queue_and_merge_area_s.starty, queue_and_merge_area_s.endy); #ifndef NO_THREADS - pthread_mutex_lock(&lock); + thread_mutex_lock(&lock); threads_done += 1; - pthread_mutex_unlock(&lock); + thread_mutex_unlock(&lock); if (threads_done == 2 * context_struct_.num_threads) { - pthread_cond_signal(&wait_after_merge); + thread_cond_signal(&wait_after_merge); } #endif } @@ -600,7 +613,7 @@ static void initialise_consumer(queue *queues[MAX_NUM_THREADS], int height, int queue_and_merge_area->queue = queues[i]; queue_and_merge_area->width = width; #ifndef NO_THREADS - pthread_create(&context_struct_.threads[i], NULL, draw_and_merge, (void *)queue_and_merge_area); + thread_create(&context_struct_.threads[i], draw_and_merge, (void *)queue_and_merge_area); #else draw_and_merge(queue_and_merge_area); #endif @@ -1997,10 +2010,10 @@ GR3API void gr3_getpixmap_softwarerendered(char *pixmap, int width, int height, width *= ssaa_factor; height *= ssaa_factor; #ifndef NO_THREADS - pthread_mutex_init(&lock, NULL); - pthread_mutex_init(&lock_main, NULL); - pthread_cond_init(&wait_for_merge, NULL); - pthread_cond_init(&wait_after_merge, NULL); + thread_mutex_init(&lock); + thread_mutex_init(&lock_main); + thread_cond_init(&wait_for_merge); + thread_cond_init(&wait_after_merge); threads_done = 0; #endif int j; @@ -2078,12 +2091,12 @@ GR3API void gr3_getpixmap_softwarerendered(char *pixmap, int width, int height, #endif #ifndef NO_THREADS - pthread_mutex_lock(&lock_main); + thread_mutex_lock(&lock_main); if (threads_done < 2 * context_struct_.num_threads) { - pthread_cond_wait(&wait_after_merge, &lock_main); + thread_cond_wait(&wait_after_merge, &lock_main); } - pthread_mutex_unlock(&lock_main); + thread_mutex_unlock(&lock_main); #endif if (ssaa_factor != 1) @@ -2561,7 +2574,7 @@ GR3API void gr3_terminateSR_(void) arg = malloc_arg(i, 0, MAT4x4_INIT_NUL, MAT4x4_INIT_NUL, MAT4x4_INIT_NUL, MAT4x4_INIT_NUL, MAT3x3_INIT_NUL, MAT3x3_INIT_NUL, NULL, NULL, 0, 0, 2, 0, 0, NULL, NULL, 0, 0, NULL); queue_enqueue(context_struct_.queues[i], arg); - pthread_join(context_struct_.threads[i], NULL); + thread_join(context_struct_.threads[i]); #endif queue_destroy(context_struct_.queues[i]); } diff --git a/lib/gr3/gr3_sr.h b/lib/gr3/gr3_sr.h index 23818cc66..c69afa945 100644 --- a/lib/gr3/gr3_sr.h +++ b/lib/gr3/gr3_sr.h @@ -1,13 +1,22 @@ #ifndef GR3_SR_H_INCLUDED #define GR3_SR_H_INCLUDED -#ifndef _MSC_VER -#include +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN 1 +#include #else -#define NO_THREADS 1 -#include +#include #endif #ifndef NO_THREADS +#ifndef _MSC_VER #include +#define thread_t pthread_t +#define thread_mutex_t pthread_mutex_t +#define thread_cond_t pthread_cond_t +#else +#define thread_t HANDLE +#define thread_mutex_t CRITICAL_SECTION +#define thread_cond_t CONDITION_VARIABLE +#endif #endif #include "gr.h" #include "gr3.h" @@ -31,8 +40,8 @@ struct queue_node_s struct queue_s { #ifndef NO_THREADS - pthread_mutex_t lock; - pthread_cond_t cond; + thread_mutex_t lock; + thread_cond_t cond; #endif struct queue_node_s *front; struct queue_node_s *back; From 1e3b663f92bab6eb97ffd0554ef99f8e0730ae0a Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Thu, 16 Jan 2025 10:48:08 +0100 Subject: [PATCH 10/16] Fix the Cairo plugin build on Windows/MSVC --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c79ec66b1..2480e6fd9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -284,7 +284,7 @@ foreach(LIBRARY gks_static gks_shared) target_link_libraries(${LIBRARY} ${GKS_LINK_MODE} pthread) target_link_libraries(${LIBRARY} ${GKS_LINK_MODE} m) endif() - if(FREETYPE_FOUND) + if(Freetype_FOUND) target_link_libraries(${LIBRARY} ${GKS_LINK_MODE} Freetype::Freetype) else() target_compile_definitions( From c380da69b33935eca7053bf37ac8aac3e5d7403c Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Thu, 16 Jan 2025 10:50:09 +0100 Subject: [PATCH 11/16] Add support for agg on Windows/MSVC --- CMakeLists.txt | 5 +++++ cmake/FindAgg.cmake | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2480e6fd9..c3a575f71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -670,6 +670,11 @@ if(Agg_FOUND) target_link_libraries(aggplugin PRIVATE Libpng::Libpng) string(APPEND GR_REPORT "- aggplugin: Yes\n") target_compile_options(aggplugin PRIVATE ${COMPILER_OPTION_ERROR_IMPLICIT}) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # Do not define `min` and `max` as macros because this can have unintended side effects (like error messages when + # using `std::min`) + target_compile_definitions(aggplugin PRIVATE NOMINMAX) + endif() else() target_compile_definitions(aggplugin PRIVATE NO_AGG) string(APPEND GR_REPORT "- aggplugin: No (Agg not found)\n") diff --git a/cmake/FindAgg.cmake b/cmake/FindAgg.cmake index 0fefc387a..73fdee7ac 100644 --- a/cmake/FindAgg.cmake +++ b/cmake/FindAgg.cmake @@ -25,7 +25,7 @@ # If false, do not try to use Agg. if(NOT AGG_INCLUDE_DIR) - find_path(AGG_INCLUDE_DIR agg_basics.h) + find_path(AGG_INCLUDE_DIR agg_basics.h PATH_SUFFIXES agg2) endif() if(NOT AGG_LIBRARY) From e1d7d71fa8c99e716fbc3a342debcd3421ee3a36 Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Thu, 16 Jan 2025 14:11:25 +0100 Subject: [PATCH 12/16] Deploy missing `msvcp140_2.dll` (needed for Qt 6.8) --- .gitlab-ci/build-windows.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci/build-windows.yml b/.gitlab-ci/build-windows.yml index c5b540f7e..d5c406912 100644 --- a/.gitlab-ci/build-windows.yml +++ b/.gitlab-ci/build-windows.yml @@ -235,6 +235,7 @@ windows-64bit-cmake-msvc: - cp -v "/c/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools/VC/Redist/MSVC/14.31.31103/x64/Microsoft.VC143.CRT/vcruntime140_1.dll" /c/gr/bin/ - cp -v "/c/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools/VC/Redist/MSVC/14.31.31103/x64/Microsoft.VC143.CRT/msvcp140.dll" /c/gr/bin/ - cp -v "/c/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools/VC/Redist/MSVC/14.31.31103/x64/Microsoft.VC143.CRT/msvcp140_1.dll" /c/gr/bin/ + - cp -v "/c/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools/VC/Redist/MSVC/14.31.31103/x64/Microsoft.VC143.CRT/msvcp140_2.dll" /c/gr/bin/ - mv /c/gr artifacts-windows-64bit-cmake-msvc artifacts: expire_in: 1 week From aacbab0e0a8653c733d58293f0f2ea0e24440574 Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Fri, 17 Jan 2025 11:56:26 +0100 Subject: [PATCH 13/16] Fix the construction of the gksqt startup command on Windows This commit replaces the format specifier `%wS` with `%S` since `%wS` does not exist (although `%ws` does). --- lib/gks/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gks/socket.c b/lib/gks/socket.c index 98029de55..b0da75a72 100644 --- a/lib/gks/socket.c +++ b/lib/gks/socket.c @@ -283,7 +283,7 @@ static int open_socket(int wstype) { if (!GetEnvironmentVariableW(L"GRDIR", w_env, MAXPATHLEN)) { - StringCbPrintfW(command, CMD_LINE_LEN, L"%wS\\bin\\gksqt.exe", GRDIR); + StringCbPrintfW(command, CMD_LINE_LEN, L"%S\\bin\\gksqt.exe", GRDIR); } else { From b4d90f2f8c10c66641f83dc35d2e70ba45e5413e Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Fri, 17 Jan 2025 11:59:05 +0100 Subject: [PATCH 14/16] Fix the GKS/GR close when called from an exit handler - Do not install the GKS exit handler more than once. - Ignore WinSock errors if called from an exit handler (WinSock may already be unloaded). - Do not register a second exit handler in the GR initialize routines. --- lib/gks/gks.c | 15 ++++++++++++++- lib/gks/gkscore.h | 1 + lib/gks/socket.c | 25 +++++++++++++++++++------ lib/gr/gr.c | 6 ++---- 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/lib/gks/gks.c b/lib/gks/gks.c index 0b067ea14..5727e313f 100644 --- a/lib/gks/gks.c +++ b/lib/gks/gks.c @@ -382,8 +382,16 @@ static int gks_parse_encoding(const char *encoding) } +static void gks_exit_handler(void) +{ + s->in_exit_handler = 1; + gks_emergency_close(); +} + + static void gks_parse_env(void) { + static int exit_handler_installed = 0; static int did_report_invalid_encoding = 0; const char *env; @@ -416,7 +424,11 @@ static void gks_parse_env(void) s->input_encoding = (char *)gks_getenv("GKS_IGNORE_ENCODING") != NULL ? ENCODING_UTF8 : ENCODING_LATIN1; } - if (gks_getenv("GKS_NO_EXIT_HANDLER") == NULL) atexit(gks_emergency_close); + if (gks_getenv("GKS_NO_EXIT_HANDLER") == NULL && !exit_handler_installed) + { + atexit(gks_exit_handler); + exit_handler_installed = 1; + } if (gks_getenv("GKS_DEBUG") != NULL) s->debug = 1; } @@ -546,6 +558,7 @@ void gks_init_gks(void) s->aspect_ratio = 1; s->callback = NULL; + s->in_exit_handler = 0; s->debug = 0; } } diff --git a/lib/gks/gkscore.h b/lib/gks/gkscore.h index af9bc8bff..ce12cf510 100644 --- a/lib/gks/gkscore.h +++ b/lib/gks/gkscore.h @@ -176,6 +176,7 @@ typedef struct double nominal_size; double aspect_ratio; char *(*callback)(const char *); + int in_exit_handler; int debug; } gks_state_list_t; diff --git a/lib/gks/socket.c b/lib/gks/socket.c index b0da75a72..0aef5ed7b 100644 --- a/lib/gks/socket.c +++ b/lib/gks/socket.c @@ -63,6 +63,8 @@ static int is_running = 0; #ifdef _WIN32 +static int wsa_initialized = 0; + #define CMD_LINE_LEN (32767 + 10) /* * The maximum length of an environment variable is 32767 characters plus 10 characters for 'cmd /c ""' @@ -182,13 +184,17 @@ static int connect_socket(char *server, char *servname, int quiet) int opt; #if defined(_WIN32) - WORD wVersionRequested = MAKEWORD(1, 1); - WSADATA wsaData; - - if (WSAStartup(wVersionRequested, &wsaData) != 0) + if (!wsa_initialized) { - fprintf(stderr, "Can't find a usable WinSock DLL\n"); - return -1; + WORD wVersionRequested = MAKEWORD(1, 1); + WSADATA wsaData; + + if (WSAStartup(wVersionRequested, &wsaData) != 0) + { + fprintf(stderr, "Can't find a usable WinSock DLL\n"); + return -1; + } + wsa_initialized = 1; } #endif @@ -372,6 +378,12 @@ static int send_socket(int s, char *buf, int size, int ignore_error) { int sent, n = 0; + /* + * If this routine is called from an exit handler, the WinSock library might already be unloaded, leading to sending + * errors. This cannot be avoided, so always ignore send errors if called from an exit handler. + */ + if (gkss != NULL && gkss->in_exit_handler) ignore_error = 1; + for (sent = 0; sent < size; sent += n) { if ((n = send(s, buf + sent, size - sent, 0)) == -1) @@ -422,6 +434,7 @@ static int close_socket(int s) #endif #if defined(_WIN32) && !defined(__GNUC__) WSACleanup(); + wsa_initialized = 0; #endif return 0; } diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 0a0778ba0..56a3019e5 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -1520,14 +1520,12 @@ static void initgks(void) used[color] = 0; } +#ifdef SIGUSR1 if (gks_getenv("GKS_NO_EXIT_HANDLER") == NULL) { -#ifdef SIGUSR1 previous_handler = signal(SIGUSR1, resetgks); -#else - atexit(resetgks); -#endif } +#endif } void gr_initgr(void) From c168b497b6cabd01ca6e37ce9853cb4f6ddfc259 Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Fri, 17 Jan 2025 12:07:25 +0100 Subject: [PATCH 15/16] Quit gksqt if the last connection is closed without showing a window --- lib/gks/qt/gksserver.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/gks/qt/gksserver.cxx b/lib/gks/qt/gksserver.cxx index 70648d7bc..3a7327f7a 100644 --- a/lib/gks/qt/gksserver.cxx +++ b/lib/gks/qt/gksserver.cxx @@ -188,7 +188,7 @@ void GKSConnection::readClient() void GKSConnection::destroyedWidget() { widget = NULL; - emit(close(*this)); + socket->close(); } void GKSConnection::disconnectedSocket() @@ -198,6 +198,7 @@ void GKSConnection::disconnectedSocket() widget->close(); widget = NULL; } + emit(close(*this)); } void GKSConnection::updateWindowTitle(QString renderer) From f2c97ed3c42abea4655c521bf4108cede6967471 Mon Sep 17 00:00:00 2001 From: Christian Felder Date: Fri, 17 Jan 2025 15:28:46 +0100 Subject: [PATCH 16/16] CI: temporary disable checking build results on OBS --- .gitlab-ci/deploy.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.gitlab-ci/deploy.yml b/.gitlab-ci/deploy.yml index 172d487b1..c31fd7bd5 100644 --- a/.gitlab-ci/deploy.yml +++ b/.gitlab-ci/deploy.yml @@ -24,11 +24,6 @@ deploy-to-obs: mv -f ${OBS_PROJECT}/gr/_service.new ${OBS_PROJECT}/gr/_service - osc commit ${OBS_PROJECT}/gr/_service -m "Release $REVISION" - - fails="$(osc results -w -f -F ${OBS_PROJECT}/gr)" - - if [[ -n "${fails}" ]]; then - echo "$fails"; - exit 1; - fi deploy-to-aur: stage: deploy