From dbd15f69ed66d785a602d15b75c2c6a31ce49d2b Mon Sep 17 00:00:00 2001 From: Gereon Vincenti Date: Sat, 4 May 2024 14:56:10 +0200 Subject: [PATCH 01/24] [CI] Add missing semicolons in clang-tidy script --- .gitlab-ci/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci/test.yml b/.gitlab-ci/test.yml index 9f7de6057..f1a634371 100644 --- a/.gitlab-ci/test.yml +++ b/.gitlab-ci/test.yml @@ -128,8 +128,8 @@ clang-tidy-check: -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DGR_USE_BUNDLED_LIBRARIES=OFF - clang_tidy_failed=0; - ancestor=$(git merge-base upstream/develop HEAD) - [[ "${ancestor}" != "$(git rev-parse HEAD)" ]] || ancestor="HEAD~" + ancestor=$(git merge-base upstream/develop HEAD); + [[ "${ancestor}" != "$(git rev-parse HEAD)" ]] || ancestor="HEAD~"; while IFS= read -r file; do [[ "${file}" != 3rdparty* ]] && [[ "${file}" != apps* ]] || continue; [[ "${file}" =~ \.(c|cpp|cxx|m|h|hpp|hxx)$ ]] || continue; From eed7a649e395b714e47b71423c5fbe3e570f692f Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Mon, 6 May 2024 21:12:22 +0200 Subject: [PATCH 02/24] [CI] Fix duplicate pipelines and include all jobs in merge requests --- .gitlab-ci.yml | 17 +++++++++++++++++ .gitlab-ci/build-emscripten.yml | 4 ---- .gitlab-ci/test-emscripten.yml | 4 ---- .gitlab-ci/test-ubuntu.yml | 14 -------------- 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0601196cc..864185978 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,6 +6,23 @@ stages: - sync - deploy +workflow: + # This workflow section prevents branch and merge request pipelines from + # being created at the same time avoiding duplicate pipelines: + # - if: $CI_PIPELINE_SOURCE == "merge_request_event" -> Create pipelines for open merge requests + # - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push" + # -> Do not create branch pipelines for open merge requests on push events but allow all other events (e.g. + # pipeline trigger, API calls, etc.) + # - when: always -> Create all other pipeline types as usual + # Using workflow rules also ensures that all jobs are included in both branch and merge request pipelines + # See + # for more information + rules: + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push" + when: never + - when: always + include: - local: "/.gitlab-ci/build-arch.yml" - local: "/.gitlab-ci/build-binary-builder.yml" diff --git a/.gitlab-ci/build-emscripten.yml b/.gitlab-ci/build-emscripten.yml index d069fadf2..033942766 100644 --- a/.gitlab-ci/build-emscripten.yml +++ b/.gitlab-ci/build-emscripten.yml @@ -11,7 +11,3 @@ emscripten: expire_in: 1 week paths: - artifacts-js/ - # Is needed to circumvent a bug in GitLab CI (if needed jobs do not contain a rule section, but the dependent job - # does, GitLab CI produces YAML errors) - rules: - - when: on_success diff --git a/.gitlab-ci/test-emscripten.yml b/.gitlab-ci/test-emscripten.yml index 919ec14d1..94f522b55 100644 --- a/.gitlab-ci/test-emscripten.yml +++ b/.gitlab-ci/test-emscripten.yml @@ -1,10 +1,6 @@ emscripten-test: stage: test image: iffregistry.fz-juelich.de/scientific-it-systems/gr-test/js-testing - rules: - - if: '$CI_MERGE_REQUEST_ID' - when: never - - when: on_success needs: - emscripten script: diff --git a/.gitlab-ci/test-ubuntu.yml b/.gitlab-ci/test-ubuntu.yml index f84ccdc4c..cb557abc2 100644 --- a/.gitlab-ci/test-ubuntu.yml +++ b/.gitlab-ci/test-ubuntu.yml @@ -1,10 +1,6 @@ ubuntu-cmake-self-contained-test-c: stage: test image: iffregistry.fz-juelich.de/scientific-it-systems/gr-test/c-testing - rules: - - if: '$CI_MERGE_REQUEST_ID' - when: never - - when: on_success needs: - ubuntu-cmake-self-contained-debug script: @@ -21,10 +17,6 @@ ubuntu-cmake-self-contained-test-c: ubuntu-cmake-self-contained-test-cpp: stage: test image: iffregistry.fz-juelich.de/scientific-it-systems/gr-test/cpp-testing - rules: - - if: '$CI_MERGE_REQUEST_ID' - when: never - - when: on_success needs: - ubuntu-cmake-self-contained-debug script: @@ -41,10 +33,6 @@ ubuntu-cmake-self-contained-test-cpp: ubuntu-cmake-self-contained-test-grplot: stage: test image: iffregistry.fz-juelich.de/scientific-it-systems/gr-test/grplot-testing - rules: - - if: '$CI_MERGE_REQUEST_ID' - when: never - - when: on_success needs: - ubuntu-cmake-self-contained-debug script: @@ -64,8 +52,6 @@ ubuntu-cmake-self-contained-test-snoop: stage: test image: iffregistry.fz-juelich.de/docker-images/gr-build-images/ubuntu-self-contained rules: - - if: '$CI_MERGE_REQUEST_ID' - when: never - if: '$CI_COMMIT_MESSAGE =~ /RebuildReferenceImages\([][ ?*_a-zA-Z0-9-]*\)/' when: never - if: '$GR_REBUILD_REFERENCE' From bcec4790745e040af066f3c590f7988360ea0d01 Mon Sep 17 00:00:00 2001 From: Verbov Date: Thu, 11 Apr 2024 10:48:44 +0200 Subject: [PATCH 03/24] added tooltips.clear() to resize event, only plots that can actually have a label respect now the label margin when the viewports are calculated; fixed and overworked ws_viewport and ws_window to fix wrong behaviour on lg screen; side_regions are not children of the plot anymore; moved text into side_regions; use metric sizes instead of pixel sizes - except for element moving (bounding boxes have pixel sizes); added keep_aspect_ratio with quadratic sides and keep_aspect_ratio mode; added text movment; fixed use_bins is getting ignored when kind is line but getting changed to heatmap; changed where legend is stored inside the tree and the resize behaviour; fixed legend location read from import.cxx is not getting set right on tree; silenced warning with wrong window in pie case - pie has no window --- lib/grm/grplot/README.md | 3 +- lib/grm/grplot/grplot_widget.cxx | 3 + lib/grm/include/grm/dom_render/render.hxx | 29 +- .../grm/dom_render/graphics_tree/schema.xsd | 5 +- lib/grm/src/grm/dom_render/render.cxx | 1534 +++++++++++------ lib/grm/src/grm/import.cxx | 13 +- lib/grm/src/grm/interaction.cxx | 8 +- lib/grm/src/grm/plot.cxx | 20 +- lib/grm/src/grm/plot_int.h | 11 +- 9 files changed, 1057 insertions(+), 569 deletions(-) diff --git a/lib/grm/grplot/README.md b/lib/grm/grplot/README.md index 0e631680b..ee023670e 100644 --- a/lib/grm/grplot/README.md +++ b/lib/grm/grplot/README.md @@ -21,7 +21,8 @@ The following parameters are key-value pairs which can be used for every plot ty There is another parameter that can be used for all two-dimensional data sets: -- `keep_aspect_ratio` or `aspect`: defines whether the aspect ratio of the data is kept or not. Possible values for this parameter are 0 or 1. +- `keep_aspect_ratio` or `aspect`: defines whether the aspect ratio of the initial picture is kept or not. Possible values for this parameter are 0 or 1. +- `only_quadratic_aspect_ratio`: defines whether the aspect ratio of quadratic data (x, y) is forced to be quadratic and kept this way or not. Notice this parameter will not work when the `keep_aspect_ratio` parameter has the value of 0. Possible values for this parameter are 0 or 1. For plots where multiple columns are read there is also a parameter that allows to select columns. diff --git a/lib/grm/grplot/grplot_widget.cxx b/lib/grm/grplot/grplot_widget.cxx index 140f87fe9..1ef504439 100644 --- a/lib/grm/grplot/grplot_widget.cxx +++ b/lib/grm/grplot/grplot_widget.cxx @@ -148,6 +148,7 @@ GRPlotWidget::GRPlotWidget(QMainWindow *parent, int argc, char **argv) "keep_window", "marginal_heatmap_side_plot", "movable", + "only_quadratic_aspect_ratio", "phi_flip", "set_text_color_for_background", "space", @@ -1871,6 +1872,7 @@ void GRPlotWidget::resizeEvent(QResizeEvent *event) mouse_move_selection = nullptr; amount_scrolled = 0; clicked.clear(); + tooltips.clear(); reset_pixmap(); } @@ -2903,6 +2905,7 @@ void GRPlotWidget::processTestCommandsFile() if (width_flag && height_flag) { window()->resize(width, height); + tooltips.clear(); QTimer::singleShot(100, this, &GRPlotWidget::processTestCommandsFile); return; diff --git a/lib/grm/include/grm/dom_render/render.hxx b/lib/grm/include/grm/dom_render/render.hxx index 02c3928c9..e8f41e1f2 100644 --- a/lib/grm/include/grm/dom_render/render.hxx +++ b/lib/grm/include/grm/dom_render/render.hxx @@ -29,7 +29,7 @@ #define PLOT_DEFAULT_SUBPLOT_MAX_Y 1.0 #define PLOT_DEFAULT_ROTATION 40.0 #define PLOT_DEFAULT_TILT 60.0 -#define PLOT_DEFAULT_KEEP_ASPECT_RATIO 0 +#define PLOT_DEFAULT_KEEP_ASPECT_RATIO 1 #define PLOT_DEFAULT_KEEP_WINDOW 1 #define PLOT_DEFAULT_TRICONT_LEVELS 20 #define PLOT_DEFAULT_CONTOUR_LEVELS 20 @@ -67,21 +67,21 @@ #define PLOT_DEFAULT_ZGRID 1 #define PLOT_DEFAULT_SPACE_3D_FOV 30.0 #define PLOT_DEFAULT_SPACE_3D_DISTANCE 0.0 -#define PLOT_DEFAULT_COLORBAR_DIAG_FACTOR 0.016 -#define PLOT_DEFAULT_COLORBAR_WIDTH 0.03 * DEFAULT_ASPECT_RATIO_FOR_SCALING -#define PLOT_DEFAULT_COLORBAR_MAX_CHAR_HEIGHT 0.016 * DEFAULT_ASPECT_RATIO_FOR_SCALING -#define PLOT_DEFAULT_COLORBAR_OFFSET 0.02 * DEFAULT_ASPECT_RATIO_FOR_SCALING -#define PLOT_3D_COLORBAR_OFFSET 0.05 * DEFAULT_ASPECT_RATIO_FOR_SCALING -#define PLOT_POLAR_COLORBAR_OFFSET 0.025 * DEFAULT_ASPECT_RATIO_FOR_SCALING -#define PLOT_DEFAULT_COLORBAR_TICK_SIZE 0.005 * DEFAULT_ASPECT_RATIO_FOR_SCALING -#define PLOT_DEFAULT_SIDEREGION_WIDTH 0.1 * DEFAULT_ASPECT_RATIO_FOR_SCALING -#define PLOT_DEFAULT_SIDEREGION_OFFSET 0.02 * DEFAULT_ASPECT_RATIO_FOR_SCALING +#define PLOT_DEFAULT_COLORBAR_WIDTH 0.03 +#define PLOT_DEFAULT_COLORBAR_MAX_CHAR_HEIGHT 0.016 +#define PLOT_DEFAULT_COLORBAR_OFFSET 0.02 +#define PLOT_3D_COLORBAR_OFFSET 0.05 +#define PLOT_POLAR_COLORBAR_OFFSET 0.025 +#define PLOT_DEFAULT_COLORBAR_TICK_SIZE 0.005 +#define PLOT_DEFAULT_SIDEREGION_WIDTH 0.1 +#define PLOT_DEFAULT_SIDEREGION_OFFSET 0.02 #define PLOT_DEFAULT_SIDEREGION_LOCATION "right" -#define PLOT_3D_CHAR_HEIGHT 0.024 * DEFAULT_ASPECT_RATIO_FOR_SCALING -#define PLOT_2D_CHAR_HEIGHT 0.018 * DEFAULT_ASPECT_RATIO_FOR_SCALING -#define PLOT_POLAR_CHAR_HEIGHT 0.018 * DEFAULT_ASPECT_RATIO_FOR_SCALING +#define PLOT_3D_CHAR_HEIGHT 0.024 +#define PLOT_2D_CHAR_HEIGHT 0.018 +#define PLOT_POLAR_CHAR_HEIGHT 0.018 #define PLOT_DEFAULT_AXES_TICK_SIZE 0.0075 #define DEFAULT_ASPECT_RATIO_FOR_SCALING 4.0 / 3.0 +#define PLOT_DEFAULT_ONLY_QUADRATIC_ASPECT_RATIO 0 /* ~~~~~~~~~~~~~~~~~~~~~~~~~ util ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -380,6 +380,9 @@ public: std::shared_ptr createIntegral(double int_lim_low, double int_lim_high, const std::shared_ptr &extElement = nullptr); + + std::shared_ptr createSideRegion(std::string location, + const std::shared_ptr &ext_element = nullptr); //! Modifierfunctions /* ------------------------------- setter functions ----------------------------------------------------------------*/ 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 53a5b531b..14ff75bd1 100644 --- a/lib/grm/src/grm/dom_render/graphics_tree/schema.xsd +++ b/lib/grm/src/grm/dom_render/graphics_tree/schema.xsd @@ -120,6 +120,7 @@ + @@ -144,6 +145,7 @@ + @@ -179,7 +181,6 @@ - @@ -424,7 +425,7 @@ - + diff --git a/lib/grm/src/grm/dom_render/render.cxx b/lib/grm/src/grm/dom_render/render.cxx index fd62e0385..4d4f25639 100644 --- a/lib/grm/src/grm/dom_render/render.cxx +++ b/lib/grm/src/grm/dom_render/render.cxx @@ -213,6 +213,11 @@ static std::set kinds_3d = { "wireframe", "surface", "plot3", "scatter3", "trisurface", "volume", "isosurface", }; +static std::set kinds_with_possible_label = { + "barplot", "contour", "contourf", "heatmap", "hexbin", "hist", "line", + "marginal_heatmap", "quiver", "scatter", "shade", "stairs", "stem", "tricontour", +}; + static std::map symbol_to_meters_per_unit{ {"m", 1.0}, {"dm", 0.1}, {"cm", 0.01}, {"mm", 0.001}, {"in", 0.0254}, {"\"", 0.0254}, {"ft", 0.3048}, {"'", 0.0254}, {"pc", 0.0254 / 6.0}, {"pt", 0.0254 / 72.0}, @@ -455,17 +460,18 @@ 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"); - GRM::Render::getFigureSize(&pixel_width, &pixel_height, nullptr, nullptr); - auto aspect_ratio_ws = (double)pixel_width / pixel_height; + 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) return 1; if (x) { max_vp = (aspect_ratio_ws < 1) ? static_cast(plot_element->getAttribute("_viewport_x_max_org")) : 1; - if (element->localName() != "side_region" && element->hasAttribute("_bbox_x_max")) + if (!str_equals_any(element->localName(), "legend", "side_region") && element->hasAttribute("_bbox_x_max")) { max_vp -= abs(static_cast(element->getAttribute("_viewport_x_max_org")) - static_cast(element->getAttribute("_bbox_x_max")) / max_width_height); @@ -474,8 +480,7 @@ static double getMaxViewport(const std::shared_ptr &element, bool else { max_vp = (aspect_ratio_ws > 1) ? static_cast(plot_element->getAttribute("_viewport_y_max_org")) : 1; - if (element->localName() != "side_region" && element->localName() != "plot" && - element->localName() != "marginal_heatmap_plot" && + if (!str_equals_any(element->localName(), "legend", "marginal_heatmap_plot", "plot", "side_region") && element->hasAttribute("_bbox_y_max")) // TODO: Exclude plot leads to problem with different aspect ration - // need to fix bboxes to fix this issue here { @@ -496,7 +501,7 @@ static double getMinViewport(const std::shared_ptr &element, bool if (x) { - if (element->localName() != "side_region" && element->hasAttribute("_bbox_x_min")) + if (!str_equals_any(element->localName(), "legend", "side_region") && element->hasAttribute("_bbox_x_min")) { min_vp += abs(static_cast(element->getAttribute("_viewport_x_min_org")) - static_cast(element->getAttribute("_bbox_x_min")) / max_width_height); @@ -504,7 +509,7 @@ static double getMinViewport(const std::shared_ptr &element, bool } else { - if (element->localName() != "side_region" && element->hasAttribute("_bbox_y_min")) + if (!str_equals_any(element->localName(), "legend", "side_region") && element->hasAttribute("_bbox_y_min")) { min_vp += abs(static_cast(element->getAttribute("_viewport_y_min_org")) - static_cast(element->getAttribute("_bbox_y_min")) / max_width_height); @@ -590,7 +595,8 @@ static void clearOldChildren(del_values *del, const std::shared_ptrlocalName() == "marginal_heatmap_plot" && child->localName() != "central_region")) + if (!(element->localName() == "marginal_heatmap_plot" && + (child->localName() != "central_region" || child->localName() != "side_region"))) child->remove(); } } @@ -614,11 +620,24 @@ static void clearOldChildren(del_values *del, const std::shared_ptrlocalName() != "central_region" && child->localName() != "text" && - element->localName() == "marginal_heatmap_plot") + if (child->localName() != "central_region" && element->localName() == "marginal_heatmap_plot") { - only_central_region_child = false; - break; + if (child->localName() == "side_region") + { + for (const auto &side_region_child : child->children()) + { + if (side_region_child->localName() != "text") + { + only_central_region_child = false; + break; + } + } + } + else + { + only_central_region_child = false; + break; + } } if (child->localName() != "error_bars" && child->localName() != "integral_group" && element->localName() != "coordinate_system" && element->localName() != "marginal_heatmap_plot") @@ -679,16 +698,16 @@ static void legendSize(const std::vector &labels, double *w, double } static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptr element, double *vp_x_min, - double *vp_x_max, double *vp_y_min, double *vp_y_max) + double *vp_x_max, double *vp_y_min, double *vp_y_max, + bool diag_factor = false) { bool y_label_margin = false, x_label_margin = false, title_margin; std::string kind; - bool keep_aspect_ratio, uniform_data = true; + bool keep_aspect_ratio, uniform_data = true, only_quadratic_aspect_ratio = false; double metric_width, metric_height; - int pixel_width, pixel_height; - double aspect_ratio_ws; + double aspect_ratio_ws, start_aspect_ratio_ws; double vp0, vp1, vp2, vp3; - double left_margin, right_margin, bottom_margin, top_margin; + double left_margin = 0.0, right_margin, bottom_margin = 0.0, top_margin; double viewport[4] = {0.0, 0.0, 0.0, 0.0}; std::shared_ptr plot_parent = element; auto render = grm_get_render(); @@ -697,6 +716,7 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptr(plot_parent->getAttribute("kind")); title_margin = static_cast(plot_parent->getAttribute("title_margin")); keep_aspect_ratio = static_cast(plot_parent->getAttribute("keep_aspect_ratio")); + only_quadratic_aspect_ratio = static_cast(plot_parent->getAttribute("only_quadratic_aspect_ratio")); if (element != nullptr && element->hasAttribute("x_label_margin")) x_label_margin = static_cast(element->getAttribute("x_label_margin")); @@ -712,8 +732,45 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptrparentElement(), render->getContext()); - GRM::Render::getFigureSize(&pixel_width, &pixel_height, nullptr, nullptr); - aspect_ratio_ws = (double)pixel_width / pixel_height; + GRM::Render::getFigureSize(nullptr, nullptr, &metric_width, &metric_height); + aspect_ratio_ws = metric_width / metric_height; + start_aspect_ratio_ws = static_cast(plot_parent->getAttribute("_start_aspect_ratio")); + + if (keep_aspect_ratio && (!only_quadratic_aspect_ratio || (only_quadratic_aspect_ratio && !uniform_data)) && + !diag_factor && kind != "imshow") + { + if (aspect_ratio_ws > start_aspect_ratio_ws) + { + auto x_min = *vp_x_min * (start_aspect_ratio_ws / aspect_ratio_ws); + auto x_max = *vp_x_max * (start_aspect_ratio_ws / aspect_ratio_ws); + auto diff = 0.5 * ((*vp_x_max - *vp_x_min) - (x_max - x_min)); + *vp_x_min += diff; + *vp_x_max -= diff; + } + else + { + auto y_min = *vp_y_min / (start_aspect_ratio_ws / aspect_ratio_ws); + auto y_max = *vp_y_max / (start_aspect_ratio_ws / aspect_ratio_ws); + auto diff = 0.5 * ((*vp_y_max - *vp_y_min) - (y_max - y_min)); + *vp_y_min += diff; + *vp_y_max -= diff; + } + } + else if (keep_aspect_ratio && uniform_data && only_quadratic_aspect_ratio && !diag_factor && kind != "imshow") + { + if (aspect_ratio_ws > 1) + { + double border = 0.5 * (*vp_x_max - *vp_x_min) * (1.0 - 1.0 / aspect_ratio_ws); + *vp_x_min += border; + *vp_x_max -= border; + } + else if (aspect_ratio_ws <= 1) + { + double border = 0.5 * (*vp_y_max - *vp_y_min) * (1.0 - aspect_ratio_ws); + *vp_y_min += border; + *vp_y_max -= border; + } + } if (str_equals_any(kind, "wireframe", "surface", "plot3", "scatter3", "trisurface", "volume")) { @@ -734,7 +791,9 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptr start_aspect_ratio_ws) + { + right_margin /= (start_aspect_ratio_ws / aspect_ratio_ws); + } + else + { + if (aspect_ratio_ws < 1) right_margin /= aspect_ratio_ws; + } + } + if (x_label_margin && std::find(kinds_with_possible_label.begin(), kinds_with_possible_label.end(), kind) != + kinds_with_possible_label.end()) + bottom_margin = 0.05; if (kind == "marginal_heatmap") { - top_margin = title_margin ? 0.075 + 0.5 * (vp1 - vp0) * (1.0 - 1.0 / aspect_ratio_ws) - : 0.5 * (vp1 - vp0) * (1.0 - 1.0 / aspect_ratio_ws); - if (keep_aspect_ratio && uniform_data) right_margin += title_margin ? 0.075 : 0; + top_margin = right_margin + (title_margin ? 0.075 : 0.025); - if (keep_aspect_ratio && uniform_data) + if (keep_aspect_ratio && uniform_data && only_quadratic_aspect_ratio) { if (bottom_margin != left_margin) { @@ -761,31 +831,25 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptr top_margin) { - auto diff = (0.975 - top_margin) - (0.95 - right_margin); - top_margin += 0.5 * diff; - bottom_margin += 0.5 * diff; + top_margin += (0.975 - top_margin) - (0.95 - right_margin); } else { - auto diff = (0.95 - right_margin) - (0.975 - top_margin); - right_margin += 0.5 * diff; - left_margin += 0.5 * diff; + right_margin += (0.95 - right_margin) - (0.975 - top_margin); } } } else { top_margin = title_margin ? 0.075 : 0; - if (keep_aspect_ratio && uniform_data) + if (keep_aspect_ratio && uniform_data && only_quadratic_aspect_ratio) { if (bottom_margin != left_margin) { bottom_margin = grm_max(left_margin, bottom_margin); left_margin = bottom_margin; } - right_margin -= - 0.5 * (vp1 - vp0) * (1.0 - (aspect_ratio_ws > 1 ? 1.0 / aspect_ratio_ws : aspect_ratio_ws)) - top_margin; - right_margin = grm_max(right_margin, 0.1); + right_margin += top_margin; if (right_margin > top_margin) { auto diff = (0.975 - top_margin) - (0.95 - right_margin); @@ -802,7 +866,7 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptr(element->getAttribute("cols")); auto rows = static_cast(element->getAttribute("rows")); @@ -900,7 +962,7 @@ static void calculateViewport(const std::shared_ptr &element) vp[2] = static_cast(element->parentElement()->getAttribute("viewport_y_min")); vp[3] = static_cast(element->parentElement()->getAttribute("viewport_y_max")); - calculateCentralRegionMarginOrDiagFactor(element, &vp[0], &vp[1], &vp[2], &vp[3]); + calculateCentralRegionMarginOrDiagFactor(element, &vp[0], &vp[1], &vp[2], &vp[3], false); render->setViewport(element, vp[0], vp[1], vp[2], vp[3]); element->setAttribute("_viewport_x_min_org", vp[0]); @@ -911,9 +973,8 @@ static void calculateViewport(const std::shared_ptr &element) else if (element->localName() == "plot") { double vp[4]; - int pixel_width, pixel_height; + double metric_width, metric_height; double aspect_ratio_ws; - bool keep_aspect_ratio, uniform_data = true; std::string kind; /* when grids are being used for layouting the subplot information is stored in the parent of the plot */ @@ -931,42 +992,21 @@ static void calculateViewport(const std::shared_ptr &element) vp[2] = static_cast(element->getAttribute("plot_y_min")); vp[3] = static_cast(element->getAttribute("plot_y_max")); } - keep_aspect_ratio = static_cast(element->getAttribute("keep_aspect_ratio")); kind = static_cast(element->getAttribute("kind")); - for (const auto &series : element->querySelectors("central_region")->children()) - { - if (!starts_with(series->localName(), "series_")) continue; - uniform_data = isUniformData(series, render->getContext()); - if (!uniform_data) break; - } - if (kind == "marginal_heatmap" && uniform_data) - uniform_data = isUniformData(element->children()[0], render->getContext()); - - GRM::Render::getFigureSize(&pixel_width, &pixel_height, nullptr, nullptr); - aspect_ratio_ws = (double)pixel_width / pixel_height; + GRM::Render::getFigureSize(nullptr, nullptr, &metric_width, &metric_height); + aspect_ratio_ws = metric_width / metric_height; if (aspect_ratio_ws > 1) { vp[2] /= aspect_ratio_ws; vp[3] /= aspect_ratio_ws; - if (keep_aspect_ratio && uniform_data) - { - double border = 0.5 * (vp[1] - vp[0]) * (1.0 - 1.0 / aspect_ratio_ws); - vp[0] += border; - vp[1] -= border; - } } else { vp[0] *= aspect_ratio_ws; vp[1] *= aspect_ratio_ws; - if (keep_aspect_ratio && uniform_data) - { - double border = 0.5 * (vp[3] - vp[2]) * (1.0 - aspect_ratio_ws); - vp[2] += border; - vp[3] -= border; - } } + 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]); element->setAttribute("_viewport_x_min_org", vp[0]); @@ -979,10 +1019,11 @@ static void calculateViewport(const std::shared_ptr &element) double viewport[4], plot_viewport[4]; double offset = PLOT_DEFAULT_SIDEREGION_OFFSET, width = PLOT_DEFAULT_SIDEREGION_WIDTH; std::shared_ptr central_region; - 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; - int pixel_width, pixel_height; + double metric_width, metric_height, start_aspect_ratio_ws; + bool keep_aspect_ratio = false, uniform_data = true, only_quadratic_aspect_ratio = false; for (const auto &child : element->parentElement()->children()) { @@ -1005,20 +1046,83 @@ static void calculateViewport(const std::shared_ptr &element) viewport[1] = static_cast(central_region->getAttribute("_viewport_x_max_org")); viewport[2] = static_cast(central_region->getAttribute("_viewport_y_min_org")); viewport[3] = static_cast(central_region->getAttribute("_viewport_y_max_org")); + plot_viewport[0] = static_cast(plot_parent->getAttribute("plot_x_min")); + plot_viewport[1] = static_cast(plot_parent->getAttribute("plot_x_max")); + plot_viewport[2] = + 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")); + start_aspect_ratio_ws = static_cast(plot_parent->getAttribute("_start_aspect_ratio")); + kind = static_cast(plot_parent->getAttribute("kind")); - plot_viewport[0] = static_cast(plot_parent->getAttribute("_viewport_x_min_org")); - plot_viewport[1] = static_cast(plot_parent->getAttribute("_viewport_x_max_org")); - plot_viewport[2] = static_cast(plot_parent->getAttribute("_viewport_y_min_org")); - plot_viewport[3] = static_cast(plot_parent->getAttribute("_viewport_y_max_org")); + if (keep_aspect_ratio && only_quadratic_aspect_ratio) + { + for (const auto &series : plot_parent->querySelectors("central_region")->children()) + { + if (!starts_with(series->localName(), "series_")) continue; + uniform_data = isUniformData(series, render->getContext()); + if (!uniform_data) break; + } + if (kind == "marginal_heatmap" && uniform_data) + uniform_data = isUniformData(plot_parent->children()[0], render->getContext()); + if (uniform_data) + { + double border = + 0.5 * (plot_viewport[1] - plot_viewport[0]) * (1.0 - 1.0 / (DEFAULT_ASPECT_RATIO_FOR_SCALING)); + plot_viewport[0] += border; + plot_viewport[1] -= border; + } + } if (element->hasAttribute("offset")) offset = static_cast(element->getAttribute("offset")); if (element->hasAttribute("width")) width = static_cast(element->getAttribute("width")); + // TODO: Change this later when other elements than texts can be inside the side_regions which isn't displayed by + // the if condition + if (!element->hasAttribute("marginal_heatmap_side_plot") && element->querySelectors("colorbar") == nullptr) + { + offset = 0.0; + width = 0.0; + } - GRM::Render::getFigureSize(&pixel_width, &pixel_height, nullptr, nullptr); - auto aspect_ratio_ws = (double)pixel_width / pixel_height; + // apply text width to the side_region + if (location == "top" && plot_parent->hasAttribute("title_margin") && + static_cast(plot_parent->getAttribute("title_margin")) && + !element->hasAttribute("marginal_heatmap_side_plot")) + { + width += (0.025 + 0.075) * (plot_viewport[3] - plot_viewport[2]); + } + if (location == "left" && central_region->hasAttribute("y_label_margin") && + static_cast(central_region->getAttribute("y_label_margin")) && + std::find(kinds_with_possible_label.begin(), kinds_with_possible_label.end(), kind) != + kinds_with_possible_label.end()) + { + width += (0.075 + 0.05) * (plot_viewport[1] - plot_viewport[0]); + } + if (location == "bottom" && central_region->hasAttribute("x_label_margin") && + static_cast(central_region->getAttribute("x_label_margin")) && + std::find(kinds_with_possible_label.begin(), kinds_with_possible_label.end(), kind) != + kinds_with_possible_label.end()) + { + width += (0.075 + 0.05) * (plot_viewport[3] - plot_viewport[2]); + } - if (location == "right") + 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); + + // 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; if (aspect_ratio_ws <= 1) { offset_rel = offset * aspect_ratio_ws; @@ -1029,6 +1133,16 @@ static void calculateViewport(const std::shared_ptr &element) offset_rel = offset / aspect_ratio_ws; width_rel = width / aspect_ratio_ws; } + } + else + { + auto default_diag_factor = static_cast(element->getAttribute("_default_diag_factor")); + offset_rel = offset * diag_factor * default_diag_factor; + width_rel = width * diag_factor * default_diag_factor; + } + + if (location == "right") + { max_vp = getMaxViewport(element, true); render->setViewport(element, viewport[1] + offset_rel, grm_min(viewport[1] + offset_rel + width_rel, max_vp), viewport[2], viewport[3]); @@ -1039,36 +1153,16 @@ static void calculateViewport(const std::shared_ptr &element) } else if (location == "left") { - if (aspect_ratio_ws <= 1) - { - offset_rel = offset * aspect_ratio_ws; - width_rel = width * aspect_ratio_ws; - } - else - { - offset_rel = offset / aspect_ratio_ws; - width_rel = width / aspect_ratio_ws; - } min_vp = getMinViewport(element, true); - render->setViewport(element, grm_max(viewport[0], min_vp), viewport[0] + offset_rel + width_rel, viewport[2], - viewport[3]); - element->setAttribute("_viewport_x_min_org", grm_max(viewport[0], min_vp)); - element->setAttribute("_viewport_x_max_org", viewport[0] + offset_rel + width_rel); + render->setViewport(element, grm_max(viewport[0] - (offset_rel + width_rel), min_vp), viewport[0], + viewport[2], viewport[3]); + element->setAttribute("_viewport_x_min_org", grm_max(viewport[0] - (offset_rel + width_rel), min_vp)); + element->setAttribute("_viewport_x_max_org", viewport[0]); element->setAttribute("_viewport_y_min_org", viewport[2]); element->setAttribute("_viewport_y_max_org", viewport[3]); } else if (location == "top") { - if (aspect_ratio_ws <= 1) - { - offset_rel = offset * aspect_ratio_ws; - width_rel = width * aspect_ratio_ws; - } - else - { - offset_rel = offset / aspect_ratio_ws; - width_rel = width / aspect_ratio_ws; - } max_vp = getMaxViewport(element, false); render->setViewport(element, viewport[0], viewport[1], viewport[3] + offset_rel, grm_min(viewport[3] + offset_rel + width_rel, max_vp)); @@ -1079,31 +1173,21 @@ static void calculateViewport(const std::shared_ptr &element) } else if (location == "bottom") { - if (aspect_ratio_ws <= 1) - { - offset_rel = offset * aspect_ratio_ws; - width_rel = width * aspect_ratio_ws; - } - else - { - offset_rel = offset / aspect_ratio_ws; - width_rel = width / aspect_ratio_ws; - } min_vp = getMinViewport(element, false); - render->setViewport(element, viewport[0], viewport[1], grm_max(viewport[2], min_vp), - viewport[2] + offset_rel + width_rel); + render->setViewport(element, viewport[0], viewport[1], + grm_max(viewport[2] - (offset_rel + width_rel), min_vp), viewport[2]); element->setAttribute("_viewport_x_min_org", viewport[0]); element->setAttribute("_viewport_x_max_org", viewport[1]); - element->setAttribute("_viewport_y_min_org", grm_max(viewport[2], min_vp)); - element->setAttribute("_viewport_y_max_org", viewport[2] + offset_rel + width_rel); + element->setAttribute("_viewport_y_min_org", grm_max(viewport[2] - (offset_rel + width_rel), min_vp)); + element->setAttribute("_viewport_y_max_org", viewport[2]); } } else if (element->localName() == "colorbar") // TODO: adjust this calculation when texts are included in side_region { - auto vp_x_min = static_cast(element->parentElement()->getAttribute("_viewport_x_min_org")); - auto vp_x_max = static_cast(element->parentElement()->getAttribute("_viewport_x_max_org")); - auto vp_y_min = static_cast(element->parentElement()->getAttribute("_viewport_y_min_org")); - auto vp_y_max = static_cast(element->parentElement()->getAttribute("_viewport_y_max_org")); + auto vp_x_min = static_cast(element->parentElement()->getAttribute("viewport_x_min")); + auto vp_x_max = static_cast(element->parentElement()->getAttribute("viewport_x_max")); + auto vp_y_min = static_cast(element->parentElement()->getAttribute("viewport_y_min")); + auto vp_y_max = static_cast(element->parentElement()->getAttribute("viewport_y_max")); render->setViewport(element, vp_x_min, vp_x_max, vp_y_min, vp_y_max); element->setAttribute("_viewport_x_min_org", vp_x_min); element->setAttribute("_viewport_x_max_org", vp_x_max); @@ -1122,6 +1206,185 @@ static void calculateViewport(const std::shared_ptr &element) element->setAttribute("_viewport_y_min_org", vp_y_min); element->setAttribute("_viewport_y_max_org", vp_y_max); } + else if (element->localName() == "legend") + { + int location = PLOT_DEFAULT_LOCATION; + double px, py, w, h; + double viewport[4]; + double vp_x_min, vp_x_max, vp_y_min, vp_y_max; + double scale_factor = 1.0, start_aspect_ratio_ws; + 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; + bool keep_aspect_ratio = false; + + for (const auto &child : element->parentElement()->children()) + { + if (child->localName() == "central_region") + { + central_region = child; + break; + } + } + + viewport[0] = static_cast(central_region->getAttribute("_viewport_x_min_org")); + viewport[1] = static_cast(central_region->getAttribute("_viewport_x_max_org")); + viewport[2] = static_cast(central_region->getAttribute("_viewport_y_min_org")); + viewport[3] = static_cast(central_region->getAttribute("_viewport_y_max_org")); + + if (element->hasAttribute("location")) + { + if (element->getAttribute("location").isInt()) + { + location = static_cast(element->getAttribute("location")); + } + else if (element->getAttribute("location").isString()) + { + location = locationStringToInt(static_cast(element->getAttribute("location"))); + } + } + else + { + element->setAttribute("location", location); + } + keep_aspect_ratio = static_cast(element->parentElement()->getAttribute("keep_aspect_ratio")); + start_aspect_ratio_ws = static_cast(element->parentElement()->getAttribute("_start_aspect_ratio")); + kind = static_cast(element->parentElement()->getAttribute("kind")); + + if (!keep_aspect_ratio) + { + double metric_width, metric_height; + GRM::Render::getFigureSize(nullptr, nullptr, &metric_width, &metric_height); + auto aspect_ratio_ws = metric_width / metric_height; + + scale_factor *= DEFAULT_ASPECT_RATIO_FOR_SCALING; + scale_factor *= (aspect_ratio_ws <= 1) ? aspect_ratio_ws : 1.0 / aspect_ratio_ws; + } + 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 default_diag_factor = static_cast(element->getAttribute("_default_diag_factor")); + + scale_factor = diag_factor * default_diag_factor; + } + element->setAttribute("_scale_factor", scale_factor); + + if (kind != "pie") + { + legendSize(labels, &w, &h); + if (element->hasAttribute("_start_w")) + { + w = static_cast(element->getAttribute("_start_w")); + } + else + { + element->setAttribute("_start_w", w); + } + if (element->hasAttribute("_start_h")) + { + h = static_cast(element->getAttribute("_start_h")); + } + else + { + element->setAttribute("_start_h", h); + } + + if (int_equals_any(location, 3, 11, 12, 13)) + { + px = viewport[1] + 0.11 * scale_factor; + } + else if (int_equals_any(location, 3, 8, 9, 10)) + { + px = 0.5 * (viewport[0] + viewport[1] - (w - 0.05) * scale_factor); + } + else if (int_equals_any(location, 3, 2, 3, 6)) + { + px = viewport[0] + 0.11 * scale_factor; + } + else + { + px = viewport[1] - (0.05 + w) * scale_factor; + } + if (int_equals_any(location, 5, 5, 6, 7, 10, 12)) + { + py = 0.5 * (viewport[2] + viewport[3] + h * scale_factor) - 0.03 * scale_factor; + } + else if (location == 13) + { + py = viewport[2] + h * scale_factor; + } + else if (int_equals_any(location, 3, 3, 4, 8)) + { + py = viewport[2] + (h + 0.03) * scale_factor; + } + else if (location == 11) + { + py = viewport[3] - 0.03 * scale_factor; + } + else + { + py = viewport[3] - 0.06 * scale_factor; + } + vp_x_min = px - 0.08 * scale_factor; + vp_x_max = px + (w + 0.02) * scale_factor; + vp_y_min = py - h * scale_factor; + vp_y_max = py + 0.03 * scale_factor; + } + else + { + double tbx[4], tby[4]; + int num_labels = labels.size(); + + w = 0; + h = 0; + for (auto current_label : labels) + { + gr_inqtext(0, 0, current_label.data(), tbx, tby); + w += tbx[2] - tbx[0]; + h = grm_max(h, tby[2] - tby[0]); + } + w += num_labels * 0.03 + (num_labels - 1) * 0.02; + + if (element->hasAttribute("_start_w")) + { + w = static_cast(element->getAttribute("_start_w")); + } + else + { + element->setAttribute("_start_w", w); + } + if (element->hasAttribute("_start_h")) + { + h = static_cast(element->getAttribute("_start_h")); + } + else + { + element->setAttribute("_start_h", h); + } + + px = 0.5 * (viewport[0] + viewport[1] - w * scale_factor); + py = viewport[2] - 0.75 * h * scale_factor; + + vp_x_min = px - 0.02 * scale_factor; + vp_x_max = px + (w + 0.02) * scale_factor; + vp_y_min = py - (0.5 * h + 0.02) * scale_factor; + vp_y_max = py + (0.5 * h + 0.02) * scale_factor; + } + + render->setViewport(element, vp_x_min, vp_x_max, vp_y_min, vp_y_max); + element->setAttribute("_viewport_x_min_org", vp_x_min); + element->setAttribute("_viewport_x_max_org", vp_x_max); + element->setAttribute("_viewport_y_min_org", vp_y_min); + element->setAttribute("_viewport_y_max_org", vp_y_max); + } GRM::Render::processViewport(element); } @@ -1145,7 +1408,8 @@ static void applyMoveTransformation(const std::shared_ptr &element "layout_grid", "central_region", "side_region", - "marginal_heatmap_plot"}; + "marginal_heatmap_plot", + "legend"}; if (std::find(ndc_transformation_elems.begin(), ndc_transformation_elems.end(), element->localName()) != ndc_transformation_elems.end()) @@ -1214,12 +1478,9 @@ static void applyMoveTransformation(const std::shared_ptr &element { // elements in ndc space gets transformed in ndc space which is equal to changing their viewport double diff; - int pixel_width, pixel_height; double vp_border_x_min = 0.0, vp_border_x_max, vp_border_y_min = 0.0, vp_border_y_max; bool private_shift = false; - GRM::Render::getFigureSize(&pixel_width, &pixel_height, nullptr, nullptr); - if (element->hasAttribute("viewport_x_min")) { vp_org[0] = static_cast(element->getAttribute("_viewport_x_min_org")); @@ -1254,6 +1515,8 @@ static void applyMoveTransformation(const std::shared_ptr &element private_shift = true; } + if (element->localName() == "text") gr_settextoffset(x_shift, -y_shift); + // when the element contains an axes the max viewport must be smaller than normal to respect the axes vp_border_x_min = getMinViewport(element, true); vp_border_x_max = getMaxViewport(element, true); @@ -1317,7 +1580,7 @@ static void applyMoveTransformation(const std::shared_ptr &element w[2] = w[2] / y_scale - y_shift; w[3] = w[3] / y_scale - y_shift; } - gr_setwindow(w[0], w[1], w[2], w[3]); + if (w[1] - w[0] > 0.0 && w[3] - w[2] > 0.0) gr_setwindow(w[0], w[1], w[2], w[3]); } } @@ -1894,25 +2157,86 @@ static void getTickSize(const std::shared_ptr &element, double &ti if (element->hasAttribute("tick_size")) { double plot_viewport[2], tick_size_rel; - int pixel_width, pixel_height; + double metric_width, metric_height; + bool keep_aspect_ratio = false, uniform_data = true, only_quadratic_aspect_ratio = false; auto plot_parent = element->parentElement(); getPlotParent(plot_parent); plot_viewport[0] = static_cast(plot_parent->getAttribute("_viewport_x_min_org")); plot_viewport[1] = static_cast(plot_parent->getAttribute("_viewport_x_max_org")); - GRM::Render::getFigureSize(&pixel_width, &pixel_height, nullptr, nullptr); - auto aspect_ratio_ws = (double)pixel_width / pixel_height; + GRM::Render::getFigureSize(nullptr, nullptr, &metric_width, &metric_height); + auto aspect_ratio_ws = metric_width / metric_height; tick_size = static_cast(element->getAttribute("tick_size")); - if (aspect_ratio_ws <= 1) + keep_aspect_ratio = static_cast(plot_parent->getAttribute("keep_aspect_ratio")); + only_quadratic_aspect_ratio = static_cast(plot_parent->getAttribute("only_quadratic_aspect_ratio")); + // special case for keep_aspect_ratio with uniform data which can lead to smaller plots + if (keep_aspect_ratio && only_quadratic_aspect_ratio) { - tick_size_rel = tick_size * aspect_ratio_ws; + auto render = grm_get_render(); + auto kind = static_cast(plot_parent->getAttribute("kind")); + for (const auto &series : plot_parent->querySelectors("central_region")->children()) + { + if (!starts_with(series->localName(), "series_")) continue; + uniform_data = isUniformData(series, render->getContext()); + if (!uniform_data) break; + } + if (kind == "marginal_heatmap" && uniform_data) + uniform_data = isUniformData(plot_parent->children()[0], render->getContext()); + } + + if ((keep_aspect_ratio && uniform_data && only_quadratic_aspect_ratio) || !keep_aspect_ratio) + { + if (aspect_ratio_ws <= 1) + { + tick_size_rel = tick_size * aspect_ratio_ws; + } + else + { + tick_size_rel = tick_size / aspect_ratio_ws; + } + if (!element->hasAttribute("_tick_size_set_by_user")) tick_size_rel *= DEFAULT_ASPECT_RATIO_FOR_SCALING; } else { - tick_size_rel = tick_size / aspect_ratio_ws; + double viewport[4]; + double default_diag_factor; + std::shared_ptr central_region, central_region_parent; + auto kind = static_cast(plot_parent->getAttribute("kind")); + + central_region_parent = plot_parent; + if (kind == "marginal_heatmap") central_region_parent = plot_parent->children()[0]; + for (const auto &child : central_region_parent->children()) + { + if (child->localName() == "central_region") + { + central_region = child; + break; + } + } + + 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")); + 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 + { + 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; + element->setAttribute("_default_diag_factor", default_diag_factor); + } + tick_size_rel = tick_size * diag_factor * default_diag_factor; } tick_size = tick_size_rel; } @@ -2520,8 +2844,7 @@ void GRM::Render::getFigureSize(int *pixel_width, int *pixel_height, double *met std::string size_unit, size_type; std::array vars = {"x", "y"}; std::array default_size = {PLOT_DEFAULT_WIDTH, PLOT_DEFAULT_HEIGHT}; - - std::shared_ptr root = active_figure; + std::shared_ptr figure = active_figure; #ifdef __EMSCRIPTEN__ display_metric_width = 0.16384; @@ -2537,28 +2860,28 @@ void GRM::Render::getFigureSize(int *pixel_width, int *pixel_height, double *met dpi[1] = dpm[1] * 0.0254; /* TODO: Overwork this calculation */ - if (root->hasAttribute("fig_size_x") && root->hasAttribute("fig_size_y")) + if (figure->hasAttribute("fig_size_x") && figure->hasAttribute("fig_size_y")) { - tmp_size_d[0] = static_cast(root->getAttribute("fig_size_x")); - tmp_size_d[1] = static_cast(root->getAttribute("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 (root->hasAttribute("size_x") && root->hasAttribute("size_y")) + else if (figure->hasAttribute("size_x") && figure->hasAttribute("size_y")) { for (i = 0; i < 2; ++i) { - size_unit = (std::string)root->getAttribute("size_" + vars[i] + "_unit"); - size_type = (std::string)root->getAttribute("size_" + vars[i] + "_type"); + size_unit = static_cast(figure->getAttribute("size_" + vars[i] + "_unit")); + size_type = static_cast(figure->getAttribute("size_" + vars[i] + "_type")); if (size_unit.empty()) size_unit = "px"; tmp_size_d[i] = default_size[i]; if (size_type == "double" || size_type == "int") { - tmp_size_d[i] = static_cast(root->getAttribute("size_" + vars[i])); + tmp_size_d[i] = static_cast(figure->getAttribute("size_" + vars[i])); auto meters_per_unit_iter = symbol_to_meters_per_unit.find(size_unit); if (meters_per_unit_iter != symbol_to_meters_per_unit.end()) { @@ -3804,7 +4127,8 @@ void GRM::Render::processLimits(const std::shared_ptr &element) auto stored_window_ymin = static_cast(central_region->getAttribute("window_y_min")); auto stored_window_ymax = static_cast(central_region->getAttribute("window_y_max")); - gr_setwindow(stored_window_xmin, stored_window_xmax, stored_window_ymin, stored_window_ymax); + if (stored_window_xmax - stored_window_xmin > 0.0 && stored_window_ymax - stored_window_ymin > 0.0) + gr_setwindow(stored_window_xmin, stored_window_xmax, stored_window_ymin, stored_window_ymax); } else { @@ -4010,8 +4334,8 @@ static void processRelativeCharHeight(const std::shared_ptr &eleme std::shared_ptr central_region, central_region_parent, subplot_parent; auto kind = static_cast(plot_element->getAttribute("kind")); double diag_factor; - int pixel_width, pixel_height; - bool multiple_plots = false; + double metric_width, metric_height; + bool multiple_plots = false, uniform_data = true, keep_aspect_ratio = false, only_quadratic_aspect_ratio = false; subplot_parent = (plot_element->parentElement()->localName() == "layout_grid_element") ? subplot_parent = plot_element->parentElement() @@ -4039,6 +4363,8 @@ static void processRelativeCharHeight(const std::shared_ptr &eleme // is always set otherwise the method wouldn't be called max_char_height = static_cast(element->getAttribute("max_char_height")); + keep_aspect_ratio = static_cast(plot_element->getAttribute("keep_aspect_ratio")); + only_quadratic_aspect_ratio = static_cast(plot_element->getAttribute("only_quadratic_aspect_ratio")); subplot_viewport[0] = static_cast(subplot_parent->getAttribute("plot_x_min")); subplot_viewport[1] = static_cast(subplot_parent->getAttribute("plot_x_max")); @@ -4047,11 +4373,12 @@ static void processRelativeCharHeight(const std::shared_ptr &eleme subplot_viewport[3] = static_cast(subplot_parent->getAttribute("plot_y_max")) / (DEFAULT_ASPECT_RATIO_FOR_SCALING); + GRM::Render::getFigureSize(nullptr, nullptr, &metric_width, &metric_height); + auto aspect_ratio_ws = metric_width / metric_height; + // special case for keep_aspect_ratio with uniform data which can lead to smaller plots - if (plot_element->hasAttribute("keep_aspect_ratio") && - static_cast(plot_element->getAttribute("keep_aspect_ratio"))) + if (keep_aspect_ratio && only_quadratic_aspect_ratio) { - bool uniform_data = true; auto render = grm_get_render(); for (const auto &series : plot_element->querySelectors("central_region")->children()) { @@ -4070,23 +4397,56 @@ static void processRelativeCharHeight(const std::shared_ptr &eleme } } - // calculate the diagonal viewport size of the default viewport with the fix aspect_ratio 4/3 - calculateCentralRegionMarginOrDiagFactor(element, &subplot_viewport[0], &subplot_viewport[1], &subplot_viewport[2], - &subplot_viewport[3]); - diag_factor = std::sqrt((subplot_viewport[1] - subplot_viewport[0]) * (subplot_viewport[1] - subplot_viewport[0]) + - (subplot_viewport[3] - subplot_viewport[2]) * (subplot_viewport[3] - subplot_viewport[2])); + if ((keep_aspect_ratio && uniform_data && only_quadratic_aspect_ratio) || !keep_aspect_ratio) + { + // calculate the diagonal viewport size of the default viewport with the fix aspect_ratio 4/3 + calculateCentralRegionMarginOrDiagFactor(element, &subplot_viewport[0], &subplot_viewport[1], + &subplot_viewport[2], &subplot_viewport[3], true); + diag_factor = + std::sqrt((subplot_viewport[1] - subplot_viewport[0]) * (subplot_viewport[1] - subplot_viewport[0]) + + (subplot_viewport[3] - subplot_viewport[2]) * (subplot_viewport[3] - subplot_viewport[2])); + } + else + { + double default_diag_factor; + 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 + { + calculateCentralRegionMarginOrDiagFactor(element, &subplot_viewport[0], &subplot_viewport[1], + &subplot_viewport[2], &subplot_viewport[3], true); + double plot_diag_factor = + std::sqrt((subplot_viewport[1] - subplot_viewport[0]) * (subplot_viewport[1] - subplot_viewport[0]) + + (subplot_viewport[3] - subplot_viewport[2]) * (subplot_viewport[3] - subplot_viewport[2])); + 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); + element->setAttribute("_default_diag_factor", default_diag_factor); + } + diag_factor *= default_diag_factor; + } if (!element->hasAttribute("diag_factor")) element->setAttribute("diag_factor", diag_factor); - GRM::Render::getFigureSize(&pixel_width, &pixel_height, nullptr, nullptr); - auto aspect_ratio_ws = (double)pixel_width / pixel_height; - - if (aspect_ratio_ws <= 1) + if ((keep_aspect_ratio && uniform_data && only_quadratic_aspect_ratio) || !keep_aspect_ratio) { - max_char_height_rel = max_char_height * aspect_ratio_ws; + if (!element->hasAttribute("_max_char_height_set_by_user")) max_char_height *= DEFAULT_ASPECT_RATIO_FOR_SCALING; + if (aspect_ratio_ws <= 1) + { + max_char_height_rel = max_char_height * aspect_ratio_ws; + } + else + { + max_char_height_rel = max_char_height / aspect_ratio_ws; + } } else { - max_char_height_rel = max_char_height / aspect_ratio_ws; + max_char_height_rel = max_char_height; } gr_setcharheight(max_char_height_rel * diag_factor); @@ -4370,25 +4730,29 @@ std::string tickOrientationIntToString(int tick_orientation) static void processTitle(const std::shared_ptr &element) { double viewport[4]; + std::shared_ptr side_region = nullptr, plot_parent = element; del_values del = del_values::update_without_default; + getPlotParent(plot_parent); - 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")); + side_region = plot_parent->querySelectors("side_region[location=\"top\"]"); + if (side_region == nullptr) return; + viewport[0] = static_cast(side_region->getAttribute("viewport_x_min")); + viewport[1] = static_cast(side_region->getAttribute("viewport_x_max")); + viewport[2] = static_cast(side_region->getAttribute("viewport_y_min")); + viewport[3] = static_cast(side_region->getAttribute("viewport_y_max")); - double x = 0.5 * (viewport[0] + viewport[1]); // prev viewport from central_region + double x = 0.5 * (viewport[0] + viewport[1]); double y = viewport[3]; - auto title = static_cast(element->getAttribute("title")); + auto title = static_cast(plot_parent->getAttribute("title")); if (title.empty()) return; // Empty title is pointless, no need to waste the space for nothing - auto kind = static_cast(getSubplotElement(element)->getAttribute("kind")); + auto kind = static_cast(plot_parent->getAttribute("kind")); if (kind == "imshow" || kind == "isosurface") return; // Don't draw a title for imshow and isosurface if (auto render = std::dynamic_pointer_cast(element->ownerDocument())) { // title is unique so child_id isn't needed here - del = del_values(static_cast(element->getAttribute("_delete_children"))); - auto title_elem = element->querySelectors("[name=\"title\"]"); + del = del_values(static_cast(side_region->getAttribute("_delete_children"))); + auto title_elem = plot_parent->querySelectors("[name=\"title\"]"); if ((del != del_values::update_without_default && del != del_values::update_with_default) || title_elem == nullptr) { @@ -4397,14 +4761,7 @@ static void processTitle(const std::shared_ptr &element) title_elem->setAttribute("name", "title"); title_elem->setAttribute("z_index", 2); render->setTextAlign(title_elem, GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_TOP); - if (static_cast(element->getAttribute("kind")) == "marginal_heatmap") - { - element->querySelectors("marginal_heatmap_plot")->append(title_elem); - } - else - { - element->append(title_elem); - } + side_region->append(title_elem); } else if (title_elem != nullptr) { @@ -4436,7 +4793,10 @@ void GRM::Render::processWindow(const std::shared_ptr &element) } else { - if (kind != "pie") gr_setwindow(xmin, xmax, ymin, ymax); + if (kind != "pie") + { + if (xmax - xmin > 0.0 && ymax - ymin > 0.0) gr_setwindow(xmin, xmax, ymin, ymax); + } } if (str_equals_any(kind, "wireframe", "surface", "plot3", "scatter3", "trisurface", "volume", "isosurface")) { @@ -4448,7 +4808,7 @@ void GRM::Render::processWindow(const std::shared_ptr &element) } else { - gr_setwindow(xmin, xmax, ymin, ymax); + if (xmax - xmin > 0.0 && ymax - ymin > 0.0) gr_setwindow(xmin, xmax, ymin, ymax); } } @@ -4494,7 +4854,7 @@ void GRM::Render::processViewport(const std::shared_ptr &element) viewport[3] = static_cast(element->getAttribute("viewport_y_max")); // TODO: Change this workaround when all elements with viewports really have a valid viewport - if (viewport[1] - viewport[0] > 0.0 && viewport[3] - viewport[2] > 0) + if (viewport[1] - viewport[0] > 0.0 && viewport[3] - viewport[2] > 0.0) { gr_setviewport(viewport[0], viewport[1], viewport[2], viewport[3]); } @@ -4511,7 +4871,8 @@ void GRM::Render::calculateCharHeight(const std::shared_ptr &eleme double subplot_viewport[4]; // the subplot vp is the figure vp unless there are more plots inside a figure double char_height, diag_factor; std::string kind; - int pixel_width, pixel_height; + double metric_width, metric_height; + bool keep_aspect_ratio = false, uniform_data = true, only_quadratic_aspect_ratio = false; std::shared_ptr plot_parent = element, subplot_parent; getPlotParent(plot_parent); @@ -4530,12 +4891,15 @@ void GRM::Render::calculateCharHeight(const std::shared_ptr &eleme subplot_viewport[3] = static_cast(subplot_parent->getAttribute("plot_y_max")) / (DEFAULT_ASPECT_RATIO_FOR_SCALING); kind = static_cast(plot_parent->getAttribute("kind")); + keep_aspect_ratio = static_cast(plot_parent->getAttribute("keep_aspect_ratio")); + only_quadratic_aspect_ratio = static_cast(plot_parent->getAttribute("only_quadratic_aspect_ratio")); + + GRM::Render::getFigureSize(nullptr, nullptr, &metric_width, &metric_height); + auto aspect_ratio_ws = metric_width / metric_height; // special case for keep_aspect_ratio with uniform data which can lead to smaller plots - if (plot_parent->hasAttribute("keep_aspect_ratio") && - static_cast(plot_parent->getAttribute("keep_aspect_ratio"))) + if (keep_aspect_ratio && only_quadratic_aspect_ratio) { - bool uniform_data = true; auto render = grm_get_render(); for (const auto &series : plot_parent->querySelectors("central_region")->children()) { @@ -4554,15 +4918,37 @@ void GRM::Render::calculateCharHeight(const std::shared_ptr &eleme } } - // calculate the diagonal viewport size of the default viewport with the fix aspect_ratio 4/3 - calculateCentralRegionMarginOrDiagFactor(element, &subplot_viewport[0], &subplot_viewport[1], &subplot_viewport[2], - &subplot_viewport[3]); - diag_factor = std::sqrt((subplot_viewport[1] - subplot_viewport[0]) * (subplot_viewport[1] - subplot_viewport[0]) + - (subplot_viewport[3] - subplot_viewport[2]) * (subplot_viewport[3] - subplot_viewport[2])); - if (!element->hasAttribute("diag_factor")) element->setAttribute("diag_factor", diag_factor); + if ((keep_aspect_ratio && uniform_data && only_quadratic_aspect_ratio) || !keep_aspect_ratio) + { + // calculate the diagonal viewport size of the default viewport with the fix aspect_ratio 4/3 + calculateCentralRegionMarginOrDiagFactor(element, &subplot_viewport[0], &subplot_viewport[1], + &subplot_viewport[2], &subplot_viewport[3], true); + diag_factor = + std::sqrt((subplot_viewport[1] - subplot_viewport[0]) * (subplot_viewport[1] - subplot_viewport[0]) + + (subplot_viewport[3] - subplot_viewport[2]) * (subplot_viewport[3] - subplot_viewport[2])); + } + else + { + 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")) + { + calculateCentralRegionMarginOrDiagFactor(element, &subplot_viewport[0], &subplot_viewport[1], + &subplot_viewport[2], &subplot_viewport[3], true); + double plot_diag_factor = + std::sqrt((subplot_viewport[1] - subplot_viewport[0]) * (subplot_viewport[1] - subplot_viewport[0]) + + (subplot_viewport[3] - subplot_viewport[2]) * (subplot_viewport[3] - subplot_viewport[2])); + 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); + element->setAttribute("_default_diag_factor", default_diag_factor); + } + diag_factor *= static_cast(element->getAttribute("_default_diag_factor")); + } - GRM::Render::getFigureSize(&pixel_width, &pixel_height, nullptr, nullptr); - double aspect_ratio_ws = (double)pixel_width / pixel_height; + if (!element->hasAttribute("diag_factor")) element->setAttribute("diag_factor", diag_factor); if (str_equals_any(kind, "wireframe", "surface", "plot3", "scatter3", "trisurface", "volume")) { @@ -4578,13 +4964,17 @@ void GRM::Render::calculateCharHeight(const std::shared_ptr &eleme } char_height *= diag_factor; - if (aspect_ratio_ws > 1) - { - char_height /= aspect_ratio_ws; - } - else + if ((keep_aspect_ratio && uniform_data && only_quadratic_aspect_ratio) || !keep_aspect_ratio) { - char_height *= aspect_ratio_ws; + if (aspect_ratio_ws > 1) + { + char_height /= aspect_ratio_ws; + } + else + { + char_height *= aspect_ratio_ws; + } + if (!element->hasAttribute("_char_height_set_by_user")) char_height *= DEFAULT_ASPECT_RATIO_FOR_SCALING; } plot_parent->setAttribute("char_height", char_height); @@ -4593,45 +4983,54 @@ void GRM::Render::calculateCharHeight(const std::shared_ptr &eleme static void processXlabel(const std::shared_ptr &element) { - double viewport[4], plot_viewport[4], char_height; + double viewport[4], char_height; + std::string kind; del_values del = del_values::update_without_default; - std::shared_ptr plot_parent = element->parentElement(); + std::shared_ptr side_region = nullptr, plot_parent = element; getPlotParent(plot_parent); - auto coordinate_system = element->parentElement(); - auto central_region = coordinate_system->parentElement(); + auto coordinate_system = plot_parent->querySelectors("coordinate_system"); gr_inqcharheight(&char_height); - 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")); - plot_viewport[0] = static_cast(plot_parent->getAttribute("viewport_x_min")); - plot_viewport[1] = static_cast(plot_parent->getAttribute("viewport_x_max")); - plot_viewport[2] = static_cast(plot_parent->getAttribute("viewport_y_min")); - plot_viewport[3] = static_cast(plot_parent->getAttribute("viewport_y_max")); + side_region = plot_parent->querySelectors("side_region[location=\"bottom\"]"); + if (side_region == nullptr) return; + viewport[0] = static_cast(side_region->getAttribute("viewport_x_min")); + viewport[1] = static_cast(side_region->getAttribute("viewport_x_max")); + viewport[2] = static_cast(side_region->getAttribute("viewport_y_min")); + viewport[3] = static_cast(side_region->getAttribute("viewport_y_max")); + kind = static_cast(plot_parent->getAttribute("kind")); double x = 0.5 * (viewport[0] + viewport[1]); - double y = plot_viewport[2] + 0.5 * char_height; + double y = viewport[2] + 0.5 * char_height; auto x_label = static_cast(coordinate_system->getAttribute("x_label")); if (x_label.empty()) return; // Empty xlabel is pointless, no need to waste the space for nothing if (auto render = std::dynamic_pointer_cast(element->ownerDocument())) { - del = del_values(static_cast(element->getAttribute("_delete_children"))); - auto xlabel_elem = element->querySelectors("[name=\"x_label\"]"); + del = del_values(static_cast(side_region->getAttribute("_delete_children"))); + auto xlabel_elem = side_region->querySelectors("text[name=\"x_label\"]"); - if ((del != del_values::update_without_default && del != del_values::update_with_default) || - xlabel_elem == nullptr) + if (((del != del_values::update_without_default && del != del_values::update_with_default) || + xlabel_elem == nullptr) && + std::find(kinds_with_possible_label.begin(), kinds_with_possible_label.end(), kind) != + kinds_with_possible_label.end()) { if (xlabel_elem != nullptr) xlabel_elem->remove(); xlabel_elem = render->createText(x, y, x_label); render->setTextAlign(xlabel_elem, GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_BOTTOM); - element->appendChild(xlabel_elem); + side_region->appendChild(xlabel_elem); } else if (xlabel_elem != nullptr) { - render->createText(x, y, x_label, CoordinateSpace::NDC, xlabel_elem); + if (std::find(kinds_with_possible_label.begin(), kinds_with_possible_label.end(), kind) != + kinds_with_possible_label.end()) + { + render->createText(x, y, x_label, CoordinateSpace::NDC, xlabel_elem); + } + else + { + xlabel_elem->remove(); + } } if (xlabel_elem != nullptr) xlabel_elem->setAttribute("name", "x_label"); } @@ -4639,48 +5038,56 @@ static void processXlabel(const std::shared_ptr &element) static void processYlabel(const std::shared_ptr &element) { - double viewport[4], plot_viewport[4], char_height; - bool keep_aspect_ratio; + double viewport[4], char_height; + std::string kind; del_values del = del_values::update_without_default; - std::shared_ptr plot_parent = element->parentElement(); + std::shared_ptr plot_parent = element->parentElement(), side_region = nullptr; getPlotParent(plot_parent); - auto coordinate_system = element->parentElement(); + auto coordinate_system = plot_parent->querySelectors("coordinate_system"); auto central_region = coordinate_system->parentElement(); - gr_inqcharheight(&char_height); - 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")); - plot_viewport[0] = static_cast(plot_parent->getAttribute("viewport_x_min")); - plot_viewport[1] = static_cast(plot_parent->getAttribute("viewport_x_max")); - plot_viewport[2] = static_cast(plot_parent->getAttribute("viewport_y_min")); - plot_viewport[3] = static_cast(plot_parent->getAttribute("viewport_y_max")); - keep_aspect_ratio = static_cast(plot_parent->getAttribute("keep_aspect_ratio")); + gr_inqcharheight(&char_height); + side_region = plot_parent->querySelectors("side_region[location=\"left\"]"); + if (side_region == nullptr) return; + viewport[0] = static_cast(side_region->getAttribute("viewport_x_min")); + viewport[1] = static_cast(side_region->getAttribute("viewport_x_max")); + viewport[2] = static_cast(side_region->getAttribute("viewport_y_min")); + viewport[3] = static_cast(side_region->getAttribute("viewport_y_max")); + kind = static_cast(plot_parent->getAttribute("kind")); - double x = ((keep_aspect_ratio) ? 0.925 : 1) * plot_viewport[0] + 0.5 * char_height; + double x = viewport[0] + 0.5 * char_height; double y = 0.5 * (viewport[2] + viewport[3]); auto y_label = static_cast(coordinate_system->getAttribute("y_label")); if (y_label.empty()) return; // Empty ylabel is pointless, no need to waste the space for nothing if (auto render = std::dynamic_pointer_cast(element->ownerDocument())) { - del = del_values(static_cast(element->getAttribute("_delete_children"))); - auto ylabel_elem = element->querySelectors("[name=\"y_label\"]"); + del = del_values(static_cast(side_region->getAttribute("_delete_children"))); + auto ylabel_elem = side_region->querySelectors("text[name=\"y_label\"]"); - if ((del != del_values::update_without_default && del != del_values::update_with_default) || - ylabel_elem == nullptr) + if (((del != del_values::update_without_default && del != del_values::update_with_default) || + ylabel_elem == nullptr) && + std::find(kinds_with_possible_label.begin(), kinds_with_possible_label.end(), kind) != + kinds_with_possible_label.end()) { if (ylabel_elem != nullptr) ylabel_elem->remove(); ylabel_elem = render->createText(x, y, y_label); render->setTextAlign(ylabel_elem, GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_TOP); render->setCharUp(ylabel_elem, -1, 0); - element->appendChild(ylabel_elem); + side_region->appendChild(ylabel_elem); } else if (ylabel_elem != nullptr) { - render->createText(x, y, y_label, CoordinateSpace::NDC, ylabel_elem); + if (std::find(kinds_with_possible_label.begin(), kinds_with_possible_label.end(), kind) != + kinds_with_possible_label.end()) + { + render->createText(x, y, y_label, CoordinateSpace::NDC, ylabel_elem); + } + else + { + ylabel_elem->remove(); + } } if (ylabel_elem != nullptr) ylabel_elem->setAttribute("name", "y_label"); } @@ -4863,7 +5270,7 @@ static void processBackgroundColor(const std::shared_ptr &element) if (element->hasAttribute("background_color")) { double vp[4]; - int pixel_width, pixel_height; + double metric_width, metric_height; double aspect_ratio_ws; std::shared_ptr plot_elem = element; getPlotParent(plot_elem); @@ -4873,8 +5280,8 @@ static void processBackgroundColor(const std::shared_ptr &element) vp[2] = static_cast(plot_elem->getAttribute("plot_y_min")); vp[3] = static_cast(plot_elem->getAttribute("plot_y_max")); - GRM::Render::getFigureSize(&pixel_width, &pixel_height, nullptr, nullptr); - aspect_ratio_ws = (double)pixel_width / pixel_height; + GRM::Render::getFigureSize(nullptr, nullptr, &metric_width, &metric_height); + aspect_ratio_ws = metric_width / metric_height; auto background_color_index = static_cast(element->getAttribute("background_color")); gr_savestate(); @@ -6476,8 +6883,6 @@ static void processIsosurface(const std::shared_ptr &element, static void processLegend(const std::shared_ptr &element, const std::shared_ptr &context) { - double viewport[4]; - double px, py, w, h; double tbx[4], tby[4]; std::shared_ptr render; std::string labels_key = static_cast(element->getAttribute("labels")); @@ -6507,76 +6912,26 @@ static void processLegend(const std::shared_ptr &element, const st throw NotFoundError("No render-document found for element\n"); } - 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")); + calculateViewport(element); + applyMoveTransformation(element); if (static_cast(plot_parent->getAttribute("kind")) != "pie") { - int location = PLOT_DEFAULT_LOCATION; double legend_symbol_x[2], legend_symbol_y[2]; int i; + double viewport[4]; std::shared_ptr fr, dr; gr_savestate(); auto specs_key = static_cast(element->getAttribute("specs")); std::vector specs = GRM::get>((*context)[specs_key]); - if (element->hasAttribute("location")) - { - if (element->getAttribute("location").isInt()) - { - location = static_cast(element->getAttribute("location")); - } - else if (element->getAttribute("location").isString()) - { - location = locationStringToInt(static_cast(element->getAttribute("location"))); - } - } - else - { - element->setAttribute("location", location); - } - - legendSize(labels, &w, &h); + auto scale_factor = static_cast(element->getAttribute("_scale_factor")); - if (int_equals_any(location, 3, 11, 12, 13)) - { - px = viewport[1] + 0.11; - } - else if (int_equals_any(location, 3, 8, 9, 10)) - { - px = 0.5 * (viewport[0] + viewport[1] - w + 0.05); - } - else if (int_equals_any(location, 3, 2, 3, 6)) - { - px = viewport[0] + 0.11; - } - else - { - px = viewport[1] - 0.05 - w; - } - if (int_equals_any(location, 5, 5, 6, 7, 10, 12)) - { - py = 0.5 * (viewport[2] + viewport[3] + h) - 0.03; - } - else if (location == 13) - { - py = viewport[2] + h; - } - else if (int_equals_any(location, 3, 3, 4, 8)) - { - py = viewport[2] + h + 0.03; - } - else if (location == 11) - { - py = viewport[3] - 0.03; - } - else - { - py = viewport[3] - 0.06; - } + 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")); del = del_values(static_cast(element->getAttribute("_delete_children"))); @@ -6616,14 +6971,14 @@ static void processLegend(const std::shared_ptr &element, const st if (del != del_values::update_without_default && del != del_values::update_with_default) { - fr = render->createFillRect(px - 0.08, px + w + 0.02, py + 0.03, py - h); + fr = render->createFillRect(viewport[0], viewport[1], viewport[3], viewport[2]); fr->setAttribute("_child_id", child_id++); element->append(fr); } else { fr = element->querySelectors("fill_rect[_child_id=" + std::to_string(child_id++) + "]"); - if (fr != nullptr) render->createFillRect(px - 0.08, px + w + 0.02, py + 0.03, py - h, 0, 0, -1, fr); + if (fr != nullptr) render->createFillRect(viewport[0], viewport[1], viewport[3], viewport[2], 0, 0, -1, fr); } render->setFillIntStyle(element, GKS_K_INTSTYLE_SOLID); @@ -6631,14 +6986,14 @@ static void processLegend(const std::shared_ptr &element, const st if (del != del_values::update_without_default && del != del_values::update_with_default) { - dr = render->createDrawRect(px - 0.08, px + w + 0.02, py + 0.03, py - h); + dr = render->createDrawRect(viewport[0], viewport[1], viewport[3], viewport[2]); dr->setAttribute("_child_id", child_id++); element->append(dr); } else { dr = element->querySelectors("draw_rect[_child_id=" + std::to_string(child_id++) + "]"); - if (dr != nullptr) render->createDrawRect(px - 0.08, px + w + 0.02, py + 0.03, py - h, dr); + if (dr != nullptr) render->createDrawRect(viewport[0], viewport[1], viewport[3], viewport[2], dr); } if (dr != nullptr && del != del_values::update_without_default) @@ -6652,191 +7007,204 @@ static void processLegend(const std::shared_ptr &element, const st render->setLineSpec(element, const_cast(" ")); int spec_i = 0; - for (const auto &child : element->parentElement()->children()) // central_region childs + for (const auto &plot_child : element->parentElement()->children()) // central_region childs { - int mask; - double dy; + if (plot_child->localName() != "central_region") continue; + for (const auto &child : plot_child->children()) + { + int mask; + double dy; - if (child->localName() != "series_line" && child->localName() != "series_scatter") continue; + if (child->localName() != "series_line" && child->localName() != "series_scatter") continue; - if (i <= labels.size()) - { - gr_inqtext(0, 0, labels[i].data(), tbx, tby); - dy = grm_max((tby[2] - tby[0]) - 0.03, 0); - py -= 0.5 * dy; - } + if (i <= labels.size()) + { + gr_inqtext(0, 0, labels[i].data(), tbx, tby); + dy = grm_max((tby[2] - tby[0]) - 0.03 * scale_factor, 0); + viewport[3] -= 0.5 * dy; + } - gr_savestate(); - mask = gr_uselinespec(specs[spec_i].data()); - gr_restorestate(); + gr_savestate(); + mask = gr_uselinespec(specs[spec_i].data()); + gr_restorestate(); - if (int_equals_any(mask, 5, 0, 1, 3, 4, 5)) - { - legend_symbol_x[0] = px - 0.07; - legend_symbol_x[1] = px - 0.01; - legend_symbol_y[0] = py; - legend_symbol_y[1] = py; - for (const auto &childchild : child->children()) + if (int_equals_any(mask, 5, 0, 1, 3, 4, 5)) { - std::shared_ptr pl; - if (childchild->localName() == "polyline") + legend_symbol_x[0] = viewport[0] + 0.01 * scale_factor; + legend_symbol_x[1] = viewport[0] + 0.07 * scale_factor; + legend_symbol_y[0] = viewport[3] - 0.03 * scale_factor; + legend_symbol_y[1] = viewport[3] - 0.03 * scale_factor; + for (const auto &childchild : child->children()) { - if (del != del_values::update_without_default && del != del_values::update_with_default) - { - pl = render->createPolyline(legend_symbol_x[0], legend_symbol_x[1], legend_symbol_y[0], - legend_symbol_y[1]); - pl->setAttribute("_child_id", child_id++); - element->append(pl); - } - else + std::shared_ptr pl; + if (childchild->localName() == "polyline") { - pl = element->querySelectors("polyline[_child_id=" + std::to_string(child_id++) + "]"); + if (del != del_values::update_without_default && del != del_values::update_with_default) + { + pl = render->createPolyline(legend_symbol_x[0], legend_symbol_x[1], legend_symbol_y[0], + legend_symbol_y[1]); + pl->setAttribute("_child_id", child_id++); + element->append(pl); + } + else + { + pl = element->querySelectors("polyline[_child_id=" + std::to_string(child_id++) + "]"); + if (pl != nullptr) + render->createPolyline(legend_symbol_x[0], legend_symbol_x[1], legend_symbol_y[0], + legend_symbol_y[1], 0, 0.0, 0, pl); + } if (pl != nullptr) - render->createPolyline(legend_symbol_x[0], legend_symbol_x[1], legend_symbol_y[0], - legend_symbol_y[1], 0, 0.0, 0, pl); + { + render->setLineSpec(pl, specs[spec_i]); + if (childchild->hasAttribute("line_color_ind")) + { + render->setLineColorInd(pl, + static_cast(childchild->getAttribute("line_color_ind"))); + } + else + { + render->setLineColorInd(pl, static_cast(child->getAttribute("line_color_ind"))); + } + } } - if (pl != nullptr) + else if (childchild->localName() == "polymarker") { - render->setLineSpec(pl, specs[spec_i]); - if (childchild->hasAttribute("line_color_ind")) + int markertype; + if (childchild->hasAttribute("marker_type")) { - render->setLineColorInd(pl, static_cast(childchild->getAttribute("line_color_ind"))); + markertype = static_cast(childchild->getAttribute("marker_type")); } else { - render->setLineColorInd(pl, static_cast(child->getAttribute("line_color_ind"))); + markertype = static_cast(child->getAttribute("marker_type")); + } + if (del != del_values::update_without_default && del != del_values::update_with_default) + { + pl = render->createPolymarker(legend_symbol_x[0] + 0.02 * scale_factor, + legend_symbol_y[0], markertype); + pl->setAttribute("_child_id", child_id++); + element->append(pl); + } + else + { + pl = element->querySelectors("polymarker[_child_id=" + std::to_string(child_id++) + "]"); + if (pl != nullptr) + render->createPolymarker(legend_symbol_x[0] + 0.02 * scale_factor, legend_symbol_y[0], + markertype, 0.0, 0, pl); } - } - } - else if (childchild->localName() == "polymarker") - { - int markertype; - if (childchild->hasAttribute("marker_type")) - { - markertype = static_cast(childchild->getAttribute("marker_type")); - } - else - { - markertype = static_cast(child->getAttribute("marker_type")); - } - if (del != del_values::update_without_default && del != del_values::update_with_default) - { - pl = render->createPolymarker(legend_symbol_x[0] + 0.02, legend_symbol_y[0], markertype); - pl->setAttribute("_child_id", child_id++); - element->append(pl); - } - else - { - pl = element->querySelectors("polymarker[_child_id=" + std::to_string(child_id++) + "]"); if (pl != nullptr) - render->createPolymarker(legend_symbol_x[0] + 0.02, legend_symbol_y[0], markertype, 0.0, 0, - pl); - } - if (pl != nullptr) - { - render->setMarkerColorInd(pl, (child->hasAttribute("marker_color_ind") + { + render->setMarkerColorInd(pl, + (child->hasAttribute("marker_color_ind") ? static_cast(child->getAttribute("marker_color_ind")) : 989)); - processMarkerColorInd(pl); + processMarkerColorInd(pl); + } } } } - } - if (mask & 2) - { - legend_symbol_x[0] = px - 0.06; - legend_symbol_x[1] = px - 0.02; - legend_symbol_y[0] = py; - legend_symbol_y[1] = py; - for (const auto &childchild : child->children()) + if (mask & 2) { - std::shared_ptr pl; - if (childchild->localName() == "polyline") + legend_symbol_x[0] = viewport[0] + 0.02 * scale_factor; + legend_symbol_x[1] = viewport[0] + 0.06 * scale_factor; + legend_symbol_y[0] = viewport[3] - 0.03 * scale_factor; + legend_symbol_y[1] = viewport[3] - 0.03 * scale_factor; + for (const auto &childchild : child->children()) { - if (del != del_values::update_without_default && del != del_values::update_with_default) - { - pl = render->createPolyline(legend_symbol_x[0], legend_symbol_x[1], legend_symbol_y[0], - legend_symbol_y[1]); - pl->setAttribute("_child_id", child_id++); - element->append(pl); - } - else + std::shared_ptr pl; + if (childchild->localName() == "polyline") { - pl = element->querySelectors("polyline[_child_id=" + std::to_string(child_id++) + "]"); + if (del != del_values::update_without_default && del != del_values::update_with_default) + { + pl = render->createPolyline(legend_symbol_x[0], legend_symbol_x[1], legend_symbol_y[0], + legend_symbol_y[1]); + pl->setAttribute("_child_id", child_id++); + element->append(pl); + } + else + { + pl = element->querySelectors("polyline[_child_id=" + std::to_string(child_id++) + "]"); + if (pl != nullptr) + render->createPolyline(legend_symbol_x[0], legend_symbol_x[1], legend_symbol_y[0], + legend_symbol_y[1], 0, 0.0, 0, pl); + } if (pl != nullptr) - render->createPolyline(legend_symbol_x[0], legend_symbol_x[1], legend_symbol_y[0], - legend_symbol_y[1], 0, 0.0, 0, pl); + { + render->setLineSpec(pl, specs[spec_i]); + if (childchild->hasAttribute("line_color_ind")) + { + render->setLineColorInd(pl, + static_cast(childchild->getAttribute("line_color_ind"))); + } + else + { + render->setLineColorInd(pl, static_cast(child->getAttribute("line_color_ind"))); + } + } } - if (pl != nullptr) + else if (childchild->localName() == "polymarker") { - render->setLineSpec(pl, specs[spec_i]); - if (childchild->hasAttribute("line_color_ind")) + int markertype; + if (childchild->hasAttribute("marker_type")) { - render->setLineColorInd(pl, static_cast(childchild->getAttribute("line_color_ind"))); + markertype = static_cast(childchild->getAttribute("marker_type")); } else { - render->setLineColorInd(pl, static_cast(child->getAttribute("line_color_ind"))); + markertype = static_cast(child->getAttribute("marker_type")); + } + if (del != del_values::update_without_default && del != del_values::update_with_default) + { + pl = render->createPolymarker(legend_symbol_x[0] + 0.02 * scale_factor, + legend_symbol_y[0], markertype); + pl->setAttribute("_child_id", child_id++); + element->append(pl); + } + else + { + pl = element->querySelectors("polymarker[_child_id=" + std::to_string(child_id++) + "]"); + if (pl != nullptr) + render->createPolymarker(legend_symbol_x[0] + 0.02 * scale_factor, legend_symbol_y[0], + markertype, 0.0, 0, pl); } - } - } - else if (childchild->localName() == "polymarker") - { - int markertype; - if (childchild->hasAttribute("marker_type")) - { - markertype = static_cast(childchild->getAttribute("marker_type")); - } - else - { - markertype = static_cast(child->getAttribute("marker_type")); - } - if (del != del_values::update_without_default && del != del_values::update_with_default) - { - pl = render->createPolymarker(legend_symbol_x[0] + 0.02, legend_symbol_y[0], markertype); - pl->setAttribute("_child_id", child_id++); - element->append(pl); - } - else - { - pl = element->querySelectors("polymarker[_child_id=" + std::to_string(child_id++) + "]"); if (pl != nullptr) - render->createPolymarker(legend_symbol_x[0] + 0.02, legend_symbol_y[0], markertype, 0.0, 0, - pl); - } - if (pl != nullptr) - { - render->setMarkerColorInd(pl, (child->hasAttribute("marker_color_ind") + { + render->setMarkerColorInd(pl, + (child->hasAttribute("marker_color_ind") ? static_cast(child->getAttribute("marker_color_ind")) : 989)); - processMarkerColorInd(pl); + processMarkerColorInd(pl); + } } } } - } - if (i < labels.size()) - { - std::shared_ptr tx; - if (del != del_values::update_without_default && del != del_values::update_with_default) - { - tx = render->createText(px, py, labels[i].data()); - tx->setAttribute("_child_id", child_id++); - element->append(tx); - } - else - { - tx = element->querySelectors("text[_child_id=" + std::to_string(child_id++) + "]"); - if (tx != nullptr) render->createText(px, py, labels[i].data(), CoordinateSpace::NDC, tx); - } - if (tx != nullptr && del != del_values::update_without_default) + if (i < labels.size()) { - render->setTextAlign(tx, GKS_K_TEXT_HALIGN_LEFT, GKS_K_TEXT_VALIGN_HALF); + std::shared_ptr tx; + if (del != del_values::update_without_default && del != del_values::update_with_default) + { + tx = render->createText(viewport[0] + 0.08 * scale_factor, viewport[3] - 0.03 * scale_factor, + labels[i].data()); + tx->setAttribute("_child_id", child_id++); + element->append(tx); + } + else + { + tx = element->querySelectors("text[_child_id=" + std::to_string(child_id++) + "]"); + if (tx != nullptr) + render->createText(viewport[0] + 0.08 * scale_factor, viewport[3] - 0.03 * scale_factor, + labels[i].data(), CoordinateSpace::NDC, tx); + } + if (tx != nullptr && del != del_values::update_without_default) + { + render->setTextAlign(tx, GKS_K_TEXT_HALIGN_LEFT, GKS_K_TEXT_VALIGN_HALF); + } + viewport[3] -= 0.5 * dy; + i += 1; } - py -= 0.5 * dy; - i += 1; + viewport[3] -= 0.03 * scale_factor; + spec_i += 1; } - py -= 0.03; - spec_i += 1; } gr_restorestate(); @@ -6847,39 +7215,35 @@ static void processLegend(const std::shared_ptr &element, const st unsigned int num_labels = labels.size(); std::shared_ptr fr, dr; int label_child_id = 0; + double viewport[4]; /* clear child nodes */ del = del_values(static_cast(element->getAttribute("_delete_children"))); clearOldChildren(&del, element); + auto scale_factor = static_cast(element->getAttribute("_scale_factor")); + auto h = static_cast(element->getAttribute("_start_h")); + + 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")); + gr_selntran(1); render->setSelectSpecificXform(element, 0); render->setScale(element, 0); - w = 0; - h = 0; - for (auto current_label : labels) - { - gr_inqtext(0, 0, current_label.data(), tbx, tby); - w += tbx[2] - tbx[0]; - h = grm_max(h, tby[2] - tby[0]); - } - w += num_labels * 0.03 + (num_labels - 1) * 0.02; - - px = 0.5 * (viewport[0] + viewport[1] - w); - py = viewport[2] - 0.75 * h; if (del != del_values::update_without_default && del != del_values::update_with_default) { - fr = render->createFillRect(px - 0.02, px + w + 0.02, py - 0.5 * h - 0.02, py + 0.5 * h + 0.02); + fr = render->createFillRect(viewport[0], viewport[1], viewport[2], viewport[3]); fr->setAttribute("_child_id", child_id++); element->append(fr); } else { fr = element->querySelectors("fill_rect[_child_id=" + std::to_string(child_id++) + "]"); - if (fr != nullptr) - render->createFillRect(px - 0.02, px + w + 0.02, py - 0.5 * h - 0.02, py + 0.5 * h + 0.02, 0, 0, -1, fr); + if (fr != nullptr) render->createFillRect(viewport[0], viewport[1], viewport[2], viewport[3], 0, 0, -1, fr); } render->setFillIntStyle(element, GKS_K_INTSTYLE_SOLID); @@ -6887,15 +7251,14 @@ static void processLegend(const std::shared_ptr &element, const st if (del != del_values::update_without_default && del != del_values::update_with_default) { - dr = render->createDrawRect(px - 0.02, px + w + 0.02, py - 0.5 * h - 0.02, py + 0.5 * h + 0.02); + dr = render->createDrawRect(viewport[0], viewport[1], viewport[2], viewport[3]); dr->setAttribute("_child_id", child_id++); element->append(dr); } else { dr = element->querySelectors("draw_rect[_child_id=" + std::to_string(child_id++) + "]"); - if (dr != nullptr) - render->createDrawRect(px - 0.02, px + w + 0.02, py - 0.5 * h - 0.02, py + 0.5 * h + 0.02, dr); + if (dr != nullptr) render->createDrawRect(viewport[0], viewport[1], viewport[2], viewport[3], dr); } render->setLineType(element, GKS_K_INTSTYLE_SOLID); @@ -6928,63 +7291,80 @@ static void processLegend(const std::shared_ptr &element, const st std::shared_ptr fr, dr, text; if (del != del_values::update_without_default && del != del_values::update_with_default) { - fr = render->createFillRect(px, px + 0.02, py - 0.01, py + 0.01); + fr = render->createFillRect(viewport[0] + 0.02 * scale_factor, viewport[0] + 0.04 * scale_factor, + viewport[2] + (0.5 * h + 0.01) * scale_factor, + viewport[2] + (0.5 * h + 0.03) * scale_factor); fr->setAttribute("_child_id", 0); labelGroup->append(fr); } else { fr = labelGroup->querySelectors("fill_rect[_child_id=0]"); - if (fr != nullptr) render->createFillRect(px, px + 0.02, py - 0.01, py + 0.01, 0, 0, -1, fr); + if (fr != nullptr) + render->createFillRect(viewport[0] + 0.02 * scale_factor, viewport[0] + 0.04 * scale_factor, + viewport[2] + (0.5 * h + 0.01) * scale_factor, + viewport[2] + (0.5 * h + 0.03) * scale_factor, 0, 0, -1, fr); } if (fr != nullptr) { - for (const auto &child : element->parentElement()->children()) // central_region childs + for (const auto &plot_child : element->parentElement()->children()) // central_region childs { - if (child->localName() == "series_pie") + if (plot_child->localName() != "central_region") continue; + for (const auto &child : plot_child->children()) { - std::shared_ptr pie_segment; - pie_segment = child->querySelectors( - "pie_segment[_child_id=" + std::to_string(label_child_id - 1) + "]"); - if (pie_segment != nullptr) + if (child->localName() == "series_pie") { - int color_ind = static_cast(pie_segment->getAttribute("fill_color_ind")); - auto colorrep = static_cast( - pie_segment->getAttribute("colorrep." + std::to_string(color_ind))); - fr->setAttribute("fill_color_ind", color_ind); - if (!colorrep.empty()) - fr->setAttribute("colorrep." + std::to_string(color_ind), colorrep); + std::shared_ptr pie_segment; + pie_segment = child->querySelectors( + "pie_segment[_child_id=" + std::to_string(label_child_id - 1) + "]"); + if (pie_segment != nullptr) + { + int color_ind = static_cast(pie_segment->getAttribute("fill_color_ind")); + auto colorrep = static_cast( + pie_segment->getAttribute("colorrep." + std::to_string(color_ind))); + fr->setAttribute("fill_color_ind", color_ind); + if (!colorrep.empty()) + fr->setAttribute("colorrep." + std::to_string(color_ind), colorrep); + } + break; } - break; } } } if (del != del_values::update_without_default && del != del_values::update_with_default) { - dr = render->createDrawRect(px, px + 0.02, py - 0.01, py + 0.01); + dr = render->createDrawRect(viewport[0] + 0.02 * scale_factor, viewport[0] + 0.04 * scale_factor, + viewport[2] + (0.5 * h + 0.01) * scale_factor, + viewport[2] + (0.5 * h + 0.03) * scale_factor); dr->setAttribute("_child_id", 1); labelGroup->append(dr); } else { dr = labelGroup->querySelectors("draw_rect[_child_id=1]"); - if (dr != nullptr) render->createDrawRect(px, px + 0.02, py - 0.01, py + 0.01, dr); + if (dr != nullptr) + render->createDrawRect(viewport[0] + 0.02 * scale_factor, viewport[0] + 0.04 * scale_factor, + viewport[2] + (0.5 * h + 0.01) * scale_factor, + viewport[2] + (0.5 * h + 0.03) * scale_factor, dr); } if (del != del_values::update_without_default && del != del_values::update_with_default) { - text = render->createText(px + 0.03, py, current_label); + text = render->createText(viewport[0] + 0.05 * scale_factor, + viewport[2] + (0.5 * h + 0.02) * scale_factor, current_label); text->setAttribute("_child_id", 2); labelGroup->append(text); } else { text = labelGroup->querySelectors("text[_child_id=2]"); - if (text != nullptr) render->createText(px + 0.03, py, current_label, CoordinateSpace::NDC, text); + if (text != nullptr) + render->createText(viewport[0] + 0.05 * scale_factor, viewport[2] + (0.5 * h + 0.02) * scale_factor, + current_label, CoordinateSpace::NDC, text); } gr_inqtext(0, 0, current_label.data(), tbx, tby); - px += tbx[2] - tbx[0] + 0.05; + viewport[0] += tbx[2] - tbx[0] + 0.05 * scale_factor; } } @@ -11423,24 +11803,23 @@ static void processMarginalHeatmapPlot(const std::shared_ptr &elem bins[i] = (bin_max == 0) ? 0 : bins[i] / bin_max * (c_max / 15); } + side_region = element->querySelectors("side_region[location=\"" + + (k == 0 ? std::string("right") : std::string("top")) + "\"]"); if (del != del_values::update_without_default && del != del_values::update_with_default) { - side_region = global_render->createElement("side_region"); - side_region->setAttribute("_child_id", child_id++); - element->append(side_region); - if (k == 0) side_region->setAttribute("location", "right"); - if (k == 1) side_region->setAttribute("location", "top"); - side_region->setAttribute("marginal_heatmap_side_plot", true); sub_group = global_render->createSeries("hist"); - sub_group->setAttribute("_child_id", 0); + sub_group->setAttribute("_child_id", child_id++); side_region->append(sub_group); } else { - side_region = element->querySelectors("side_region[_child_id=" + std::to_string(child_id++) + "]"); - sub_group = side_region->querySelectors("series_hist[_child_id=0]"); + sub_group = side_region->querySelectors("series_hist[_child_id=\"" + std::to_string(child_id++) + "\"]"); sub_group->setAttribute("_update_required", true); } + if (side_region != nullptr) + { + side_region->setAttribute("marginal_heatmap_side_plot", true); + } if (sub_group != nullptr) { @@ -11463,19 +11842,12 @@ static void processMarginalHeatmapPlot(const std::shared_ptr &elem } else if (marginal_heatmap_kind == "line" && x_ind != -1 && y_ind != -1) { - if (del != del_values::update_without_default && del != del_values::update_with_default) + side_region = element->querySelectors("side_region[location=\"" + + (k == 0 ? std::string("right") : std::string("top")) + "\"]"); + if (side_region != nullptr) { - side_region = global_render->createElement("side_region"); - side_region->setAttribute("_child_id", child_id++); - element->append(side_region); - if (k == 0) side_region->setAttribute("location", "right"); - if (k == 1) side_region->setAttribute("location", "top"); side_region->setAttribute("marginal_heatmap_side_plot", true); } - else - { - side_region = element->querySelectors("side_region[_child_id=" + std::to_string(child_id++) + "]"); - } // special case for marginal_heatmap_kind line - when new indices != -1 are received the 2 lines should be // displayed sub_group = side_region->querySelectors("[_child_id=0]"); @@ -11483,7 +11855,7 @@ static void processMarginalHeatmapPlot(const std::shared_ptr &elem (sub_group == nullptr && static_cast(element->getAttribute("_update_required")))) { sub_group = global_render->createSeries("stairs"); - sub_group->setAttribute("_child_id", 0); + sub_group->setAttribute("_child_id", child_id++); side_region->append(sub_group); } else @@ -11528,14 +11900,14 @@ static void processMarginalHeatmapPlot(const std::shared_ptr &elem { child->parentElement()->setAttribute("y_flip", 0); child->parentElement()->setAttribute("x_flip", 0); - if (static_cast(child->parentElement()->getAttribute("_child_id")) == 1) + if (static_cast(child->getAttribute("_child_id")) == 1) { child->parentElement()->setAttribute("x_flip", 0); if (static_cast(plot_parent->getAttribute("y_flip")) == 1) child->parentElement()->setAttribute("y_flip", 1); processFlip(child->parentElement()); } - if (static_cast(child->parentElement()->getAttribute("_child_id")) == 2) + if (static_cast(child->getAttribute("_child_id")) == 2) { if (static_cast(plot_parent->getAttribute("x_flip")) == 1) child->parentElement()->setAttribute("x_flip", 1); @@ -12364,7 +12736,7 @@ void plotProcessWsWindowWsViewport(const std::shared_ptr &element, { int pixel_width, pixel_height; double metric_width, metric_height; - double aspect_ratio_ws_pixel, aspect_ratio_ws_metric; + double aspect_ratio_ws_metric; double ws_viewport[4] = {0.0, 0.0, 0.0, 0.0}; double ws_window[4] = {0.0, 0.0, 0.0, 0.0}; @@ -12381,26 +12753,23 @@ void plotProcessWsWindowWsViewport(const std::shared_ptr &element, { figure_id_str = figure_id_str.substr(6); } - int figure_id = std::stoi(figure_id_str); + auto figure_id = std::stoi(figure_id_str); event_queue_enqueue_size_event(event_queue, figure_id, pixel_width, pixel_height); } - aspect_ratio_ws_pixel = (double)pixel_width / pixel_height; aspect_ratio_ws_metric = metric_width / metric_height; - if (aspect_ratio_ws_pixel > 1) + if (aspect_ratio_ws_metric > 1) { - ws_viewport[1] = metric_width; - ws_viewport[3] = metric_width / aspect_ratio_ws_metric; ws_window[1] = 1.0; - ws_window[3] = 1.0 / (aspect_ratio_ws_pixel); + ws_window[3] = 1.0 / (aspect_ratio_ws_metric); } else { - ws_viewport[1] = metric_height * aspect_ratio_ws_metric; - ws_viewport[3] = metric_height; - ws_window[1] = aspect_ratio_ws_pixel; + ws_window[1] = aspect_ratio_ws_metric; ws_window[3] = 1.0; } + ws_viewport[1] = metric_width; + ws_viewport[3] = metric_height; global_render->setWSViewport(active_figure, ws_viewport[0], ws_viewport[1], ws_viewport[2], ws_viewport[3]); global_render->setWSWindow(active_figure, ws_window[0], ws_window[1], ws_window[2], ws_window[3]); @@ -13094,10 +13463,29 @@ static void plotCoordinateRanges(const std::shared_ptr &element, static void processSideRegion(const std::shared_ptr &element, const std::shared_ptr &context) { + std::shared_ptr plot_parent = element; + getPlotParent(plot_parent); calculateViewport(element); + applyMoveTransformation(element); GRM::Render::processViewport(element); - GRM::Render::processWindow(element); /* needs to be set before space 3d is processed */ - GRM::Render::processScale(element->parentElement()); /* needs to be set before flip is processed */ + GRM::Render::processWindow(element); /* needs to be set before space 3d is processed */ + GRM::Render::processScale(plot_parent); /* needs to be set before flip is processed */ + + for (const auto &child : element->children()) + { + if (child->localName() == "text" && static_cast(child->getAttribute("name")) == "x_label") + { + processXlabel(child); + } + if (child->localName() == "text" && static_cast(child->getAttribute("name")) == "y_label") + { + processYlabel(child); + } + if (child->localName() == "text" && static_cast(child->getAttribute("name")) == "title") + { + processTitle(child); + } + } } static void processCoordinateSystem(const std::shared_ptr &element, @@ -13153,7 +13541,7 @@ static void processCoordinateSystem(const std::shared_ptr &element } // 3d plots are always in keep_aspect_ratio mode so the scaling with the aspect_ratio isn't needed here - double char_height = PLOT_3D_CHAR_HEIGHT * 3.0 / 4.0; + double char_height = PLOT_3D_CHAR_HEIGHT; auto diag_factor = static_cast(central_region->getAttribute("diag_factor")); element->setAttribute("char_height", char_height * diag_factor); processCharHeight(element); @@ -13648,6 +14036,48 @@ static void processPlot(const std::shared_ptr &element, const std: break; } } + + std::shared_ptr side_region; + auto kind = static_cast(element->getAttribute("kind")); + + // create side_region for labels, title, colorbar and marginal_heatmap_plot if they don't exist yet + if (central_region_parent->localName() == "marginal_heatmap_plot" || + str_equals_any(kind, "contour", "contourf", "hexbin", "heatmap", "nonuniformheatmap", "surface", "tricontour", + "trisurface", "volume", "marginal_heatmap", "quiver", "polar_heatmap", "nonuniformpolar_heatmap")) + { + if (!element->querySelectors("side_region[location=\"right\"]")) + { + side_region = global_render->createSideRegion("right"); + central_region_parent->append(side_region); + } + } + if (central_region_parent->localName() == "marginal_heatmap_plot" || + (element->hasAttribute("title_margin") && static_cast(element->getAttribute("title_margin")))) + { + if (!element->querySelectors("side_region[location=\"top\"]")) + { + side_region = global_render->createSideRegion("top"); + central_region_parent->append(side_region); + } + } + if (central_region->hasAttribute("y_label_margin") && + static_cast(central_region->getAttribute("y_label_margin"))) + { + if (!element->querySelectors("side_region[location=\"left\"]")) + { + side_region = global_render->createSideRegion("left"); + central_region_parent->append(side_region); + } + } + if (central_region->hasAttribute("x_label_margin") && + static_cast(central_region->getAttribute("x_label_margin"))) + { + if (!element->querySelectors("side_region[location=\"bottom\"]")) + { + side_region = global_render->createSideRegion("bottom"); + central_region_parent->append(side_region); + } + } } static void processSeries(const std::shared_ptr &element, const std::shared_ptr &context) @@ -13772,8 +14202,8 @@ static void processElement(const std::shared_ptr &element, const s /* Modifier */ if (str_equals_any(element->localName(), "axes_text_group", "central_region", "figure", "plot", "label", - "labels_group", "root", "side_region", "x_tick_label_group", "y_tick_label_group", - "layout_grid_element")) + "labels_group", "root", "x_tick_label_group", "y_tick_label_group", "layout_grid_element", + "side_region")) { bool old_state = automatic_update; automatic_update = false; @@ -13809,6 +14239,7 @@ static void processElement(const std::shared_ptr &element, const s { calculateViewport(element); } + if (element->localName() == "side_region") processSideRegion(element, context); GRM::Render::processAttributes(element); automatic_update = old_state; if (element->localName() != "root") applyMoveTransformation(element); @@ -14153,6 +14584,8 @@ static void applyPlotDefaults(const std::shared_ptr &plot) { if (!plot->hasAttribute("kind")) plot->setAttribute("kind", PLOT_DEFAULT_KIND); if (!plot->hasAttribute("keep_aspect_ratio")) plot->setAttribute("keep_aspect_ratio", PLOT_DEFAULT_KEEP_ASPECT_RATIO); + if (!plot->hasAttribute("only_quadratic_aspect_ratio")) + plot->setAttribute("only_quadratic_aspect_ratio", PLOT_DEFAULT_ONLY_QUADRATIC_ASPECT_RATIO); if (!plot->hasAttribute("plot_x_min")) plot->setAttribute("plot_x_min", PLOT_DEFAULT_SUBPLOT_MIN_X); if (!plot->hasAttribute("plot_x_max")) plot->setAttribute("plot_x_max", PLOT_DEFAULT_SUBPLOT_MAX_X); if (!plot->hasAttribute("plot_y_min")) plot->setAttribute("plot_y_min", PLOT_DEFAULT_SUBPLOT_MIN_Y); @@ -14529,7 +14962,7 @@ std::vector GRM::Render::getDefaultAndTooltip(const std::shared_ptr {std::string("int_limits_low"), std::vector{"None", "References the lower integral limit-values stored in the context"}}, {std::string("isovalue"), std::vector{"0.5", "The used isovalue"}}, - {std::string("keep_aspect_ratio"), std::vector{"0", "Sets if the aspect ratio is kept"}}, + {std::string("keep_aspect_ratio"), std::vector{"1", "Sets if the aspect ratio is kept"}}, {std::string("keep_window"), std::vector{"1", "Sets if the window will be inflicted by attribute changes"}}, {std::string("kind"), @@ -14570,6 +15003,8 @@ std::vector GRM::Render::getDefaultAndTooltip(const std::shared_ptr {std::string("num_row"), std::vector{"None", "Number of rows"}}, {std::string("norm"), std::vector{"None", "Specify the used normalisation"}}, {std::string("offset"), std::vector{"None", "The offset for the side region viewport"}}, + {std::string("only_quadratic_aspect_ratio"), + std::vector{"0", "Sets if the aspect ratio is forced to be quadratic and kept this way"}}, {std::string("orientation"), std::vector{"horizontal", "The orientation of the element"}}, {std::string("phi"), std::vector{"None", "References the phi-angles stored in the context"}}, {std::string("phi_dim"), std::vector{"None", "The dimension of the phi-angles"}}, @@ -15961,6 +16396,15 @@ std::shared_ptr GRM::Render::createIntegral(double int_lim_low, do return element; } +std::shared_ptr GRM::Render::createSideRegion(std::string location, + const std::shared_ptr &ext_element) +{ + std::shared_ptr element = (ext_element == nullptr) ? createElement("side_region") : ext_element; + element->setAttribute("location", location); + + return element; +} + /* ~~~~~~~~~~~~~~~~~~~~~~~~~ modifier functions~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -16828,6 +17272,7 @@ void updateFilter(const std::shared_ptr &element, const std::strin // TODO: critical update in plot means critical update inside childs, extend the following lists std::vector plot_bbox_attributes{ "keep_aspect_ratio", + "only_quadratic_aspect_ratio", "reset_ranges", }; std::vector plot_critical_attributes{ @@ -16917,7 +17362,14 @@ void updateFilter(const std::shared_ptr &element, const std::strin // kind was 'marginal_heatmap' so the marginal_heatmap_plot must be removed and a new series created for (const auto &child : element->children()) { - if (child->localName() == "side_region") child->remove(); + if (child->localName() == "side_region") + { + element->parentElement()->append(child); + for (const auto &side_region_child : child->children()) + { + side_region_child->remove(); + } + } if (child->localName() == "central_region") { for (const auto ¢ral_region_child : child->children()) @@ -16930,21 +17382,27 @@ void updateFilter(const std::shared_ptr &element, const std::strin } } element->parentElement()->append(central_region); - for (const auto &child : central_region_parent->children()) - { - if (child->localName() == "text" && - static_cast(child->getAttribute("name")) == "title") - { - element->parentElement()->append(child); - break; - } - } central_region->append(new_series); } else if (static_cast(element->getAttribute("kind")) == "marginal_heatmap") { + // move the side_regions into the marginal_heatmap_plot + for (const auto &side_region_child : central_region->parentElement()->children()) + { + if (side_region_child->localName() == "side_region") + { + new_series->append(side_region_child); + if (side_region_child->querySelectors("colorbar")) + { + side_region_child->querySelectors("colorbar")->remove(); + side_region_child->setAttribute("offset", PLOT_DEFAULT_SIDEREGION_OFFSET); + side_region_child->setAttribute("width", PLOT_DEFAULT_SIDEREGION_WIDTH); + } + } + } // create marginal_heatmap_plot as central_region father central_region->parentElement()->insertBefore(new_series, central_region); + new_series->append(central_region); } else { @@ -17144,6 +17602,18 @@ void updateFilter(const std::shared_ptr &element, const std::strin if (polar_kinds.count(new_kind) != 0) new_type = "polar"; if (kinds_3d.count(new_kind) != 0) new_type = "3d"; + // the default diag_factor must be recalculated cause the default plot size can diverge + // 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]")) + { + elem->removeAttribute("_default_diag_factor"); + } + if (new_type == "3d" && !central_region->hasAttribute("_diag_factor_set_by_user")) + central_region->removeAttribute("diag_factor"); + } + if (coordinate_system) { std::string old_type = static_cast(coordinate_system->getAttribute("plot_type")); @@ -17226,14 +17696,14 @@ void updateFilter(const std::shared_ptr &element, const std::strin { double offset; int colors; - std::shared_ptr side_region = plot->querySelectors("side_region"), + std::shared_ptr side_region = plot->querySelectors("side_region[location=\"right\"]"), colorbar = plot->querySelectors("colorbar"); std::tie(offset, colors) = getColorbarAttributes(new_kind, plot); - if (side_region == nullptr) side_region = global_render->createElement("side_region"); + if (side_region == nullptr) + side_region = global_render->createSideRegion(PLOT_DEFAULT_SIDEREGION_LOCATION); side_region->setAttribute("offset", offset + PLOT_DEFAULT_COLORBAR_OFFSET); side_region->setAttribute("width", PLOT_DEFAULT_COLORBAR_WIDTH); - side_region->setAttribute("location", PLOT_DEFAULT_SIDEREGION_LOCATION); side_region->setAttribute("_update_required", true); colorbar = global_render->createColorbar(colors, nullptr, colorbar); @@ -17242,7 +17712,7 @@ void updateFilter(const std::shared_ptr &element, const std::strin colorbar->setAttribute("_update_required", true); colorbar->setAttribute("_delete_children", static_cast(del_values::recreate_all_children)); - if (!plot->querySelectors("side_region")) plot->append(side_region); + if (!plot->querySelectors("side_region[location=\"right\"]")) plot->append(side_region); if (!plot->querySelectors("colorbar")) side_region->append(colorbar); } } diff --git a/lib/grm/src/grm/import.cxx b/lib/grm/src/grm/import.cxx index 005fbc4a4..d64085655 100644 --- a/lib/grm/src/grm/import.cxx +++ b/lib/grm/src/grm/import.cxx @@ -50,6 +50,7 @@ static std::map key_to_types{ {"marker_type", "i"}, {"num_bins", "i"}, {"normalization", "s"}, + {"only_quadratic_aspect_ratio", "i"}, {"orientation", "s"}, {"phi_flip", "i"}, {"phi_lim", "dd"}, @@ -594,7 +595,8 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) } grm_args_values(args, "kind", "s", &kind); - if ((strcmp(kind, "line") == 0 || (strcmp(kind, "scatter") == 0 && !scatter_with_z)) && (rows >= 50 && cols >= 50)) + 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"; @@ -607,6 +609,11 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) grm_args_push(args, "kind", "s", kind); } + if (!str_equals_any(kind, "contour", "contourf", "heatmap", "imshow", "marginal_heatmap", "surface", "wireframe")) + { + use_bins = 0; // this parameter is only for surface and similar types + } + if (str_equals_any(kind, "contour", "contourf", "heatmap", "imshow", "marginal_heatmap", "surface", "wireframe")) { std::vector xi(cols), yi(rows), zi(rows * cols); @@ -1374,10 +1381,6 @@ int convert_inputstream_into_args(grm_args_t *args, grm_file_args_t *file_args, fprintf(stderr, "Invalid plot type (%s) - fallback to line plot\n", kind.c_str()); kind = "line"; } - if (!str_equals_any(kind, "contour", "contourf", "heatmap", "imshow", "marginal_heatmap", "surface", "wireframe")) - { - use_bins = 0; // this parameter is only for surface and similar types - } grm_args_push(args, "kind", "s", kind.c_str()); return 1; } diff --git a/lib/grm/src/grm/interaction.cxx b/lib/grm/src/grm/interaction.cxx index c5a22c8a8..736bf02bf 100644 --- a/lib/grm/src/grm/interaction.cxx +++ b/lib/grm/src/grm/interaction.cxx @@ -376,7 +376,8 @@ static void moveTransformationHelper(const std::shared_ptr &elemen "layout_grid", "central_region", "side_region", - "marginal_heatmap_plot"}; + "marginal_heatmap_plot", + "legend"}; GRM::Render::getFigureSize(&width, &height, nullptr, nullptr); max_width_height = grm_max(width, height); @@ -1283,12 +1284,15 @@ err_t get_tooltips(int mouse_x, int mouse_y, err_t (*tooltip_callback)(int, int, gr_savestate(); auto central_region = subplot_element->querySelectors("central_region"); GRM::Render::processWindow(central_region); - if (central_region->hasAttribute("window_x_min")) + if (central_region->hasAttribute("viewport_x_min")) { gr_setviewport(static_cast(central_region->getAttribute("viewport_x_min")), static_cast(central_region->getAttribute("viewport_x_max")), static_cast(central_region->getAttribute("viewport_y_min")), static_cast(central_region->getAttribute("viewport_y_max"))); + } + if (central_region->hasAttribute("window_x_min") && kind != "pie") + { gr_setwindow(static_cast(central_region->getAttribute("window_x_min")), static_cast(central_region->getAttribute("window_x_max")), static_cast(central_region->getAttribute("window_y_min")), diff --git a/lib/grm/src/grm/plot.cxx b/lib/grm/src/grm/plot.cxx index c540f6756..170f0ea43 100644 --- a/lib/grm/src/grm/plot.cxx +++ b/lib/grm/src/grm/plot.cxx @@ -280,6 +280,7 @@ const char *valid_subplot_keys[] = {"abs_height", "location", "major_h", "normalization", + "only_quadratic_aspect_ratio", "orientation", "panzoom", "phi_flip", @@ -417,6 +418,7 @@ static string_map_entry_t key_to_formats[] = {{"a", "A"}, {"marginal_heatmap_kind", "s"}, {"marker_type", "i|D"}, {"num_bins", "i"}, + {"only_quadratic_aspect_ratio", "i"}, {"orientation", "s"}, {"panzoom", "D"}, {"raw", "s"}, @@ -3671,8 +3673,7 @@ err_t plot_draw_legend(grm_args_t *subplot_args) grm_args_t **current_series; int location; - std::shared_ptr group = - (current_central_region_element) ? current_central_region_element : getCentralRegion(); + std::shared_ptr group = (current_dom_element) ? current_dom_element : edit_figure->lastChildElement(); return_error_if(!grm_args_first_value(subplot_args, "labels", "S", &labels, &num_labels), ERROR_PLOT_MISSING_LABELS); logger((stderr, "Draw a legend with %d labels\n", num_labels)); @@ -3700,12 +3701,12 @@ err_t plot_draw_legend(grm_args_t *subplot_args) ++current_series; } - auto subGroup = global_render->createLegend(labels_key, labels_vec, specs_key, specs_vec); + auto legend = global_render->createLegend(labels_key, labels_vec, specs_key, specs_vec); if (grm_args_values(subplot_args, "location", "i", &location)) { - group->parentElement()->setAttribute("location", location); + legend->setAttribute("location", location); } - group->append(subGroup); + group->append(legend); return ERROR_NONE; } @@ -3716,8 +3717,7 @@ err_t plot_draw_pie_legend(grm_args_t *subplot_args) unsigned int num_labels; grm_args_t *series; - std::shared_ptr group = - (current_central_region_element) ? current_central_region_element : getCentralRegion(); + std::shared_ptr group = (current_dom_element) ? current_dom_element : edit_figure->lastChildElement(); return_error_if(!grm_args_first_value(subplot_args, "labels", "S", &labels, &num_labels), ERROR_PLOT_MISSING_LABELS); grm_args_values(subplot_args, "series", "a", &series); /* series exists always */ @@ -4654,7 +4654,7 @@ int plot_process_subplot_args(grm_args_t *subplot_args) { plot_func_t plot_func; char *y_label, *x_label, *title, *kind; - int keep_aspect_ratio, location, adjust_x_lim, adjust_y_lim; + int keep_aspect_ratio, location, adjust_x_lim, adjust_y_lim, only_quadratic_aspect_ratio; double *subplot; double x_lim_min, x_lim_max, y_lim_min, y_lim_max, z_lim_min, z_lim_max; double x_min, x_max, y_min, y_max, z_min, z_max; @@ -4688,6 +4688,10 @@ int plot_process_subplot_args(grm_args_t *subplot_args) { group->setAttribute("keep_aspect_ratio", keep_aspect_ratio); } + if (grm_args_values(subplot_args, "only_quadratic_aspect_ratio", "i", &only_quadratic_aspect_ratio)) + { + group->setAttribute("only_quadratic_aspect_ratio", only_quadratic_aspect_ratio); + } if (grm_args_values(subplot_args, "location", "i", &location)) { group->setAttribute("location", location); diff --git a/lib/grm/src/grm/plot_int.h b/lib/grm/src/grm/plot_int.h index 63d363d41..c9f9c91f1 100644 --- a/lib/grm/src/grm/plot_int.h +++ b/lib/grm/src/grm/plot_int.h @@ -58,13 +58,12 @@ extern const char *plot_clear_exclude_keys[]; #define PLOT_DEFAULT_XGRID 1 #define PLOT_DEFAULT_YGRID 1 #define PLOT_DEFAULT_ZGRID 1 -#define PLOT_DEFAULT_COLORBAR_MAX_CHAR_HEIGHT 0.016 * DEFAULT_ASPECT_RATIO_FOR_SCALING -#define PLOT_DEFAULT_COLORBAR_OFFSET 0.02 * DEFAULT_ASPECT_RATIO_FOR_SCALING -#define PLOT_3D_COLORBAR_OFFSET 0.05 * DEFAULT_ASPECT_RATIO_FOR_SCALING -#define PLOT_POLAR_COLORBAR_OFFSET 0.025 * DEFAULT_ASPECT_RATIO_FOR_SCALING +#define PLOT_DEFAULT_COLORBAR_MAX_CHAR_HEIGHT 0.016 +#define PLOT_DEFAULT_COLORBAR_OFFSET 0.02 +#define PLOT_3D_COLORBAR_OFFSET 0.05 +#define PLOT_POLAR_COLORBAR_OFFSET 0.025 #define PLOT_DEFAULT_COLORBAR_LOCATION "right" -#define PLOT_DEFAULT_COLORBAR_WIDTH 0.04 -#define DEFAULT_ASPECT_RATIO_FOR_SCALING 4.0 / 3.0 +#define PLOT_DEFAULT_COLORBAR_WIDTH 0.03 /* ========================= datatypes ============================================================================== */ From c22b7c67547cbbf1512e6d0e0634d3bf547705f7 Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Mon, 13 May 2024 22:12:00 +0200 Subject: [PATCH 04/24] Apply fixes for GCC 14 GCC 14 is more strict on applying standards (turning warnings into errors) and reduces internal depdendencies between system headers (`algorithm` now must always be included explicitly and is not included implicitly over other headers anymore). This can cause compile errors on code which compiled fine with GCC 13 or earlier. This commit applies fixes to the code base to be compatible with GCC 14 (on Linux). See for more details. --- 3rdparty/ffmpeg/Makefile | 2 ++ lib/grm/src/grm/interaction.cxx | 1 + lib/grm/src/grm/plot.cxx | 1 + 3 files changed, 4 insertions(+) diff --git a/3rdparty/ffmpeg/Makefile b/3rdparty/ffmpeg/Makefile index 3cfc6687f..4be3dc20e 100644 --- a/3rdparty/ffmpeg/Makefile +++ b/3rdparty/ffmpeg/Makefile @@ -6,6 +6,8 @@ VERSION = 4.2.1 FFMPEG_EXTRA_CONFIGURE_FLAGS ?= ifeq ($(shell uname),Darwin) FFMPEG_EXTRA_CONFIGURE_FLAGS += --extra-cflags=-mmacosx-version-min=10.15 +else ifeq ($(shell uname),Linux) +FFMPEG_EXTRA_CONFIGURE_FLAGS += --extra-cflags=-Wno-error=incompatible-pointer-types endif diff --git a/lib/grm/src/grm/interaction.cxx b/lib/grm/src/grm/interaction.cxx index 736bf02bf..33845655f 100644 --- a/lib/grm/src/grm/interaction.cxx +++ b/lib/grm/src/grm/interaction.cxx @@ -1,5 +1,6 @@ /* ######################### includes ############################################################################### */ +#include #include #include #include diff --git a/lib/grm/src/grm/plot.cxx b/lib/grm/src/grm/plot.cxx index 170f0ea43..94fff7894 100644 --- a/lib/grm/src/grm/plot.cxx +++ b/lib/grm/src/grm/plot.cxx @@ -5,6 +5,7 @@ #include #include +#include #include #include From 8bffb0de7a5b62f7eaafe876a21816971eead3c9 Mon Sep 17 00:00:00 2001 From: Gereon Vincenti Date: Wed, 15 May 2024 07:36:08 +0200 Subject: [PATCH 05/24] Move interp2 constants to gr.h --- lib/gr/gr.h | 32 ++++++++++++++++++++------------ lib/gr/interp2.c | 18 +++++++----------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/lib/gr/gr.h b/lib/gr/gr.h index f423bf7e5..0f08c3011 100644 --- a/lib/gr/gr.h +++ b/lib/gr/gr.h @@ -97,24 +97,32 @@ typedef enum typedef enum { - GR_STOP = 0, - GR_MOVETO = 1, - GR_LINETO = 2, - GR_CURVE3 = 3, - GR_CURVE4 = 4, + GR_STOP, + GR_MOVETO, + GR_LINETO, + GR_CURVE3, + GR_CURVE4, GR_CLOSEPOLY = 0x4f, } path_code_t; typedef enum { - GR_XFORM_BOOLEAN = 0, - GR_XFORM_LINEAR = 1, - GR_XFORM_LOG = 2, - GR_XFORM_LOGLOG = 3, - GR_XFORM_CUBIC = 4, - GR_XFORM_EQUALIZED = 5, + GR_XFORM_BOOLEAN, + GR_XFORM_LINEAR, + GR_XFORM_LOG, + GR_XFORM_LOGLOG, + GR_XFORM_CUBIC, + GR_XFORM_EQUALIZED, } xform_types_t; +typedef enum +{ + GR_INTERP2_NEAREST, + GR_INTERP2_LINEAR, + GR_INTERP2_SPLINE, + GR_INTERP2_CUBIC, +} interp2_method_t; + typedef struct { double x, y; @@ -338,7 +346,7 @@ DLLEXPORT void gr_trisurface(int, double *, double *, double *); DLLEXPORT void gr_gradient(int, int, double *, double *, double *, double *, double *); DLLEXPORT void gr_quiver(int, int, double *, double *, double *, double *, int); DLLEXPORT void gr_interp2(int nx, int ny, const double *x, const double *y, const double *z, int nxq, int nyq, - const double *xq, const double *yq, double *zq, int method, double extrapval); + const double *xq, const double *yq, double *zq, interp2_method_t method, double extrapval); DLLEXPORT const char *gr_version(void); DLLEXPORT void gr_shade(int, double *, double *, int, int, double *, int, int, int *); DLLEXPORT void gr_shadepoints(int, double *, double *, int, int, int); diff --git a/lib/gr/interp2.c b/lib/gr/interp2.c index 3d6b30cc7..4a35179dd 100644 --- a/lib/gr/interp2.c +++ b/lib/gr/interp2.c @@ -5,10 +5,6 @@ #include "gr.h" -#define INTERP2_NEAREST 0 -#define INTERP2_LINEAR 1 -#define INTERP2_CUBIC 3 -#define INTERP2_SPLINE 2 static char *xmalloc(int size) { @@ -241,12 +237,12 @@ static void create_splines(const double *x, const double *y, int n, double **spl * \param[in] extrapval The extrapolation value */ void gr_interp2(int nx, int ny, const double *x, const double *y, const double *z, int nxq, int nyq, const double *xq, - const double *yq, double *zq, int method, double extrapval) + const double *yq, double *zq, interp2_method_t method, double extrapval) { int ixq, iyq, ix, iy, ind, i; double ***x_splines = NULL, **spline = NULL, *a = NULL, diff; - if (method == INTERP2_SPLINE) + if (method == GR_INTERP2_SPLINE) { x_splines = (double ***)xmalloc(ny * sizeof(double **)); spline = (double **)xmalloc(ny * sizeof(double *)); @@ -303,7 +299,7 @@ void gr_interp2(int nx, int ny, const double *x, const double *y, const double * } } - if (method == INTERP2_NEAREST) + if (method == GR_INTERP2_NEAREST) { if (ix + 1 < nx && xq[ixq] - x[ix] > x[ix + 1] - xq[ixq]) { @@ -315,7 +311,7 @@ void gr_interp2(int nx, int ny, const double *x, const double *y, const double * } zq[iyq * nxq + ixq] = z[iy * nx + ix]; } - else if (method == INTERP2_SPLINE) + else if (method == GR_INTERP2_SPLINE) { /* interpolation in X direction: */ diff = xq[ixq] - x[ix]; @@ -337,18 +333,18 @@ void gr_interp2(int nx, int ny, const double *x, const double *y, const double * zq[iyq * nxq + ixq] = zq[iyq * nxq + ixq] * diff + spline[iy][1]; zq[iyq * nxq + ixq] = zq[iyq * nxq + ixq] * diff + spline[iy][0]; } - else if (method == INTERP2_CUBIC) + else if (method == GR_INTERP2_CUBIC) { zq[iyq * nxq + ixq] = bicubic_interp(x, y, z, ix, iy, nx, ny, xq[ixq], yq[iyq]); } - else if (method == INTERP2_LINEAR) + else if (method == GR_INTERP2_LINEAR) { zq[iyq * nxq + ixq] = bilinear_interp(x, y, z, ix, iy, nx, xq[ixq], yq[iyq]); } } } } - if (method == INTERP2_SPLINE) + if (method == GR_INTERP2_SPLINE) { /* free allocated memory */ for (ind = 0; ind < ny; ind++) From 3685d977b07e994cef19c9201e456a529327a208 Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Mon, 3 Jun 2024 17:22:02 +0200 Subject: [PATCH 06/24] GR: added new axis functions --- lib/gr/gr.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/gr/gr.h | 29 ++++++++ 2 files changed, 232 insertions(+) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index fd9803942..7343fd43d 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -5307,6 +5307,209 @@ void gr_axes(double x_tick, double y_tick, double x_org, double y_org, int major gr_axeslbl(x_tick, y_tick, x_org, y_org, major_x, major_y, tick_size, NULL, NULL); } +void gr_axis(char which, axis_t *axis) +{ + int errind, tnr; + double wn[4], vp[4]; + double x_min, x_max, y_min, y_max; + int scale_option, base, decade, exponent; + double tick; + double epsilon; + int i, j, k; + double a, a0, tbx[4], tby[4]; + char *s; + format_reference_t formatReference; + + check_autoinit; + + /* inquire current normalization transformation */ + + gks_inq_current_xformno(&errind, &tnr); + gks_inq_xform(tnr, &errind, wn, vp); + + x_min = wn[0]; + x_max = wn[1]; + y_min = wn[2]; + y_max = wn[3]; + + if (which == 'X') + { + scale_option = GR_OPTION_X_LOG; + base = lx.basex; + if (scale_option & GR_OPTION_X_LOG == 0) gr_adjustrange(&x_min, &x_max); + if (is_nan(axis->axis_min)) axis->axis_min = x_min; + if (is_nan(axis->axis_max)) axis->axis_max = x_max; + if (is_nan(axis->axis_org)) axis->axis_org = y_min; + } + else if (which == 'Y') + { + scale_option = GR_OPTION_Y_LOG; + base = lx.basey; + if (scale_option & GR_OPTION_Y_LOG == 0) gr_adjustrange(&y_min, &y_max); + if (is_nan(axis->axis_min)) axis->axis_min = y_min; + if (is_nan(axis->axis_max)) axis->axis_max = y_max; + if (is_nan(axis->axis_org)) axis->axis_org = x_min; + } + + if (scale_option & lx.scale_options) + { + axis->num_tick_labels = igauss(blog(base, axis->axis_max / axis->axis_min)) + 2; + axis->tick_label = (tick_label_t *)xcalloc(axis->num_tick_labels, sizeof(tick_label_t)); + axis->num_ticks = axis->num_tick_labels * base; + axis->ticks = (tick_t *)xcalloc(axis->num_ticks, sizeof(tick_t)); + + a0 = pow(base, gauss(blog(base, axis->axis_min))); + i = ipred(axis->axis_min / a0); + a = a0 + i * a0; + decade = igauss(blog(base, axis->axis_min / axis->axis_org)); + j = k = 0; + while (a <= axis->axis_max) + { + axis->ticks[j].value = a; + axis->ticks[j++].is_major = (i == 0); + if (i == 0) + { + if (axis->major_count > 0) + { + if (decade % axis->major_count == 0) + { + exponent = iround(blog(base, a)); + s = (char *)xcalloc(256, sizeof(char)); + snprintf(s, 255, "%d^{%d}", base, exponent); + axis->tick_label[k].tick = a; + axis->tick_label[k].label = replace_minus_sign(s); + gr_inqtext(0, 0, axis->tick_label[k].label, tbx, tby); + axis->tick_label[k].width = tbx[2] - tbx[0]; + k++; + } + } + } + if (i == 8 || base < 10) + { + a0 = a0 * base; + i = 0; + decade++; + } + else + { + i++; + } + a = a0 + i * a0; + } + axis->num_ticks = j; + axis->num_tick_labels = k; + } + else + { + tick = gr_tick(axis->axis_min, axis->axis_max); + if (axis->major_count == 0) axis->major_count = 1; + axis->num_ticks = (int)((axis->axis_max - axis->axis_min) / tick + 0.5) + 1; + axis->ticks = (double *)xcalloc(axis->num_ticks, sizeof(tick_t)); + axis->num_tick_labels = axis->num_ticks / axis->major_count; + axis->tick_label = (tick_label_t *)xcalloc(axis->num_tick_labels, sizeof(tick_label_t)); + + epsilon = FEPS * (axis->axis_max - axis->axis_min); + + i = isucc(axis->axis_min / tick); + a = i * tick; + j = k = 0; + while (a <= axis->axis_max + epsilon) + { + axis->ticks[j].value = a; + axis->ticks[j++].is_major = (i % axis->major_count == 0); + gr_getformat(&formatReference, axis->axis_min, a, axis->axis_max, tick, axis->major_count); + if (i % axis->major_count == 0) + { + s = (char *)xcalloc(256, sizeof(char)); + gr_ftoa(s, a, &formatReference); + axis->tick_label[k].tick = a; + axis->tick_label[k].label = replace_minus_sign(s); + gr_inqtext(0, 0, axis->tick_label[k].label, tbx, tby); + axis->tick_label[k].width = tbx[2] - tbx[0]; + k++; + } + i++; + a = i * tick; + } + } +} + +void gr_draw_axis(char which, axis_t *axis) +{ + int errind, tnr; + double wn[4], vp[4]; + double tick, minor_tick, major_tick, label_org; + int i; + + check_autoinit; + + /* inquire current normalization transformation */ + + gks_inq_current_xformno(&errind, &tnr); + gks_inq_xform(tnr, &errind, wn, vp); + + gks_set_clipping(GKS_K_NOCLIP); + + if (which == 'X') + { + tick = axis->tick_size * (wn[3] - wn[2]) / (vp[3] - vp[2]); + minor_tick = y_log(y_lin(axis->axis_org) + tick); + major_tick = y_log(y_lin(axis->axis_org) + 2 * tick); + label_org = y_log(y_lin(axis->axis_org) - tick); + pline(axis->axis_min, axis->axis_org); + pline(axis->axis_max, axis->axis_org); + end_pline(); + gks_set_text_align(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_TOP); + } + else + { + tick = axis->tick_size * (wn[1] - wn[0]) / (vp[1] - vp[0]); + minor_tick = x_log(x_lin(axis->axis_org) + tick); + major_tick = x_log(x_lin(axis->axis_org) + 2 * tick); + label_org = x_log(x_lin(axis->axis_org) - tick); + pline(axis->axis_org, axis->axis_min); + pline(axis->axis_org, axis->axis_max); + end_pline(); + gks_set_text_align(GKS_K_TEXT_HALIGN_RIGHT, GKS_K_TEXT_VALIGN_HALF); + } + + for (i = 0; i < axis->num_ticks; i++) + { + tick = axis->ticks[i].is_major ? major_tick : minor_tick; + if (which == 'X') + { + pline(axis->ticks[i].value, axis->axis_org); + pline(axis->ticks[i].value, tick); + end_pline(); + } + else + { + pline(axis->axis_org, axis->ticks[i].value); + pline(tick, axis->ticks[i].value); + end_pline(); + } + } + + for (i = 0; i < axis->num_tick_labels; i++) + { + if (which == 'X') + text2d(axis->tick_label[i].tick, label_org, axis->tick_label[i].label); + else + text2d(label_org, axis->tick_label[i].tick, axis->tick_label[i].label); + } +} + +void gr_free_axis(axis_t *axis) +{ + int i; + for (i = 0; i < axis->num_tick_labels; i++) + { + free(axis->tick_label[i].label); + } + free(axis->tick_label); + free(axis->ticks); +} + static void grid_line(double x0, double y0, double x1, double y1, int color, int major) { if (color != 0) diff --git a/lib/gr/gr.h b/lib/gr/gr.h index 0f08c3011..d8f20e491 100644 --- a/lib/gr/gr.h +++ b/lib/gr/gr.h @@ -180,6 +180,32 @@ typedef struct int decimal_digits; } format_reference_t; +typedef struct +{ + double value; + int is_major; +} tick_t; + +typedef struct +{ + double tick; + char *label; + double width; +} tick_label_t; + +typedef struct +{ + int dry_run; + double axis_min, axis_max, axis_org; + int major_count; + int num_ticks; + tick_t *ticks; + int num_tick_labels; + tick_label_t *tick_label; + double tick_size; + int draw_grid_line; +} axis_t; + DLLEXPORT void gr_initgr(void); DLLEXPORT int gr_debug(void); DLLEXPORT void gr_opengks(void); @@ -265,6 +291,9 @@ DLLEXPORT void gr_axes(double, double, double, double, int, int, double); DLLEXPORT void gr_axeslbl(double, double, double, double, int, int, double, void (*)(double, double, const char *, double), void (*)(double, double, const char *, double)); +DLLEXPORT void gr_axis(char, axis_t *); +DLLEXPORT void gr_draw_axis(char, axis_t *); +DLLEXPORT void gr_free_axis(axis_t *); DLLEXPORT void gr_grid(double, double, double, double, int, int); DLLEXPORT void gr_grid3d(double, double, double, double, double, double, int, int, int); DLLEXPORT void gr_verrorbars(int, double *, double *, double *, double *); From bc208bd650bb4cc6b0e986449330589622f40403 Mon Sep 17 00:00:00 2001 From: Verbov Date: Mon, 13 May 2024 10:31:03 +0200 Subject: [PATCH 07/24] fixed non keep_aspect_ratio behaviour, fixed a minor issues with label and title, moved most of the stuff from processXlabel processYlabel and processTitle into processSideRegion, reset shift when location gets changed, added text_region and side_plot_region, added missing font_precision in text, updated attribute edit for label and title, updated menubar that it fits the common style -> new menu file with submenus export load and save --- lib/grm/grplot/grplot_widget.cxx | 50 +- lib/grm/grplot/grplot_widget.hxx | 4 +- lib/grm/include/grm/dom_render/render.hxx | 5 + .../grm/dom_render/graphics_tree/schema.xsd | 48 +- lib/grm/src/grm/dom_render/render.cxx | 947 ++++++++++-------- lib/grm/src/grm/interaction.cxx | 56 +- lib/grm/src/grm/plot.cxx | 81 +- 7 files changed, 700 insertions(+), 491 deletions(-) diff --git a/lib/grm/grplot/grplot_widget.cxx b/lib/grm/grplot/grplot_widget.cxx index 1ef504439..253c2358d 100644 --- a/lib/grm/grplot/grplot_widget.cxx +++ b/lib/grm/grplot/grplot_widget.cxx @@ -153,14 +153,12 @@ GRPlotWidget::GRPlotWidget(QMainWindow *parent, int argc, char **argv) "set_text_color_for_background", "space", "stairs", - "title_margin", + "text_is_title", "x_flip", "x_grid", - "x_label_margin", "x_log", "y_flip", "y_grid", - "y_label_margin", "y_line", "y_log", "z_flip", @@ -184,7 +182,9 @@ GRPlotWidget::GRPlotWidget(QMainWindow *parent, int argc, char **argv) mouseState.anchor = {0, 0}; menu = parent->menuBar(); - export_menu = new QMenu("&Export"); + file_menu = new QMenu("&File"); + export_menu = file_menu->addMenu("&Export"); + PdfAct = new QAction(tr("&PDF"), this); connect(PdfAct, &QAction::triggered, this, &GRPlotWidget::pdf); export_menu->addAction(PdfAct); @@ -372,12 +372,12 @@ GRPlotWidget::GRPlotWidget(QMainWindow *parent, int argc, char **argv) if (strcmp(argv[1], "--test") != 0 && !test_commands_stream) { + menu->addMenu(file_menu); menu->addMenu(type); menu->addMenu(algo); menu->addMenu(modi_menu); } } - if (strcmp(argv[1], "--test") != 0 && !test_commands_stream) menu->addMenu(export_menu); if (getenv("GRDISPLAY") && strcmp(getenv("GRDISPLAY"), "edit") == 0) { @@ -395,16 +395,15 @@ GRPlotWidget::GRPlotWidget(QMainWindow *parent, int argc, char **argv) editor_menu->addAction(editor_action); QObject::connect(editor_action, SIGNAL(triggered()), this, SLOT(enable_editor_functions())); - file_menu = editor_menu->addMenu(tr("&XML-File")); - save_file_action = new QAction("&Save Plot"); + save_file_action = new QAction("&Save"); save_file_action->setShortcut(Qt::CTRL | Qt::Key_S); file_menu->addAction(save_file_action); QObject::connect(save_file_action, SIGNAL(triggered()), this, SLOT(save_file_slot())); - open_file_action = new QAction("&Open Plot"); - open_file_action->setShortcut(Qt::CTRL | Qt::Key_O); - file_menu->addAction(open_file_action); - QObject::connect(open_file_action, SIGNAL(triggered()), this, SLOT(open_file_slot())); + load_file_action = new QAction("&Load"); + load_file_action->setShortcut(Qt::CTRL | Qt::Key_O); + file_menu->addAction(load_file_action); + QObject::connect(load_file_action, SIGNAL(triggered()), this, SLOT(load_file_slot())); configuration_menu = editor_menu->addMenu(tr("&Show")); configuration_menu->menuAction()->setVisible(false); @@ -690,7 +689,8 @@ void GRPlotWidget::attributeComboBoxHandler(const std::string &cur_attr_name, st completer->setCaseSensitivity(Qt::CaseInsensitive); ((QComboBox *)*lineEdit)->setCompleter(completer); } - else if (cur_attr_name == "location" && cur_elem_name == "side_region") + else if (cur_attr_name == "location" && + (cur_elem_name == "side_region" || cur_elem_name == "side_plot_region" || cur_elem_name == "text_region")) { for (const auto &elem : side_region_location_list) { @@ -700,7 +700,8 @@ void GRPlotWidget::attributeComboBoxHandler(const std::string &cur_attr_name, st completer->setCaseSensitivity(Qt::CaseInsensitive); ((QComboBox *)*lineEdit)->setCompleter(completer); } - else if (cur_attr_name == "location" && cur_elem_name != "side_region") + else if (cur_attr_name == "location" && + !(cur_elem_name == "side_region" || cur_elem_name == "side_plot_region" || cur_elem_name == "text_region")) { for (const auto &elem : location_list) { @@ -746,7 +747,10 @@ void GRPlotWidget::advancedAttributeComboBoxHandler(const std::string &cur_attr_ current_text = projectionTypeIntToString(static_cast(current_selection->get_ref()->getAttribute(cur_attr_name))); } - else if (cur_attr_name == "location" && current_selection->get_ref()->localName() != "side_region" && + else if (cur_attr_name == "location" && + !(current_selection->get_ref()->localName() == "side_region" || + current_selection->get_ref()->localName() == "side_plot_region" || + current_selection->get_ref()->localName() == "text_region") && current_selection->get_ref()->getAttribute(cur_attr_name).isInt()) { current_text = locationIntToString(static_cast(current_selection->get_ref()->getAttribute(cur_attr_name))); @@ -1123,13 +1127,25 @@ void GRPlotWidget::AttributeEditEvent() } else { - if (labels[i].toStdString() == "text" && (name == "title" || name == "xlabel" || name == "ylabel")) + if (labels[i].toStdString() == "text") { const std::string value = ((QLineEdit *)fields[i])->text().toStdString(); if (attr_type[attr_name] == "xs:string" || (attr_type[attr_name] == "strint" && !util::is_digits(value))) { - current_selection->get_ref()->parentElement()->setAttribute(name, value); + if (current_selection->get_ref()->parentElement()->localName() == "text_region") + { + current_selection->get_ref()->parentElement()->parentElement()->setAttribute( + "text_content", value); + } + else if (name == "xlabel" || name == "ylabel") + { + current_selection->get_ref() + ->parentElement() + ->parentElement() + ->querySelectors(name) + ->setAttribute(name, value); + } } else if (attr_type[attr_name] == "xs:double") { @@ -2502,7 +2518,7 @@ void GRPlotWidget::show_bounding_boxes_slot() } } -void GRPlotWidget::open_file_slot() +void GRPlotWidget::load_file_slot() { if (enable_editor) { diff --git a/lib/grm/grplot/grplot_widget.hxx b/lib/grm/grplot/grplot_widget.hxx index 4facd17fc..3b6256a4a 100644 --- a/lib/grm/grplot/grplot_widget.hxx +++ b/lib/grm/grplot/grplot_widget.hxx @@ -104,7 +104,7 @@ private slots: void show_container_slot(); void show_bounding_boxes_slot(); void save_file_slot(); - void open_file_slot(); + void load_file_slot(); void enable_editor_functions(); void add_element_slot(); void received(grm_args_t_wrapper args); @@ -225,7 +225,7 @@ private: QAction *histAct, *barplotAct, *stairsAct, *stemAct; QAction *shadeAct, *hexbinAct; QAction *PdfAct, *PngAct, *JpegAct, *SvgAct; - QAction *show_container_action, *show_bounding_boxes_action, *save_file_action, *open_file_action, *editor_action, + QAction *show_container_action, *show_bounding_boxes_action, *save_file_action, *load_file_action, *editor_action, *add_element_action; QAction *moveableModeAct; QCursor *csr; diff --git a/lib/grm/include/grm/dom_render/render.hxx b/lib/grm/include/grm/dom_render/render.hxx index e8f41e1f2..ba535e322 100644 --- a/lib/grm/include/grm/dom_render/render.hxx +++ b/lib/grm/include/grm/dom_render/render.hxx @@ -383,6 +383,11 @@ public: std::shared_ptr createSideRegion(std::string location, const std::shared_ptr &ext_element = nullptr); + + std::shared_ptr createTextRegion(const std::shared_ptr &ext_element = nullptr); + + std::shared_ptr createSidePlotRegion(const std::shared_ptr &ext_element = nullptr); + //! Modifierfunctions /* ------------------------------- setter functions ----------------------------------------------------------------*/ 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 14ff75bd1..5b5f95b7b 100644 --- a/lib/grm/src/grm/dom_render/graphics_tree/schema.xsd +++ b/lib/grm/src/grm/dom_render/graphics_tree/schema.xsd @@ -122,7 +122,6 @@ - @@ -158,8 +157,6 @@ - - @@ -234,23 +231,42 @@ - - + + + + + + + + + + + + + + + + + + + + + + + - - - + @@ -262,6 +278,19 @@ + + + + + + + + + + + + + @@ -386,6 +415,7 @@ + @@ -1352,12 +1382,10 @@ - - diff --git a/lib/grm/src/grm/dom_render/render.cxx b/lib/grm/src/grm/dom_render/render.cxx index 4d4f25639..9ecf91922 100644 --- a/lib/grm/src/grm/dom_render/render.cxx +++ b/lib/grm/src/grm/dom_render/render.cxx @@ -117,6 +117,8 @@ static std::set parent_types = { "series_volume", "series_wireframe", "side_region", + "side_plot_region", + "text_region", "x_tick_label_group", "y_tick_label_group", }; @@ -471,7 +473,8 @@ static double getMaxViewport(const std::shared_ptr &element, bool if (x) { max_vp = (aspect_ratio_ws < 1) ? static_cast(plot_element->getAttribute("_viewport_x_max_org")) : 1; - if (!str_equals_any(element->localName(), "legend", "side_region") && element->hasAttribute("_bbox_x_max")) + if (!str_equals_any(element->localName(), "legend", "side_region", "text_region", "side_plot_region") && + element->hasAttribute("_bbox_x_max")) { max_vp -= abs(static_cast(element->getAttribute("_viewport_x_max_org")) - static_cast(element->getAttribute("_bbox_x_max")) / max_width_height); @@ -480,7 +483,8 @@ static double getMaxViewport(const std::shared_ptr &element, bool else { max_vp = (aspect_ratio_ws > 1) ? static_cast(plot_element->getAttribute("_viewport_y_max_org")) : 1; - if (!str_equals_any(element->localName(), "legend", "marginal_heatmap_plot", "plot", "side_region") && + if (!str_equals_any(element->localName(), "legend", "marginal_heatmap_plot", "plot", "side_region", + "side_plot_region", "text_region") && element->hasAttribute("_bbox_y_max")) // TODO: Exclude plot leads to problem with different aspect ration - // need to fix bboxes to fix this issue here { @@ -501,7 +505,8 @@ static double getMinViewport(const std::shared_ptr &element, bool if (x) { - if (!str_equals_any(element->localName(), "legend", "side_region") && element->hasAttribute("_bbox_x_min")) + if (!str_equals_any(element->localName(), "legend", "side_region", "text_region", "side_plot_region") && + element->hasAttribute("_bbox_x_min")) { min_vp += abs(static_cast(element->getAttribute("_viewport_x_min_org")) - static_cast(element->getAttribute("_bbox_x_min")) / max_width_height); @@ -509,7 +514,8 @@ static double getMinViewport(const std::shared_ptr &element, bool } else { - if (!str_equals_any(element->localName(), "legend", "side_region") && element->hasAttribute("_bbox_y_min")) + if (!str_equals_any(element->localName(), "legend", "side_region", "text_region", "side_plot_region") && + element->hasAttribute("_bbox_y_min")) { min_vp += abs(static_cast(element->getAttribute("_viewport_y_min_org")) - static_cast(element->getAttribute("_bbox_y_min")) / max_width_height); @@ -590,6 +596,13 @@ static void clearOldChildren(del_values *del, const std::shared_ptrchildren()) { if (real_child->hasAttribute("_child_id")) real_child->remove(); + if (real_child->localName() == "side_plot_region") + { + for (const auto &side_plot_child : real_child->children()) + { + if (side_plot_child->hasAttribute("_child_id")) side_plot_child->remove(); + } + } } } } @@ -607,10 +620,10 @@ static void clearOldChildren(del_values *del, const std::shared_ptr coordinate_system_children = {"x_tick_label_group", "y_tick_label_group", "text", - "titles_3d"}; + std::vector coordinate_system_children = {"x_tick_label_group", "y_tick_label_group", "titles_3d"}; for (const auto &child : element->children()) { if (element->localName() == "coordinate_system" && @@ -620,23 +633,33 @@ static void clearOldChildren(del_values *del, const std::shared_ptrlocalName() != "central_region" && element->localName() == "marginal_heatmap_plot") + if (element->localName() == "marginal_heatmap_plot") { - if (child->localName() == "side_region") + if (child->localName() == "side_region" && child->hasAttribute("marginal_heatmap_side_plot")) { for (const auto &side_region_child : child->children()) { - if (side_region_child->localName() != "text") + if (side_region_child->localName() != "text_region") { - only_central_region_child = false; + only_non_marginal_heatmap_children = false; break; } } } - else + else if (child->localName() == "central_region") { - only_central_region_child = false; - break; + for (const auto ¢ral_region_child : child->children()) + { + if (central_region_child->localName() == "series_heatmap") + only_non_marginal_heatmap_children = false; + } + } + } + if (element->localName() == "side_region") + { + for (const auto &side_region_child : element->children()) + { + if (side_region_child->localName() == "text_region") only_side_plot_region_child = false; } } if (child->localName() != "error_bars" && child->localName() != "integral_group" && @@ -649,7 +672,9 @@ static void clearOldChildren(del_values *del, const std::shared_ptrlocalName() == "coordinate_system" && only_children_created_from_attributes) *del = del_values::recreate_own_children; if (starts_with(element->localName(), "series_") && only_error_child) *del = del_values::recreate_own_children; - if (element->localName() == "marginal_heatmap_plot" && only_central_region_child) + if (element->localName() == "marginal_heatmap_plot" && only_non_marginal_heatmap_children) + *del = del_values::recreate_own_children; + if (element->localName() == "side_region" && only_side_plot_region_child) *del = del_values::recreate_own_children; } } @@ -701,27 +726,35 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptr plot_parent = element; + std::shared_ptr plot_parent = element, left_side_region, right_side_region, bottom_side_region, + top_side_region; + auto render = grm_get_render(); getPlotParent(plot_parent); kind = static_cast(plot_parent->getAttribute("kind")); - title_margin = static_cast(plot_parent->getAttribute("title_margin")); keep_aspect_ratio = static_cast(plot_parent->getAttribute("keep_aspect_ratio")); only_quadratic_aspect_ratio = static_cast(plot_parent->getAttribute("only_quadratic_aspect_ratio")); - if (element != nullptr && element->hasAttribute("x_label_margin")) - x_label_margin = static_cast(element->getAttribute("x_label_margin")); - if (element != nullptr && element->hasAttribute("y_label_margin")) - y_label_margin = static_cast(element->getAttribute("y_label_margin")); + left_side_region = plot_parent->querySelectors("side_region[location=\"left\"]"); + right_side_region = plot_parent->querySelectors("side_region[location=\"right\"]"); + bottom_side_region = plot_parent->querySelectors("side_region[location=\"bottom\"]"); + top_side_region = plot_parent->querySelectors("side_region[location=\"top\"]"); + if (left_side_region && left_side_region->hasAttribute("text_content")) left_text_margin = true; + if (right_side_region && right_side_region->hasAttribute("text_content")) right_text_margin = true; + if (bottom_side_region && bottom_side_region->hasAttribute("text_content")) bottom_text_margin = true; + if (top_side_region && top_side_region->hasAttribute("text_content")) top_text_margin = true; + if (top_side_region && top_side_region->hasAttribute("text_is_title")) + top_text_is_title = top_text_margin && static_cast(top_side_region->getAttribute("text_is_title")); for (const auto &series : element->children()) { @@ -791,18 +824,15 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptr start_aspect_ratio_ws) @@ -814,13 +844,10 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptr &element, double offset, double width, + bool uniform_data) +{ + double viewport[4]; + std::string location = PLOT_DEFAULT_SIDEREGION_LOCATION; + double max_vp, min_vp; + double offset_rel, width_rel; + double metric_width, metric_height, start_aspect_ratio_ws; + bool keep_aspect_ratio = false, only_quadratic_aspect_ratio = false; + std::shared_ptr plot_parent = element, central_region, side_region = element; + getPlotParent(plot_parent); + + central_region = plot_parent->querySelectors("central_region"); + if (element->localName() != "side_region") side_region = element->parentElement(); + + viewport[0] = static_cast(central_region->getAttribute("_viewport_x_min_org")); + viewport[1] = static_cast(central_region->getAttribute("_viewport_x_max_org")); + viewport[2] = static_cast(central_region->getAttribute("_viewport_y_min_org")); + viewport[3] = static_cast(central_region->getAttribute("_viewport_y_max_org")); + keep_aspect_ratio = static_cast(plot_parent->getAttribute("keep_aspect_ratio")); + 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")); + + 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); + + // 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; + if (aspect_ratio_ws <= 1) + { + offset_rel = offset * aspect_ratio_ws; + width_rel = width * aspect_ratio_ws; + } + else + { + offset_rel = offset / aspect_ratio_ws; + width_rel = width / aspect_ratio_ws; + } + } + else + { + auto default_diag_factor = static_cast(element->getAttribute("_default_diag_factor")); + offset_rel = offset * diag_factor * default_diag_factor; + width_rel = width * diag_factor * default_diag_factor; + } + + if (location == "right") + { + max_vp = getMaxViewport(element, true); + global_render->setViewport(element, viewport[1] + offset_rel, + grm_min(viewport[1] + offset_rel + width_rel, max_vp), viewport[2], viewport[3]); + element->setAttribute("_viewport_x_min_org", viewport[1] + offset_rel); + element->setAttribute("_viewport_x_max_org", grm_min(viewport[1] + offset_rel + width_rel, max_vp)); + element->setAttribute("_viewport_y_min_org", viewport[2]); + element->setAttribute("_viewport_y_max_org", viewport[3]); + } + else if (location == "left") + { + min_vp = getMinViewport(element, true); + global_render->setViewport(element, grm_max(viewport[0] - (offset_rel + width_rel), min_vp), viewport[0], + viewport[2], viewport[3]); + element->setAttribute("_viewport_x_min_org", grm_max(viewport[0] - (offset_rel + width_rel), min_vp)); + element->setAttribute("_viewport_x_max_org", viewport[0]); + element->setAttribute("_viewport_y_min_org", viewport[2]); + element->setAttribute("_viewport_y_max_org", viewport[3]); + } + else if (location == "top") + { + max_vp = getMaxViewport(element, false); + global_render->setViewport(element, viewport[0], viewport[1], viewport[3] + offset_rel, + grm_min(viewport[3] + offset_rel + width_rel, max_vp)); + element->setAttribute("_viewport_x_min_org", viewport[0]); + element->setAttribute("_viewport_x_max_org", viewport[1]); + element->setAttribute("_viewport_y_min_org", viewport[3] + offset_rel); + element->setAttribute("_viewport_y_max_org", grm_min(viewport[3] + offset_rel + width_rel, max_vp)); + } + else if (location == "bottom") + { + min_vp = getMinViewport(element, false); + global_render->setViewport(element, viewport[0], viewport[1], + grm_max(viewport[2] - (offset_rel + width_rel), min_vp), viewport[2]); + element->setAttribute("_viewport_x_min_org", viewport[0]); + element->setAttribute("_viewport_x_max_org", viewport[1]); + element->setAttribute("_viewport_y_min_org", grm_max(viewport[2] - (offset_rel + width_rel), min_vp)); + element->setAttribute("_viewport_y_max_org", viewport[2]); + } +} + static void calculateViewport(const std::shared_ptr &element) { auto render = grm_get_render(); @@ -1016,25 +1149,15 @@ static void calculateViewport(const std::shared_ptr &element) } else if (element->localName() == "side_region") { - double viewport[4], plot_viewport[4]; + double plot_viewport[4]; double offset = PLOT_DEFAULT_SIDEREGION_OFFSET, width = PLOT_DEFAULT_SIDEREGION_WIDTH; - std::shared_ptr central_region; 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; + double metric_width, metric_height; bool keep_aspect_ratio = false, uniform_data = true, only_quadratic_aspect_ratio = false; - for (const auto &child : element->parentElement()->children()) - { - if (child->localName() == "central_region") - { - central_region = child; - break; - } - } - - auto plot_parent = central_region; + auto plot_parent = element; getPlotParent(plot_parent); if (element->hasAttribute("location")) location = static_cast(element->getAttribute("location")); @@ -1042,10 +1165,6 @@ static void calculateViewport(const std::shared_ptr &element) // is set cause processElement is run for every Element even when only attributes gets processed; so the // calculateViewport call from the plot element which causes the calculation of the central_region viewport is // also processed - viewport[0] = static_cast(central_region->getAttribute("_viewport_x_min_org")); - viewport[1] = static_cast(central_region->getAttribute("_viewport_x_max_org")); - viewport[2] = static_cast(central_region->getAttribute("_viewport_y_min_org")); - viewport[3] = static_cast(central_region->getAttribute("_viewport_y_max_org")); plot_viewport[0] = static_cast(plot_parent->getAttribute("plot_x_min")); plot_viewport[1] = static_cast(plot_parent->getAttribute("plot_x_max")); plot_viewport[2] = @@ -1054,7 +1173,6 @@ static void calculateViewport(const std::shared_ptr &element) 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")); - start_aspect_ratio_ws = static_cast(plot_parent->getAttribute("_start_aspect_ratio")); kind = static_cast(plot_parent->getAttribute("kind")); if (keep_aspect_ratio && only_quadratic_aspect_ratio) @@ -1076,8 +1194,11 @@ static void calculateViewport(const std::shared_ptr &element) } } - if (element->hasAttribute("offset")) offset = static_cast(element->getAttribute("offset")); - if (element->hasAttribute("width")) width = static_cast(element->getAttribute("width")); + if (element->hasAttribute("offset") && !element->hasAttribute("marginal_heatmap_side_plot")) + offset = static_cast(element->getAttribute("offset")); + if (element->hasAttribute("width") && !element->hasAttribute("marginal_heatmap_side_plot")) + width = static_cast(element->getAttribute("width")); + // TODO: Change this later when other elements than texts can be inside the side_regions which isn't displayed by // the if condition if (!element->hasAttribute("marginal_heatmap_side_plot") && element->querySelectors("colorbar") == nullptr) @@ -1086,101 +1207,130 @@ static void calculateViewport(const std::shared_ptr &element) width = 0.0; } + if (!element->hasAttribute("_offset_set_by_user")) element->setAttribute("offset", offset); + if (!element->hasAttribute("_width_set_by_user")) element->setAttribute("width", width); + // apply text width to the side_region - if (location == "top" && plot_parent->hasAttribute("title_margin") && - static_cast(plot_parent->getAttribute("title_margin")) && - !element->hasAttribute("marginal_heatmap_side_plot")) - { - width += (0.025 + 0.075) * (plot_viewport[3] - plot_viewport[2]); - } - if (location == "left" && central_region->hasAttribute("y_label_margin") && - static_cast(central_region->getAttribute("y_label_margin")) && - std::find(kinds_with_possible_label.begin(), kinds_with_possible_label.end(), kind) != - kinds_with_possible_label.end()) + if (kind != "imshow") { - width += (0.075 + 0.05) * (plot_viewport[1] - plot_viewport[0]); - } - if (location == "bottom" && central_region->hasAttribute("x_label_margin") && - static_cast(central_region->getAttribute("x_label_margin")) && - std::find(kinds_with_possible_label.begin(), kinds_with_possible_label.end(), kind) != - kinds_with_possible_label.end()) - { - width += (0.075 + 0.05) * (plot_viewport[3] - plot_viewport[2]); + if (location == "top" && element->hasAttribute("text_content") && + !element->hasAttribute("marginal_heatmap_side_plot")) + { + width += (0.025 + ((element->hasAttribute("text_is_title") && + static_cast(element->getAttribute("text_is_title"))) + ? 0.075 + : 0.05)) * + (plot_viewport[3] - plot_viewport[2]); + } + if (location == "left" && element->hasAttribute("text_content")) + { + width += (0.075 + 0.05) * (plot_viewport[1] - plot_viewport[0]); + } + if (location == "bottom" && element->hasAttribute("text_content")) + { + width += (0.075 + 0.05) * (plot_viewport[3] - plot_viewport[2]); + } + if (location == "right" && element->hasAttribute("text_content")) + { + width += (0.075 + 0.05) * (plot_viewport[1] - plot_viewport[0]); + } } - 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); + setViewportForSideRegionElements(element, offset, width, uniform_data); + } + else if (element->localName() == "text_region") + { + double width = 0.0, offset = 0.0; + double plot_viewport[4]; + std::string kind, location; + auto plot_parent = element; + getPlotParent(plot_parent); - // 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) + plot_viewport[0] = static_cast(plot_parent->getAttribute("plot_x_min")); + plot_viewport[1] = static_cast(plot_parent->getAttribute("plot_x_max")); + plot_viewport[2] = + 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")); + + // apply text width to the side_region + if (kind != "imshow") { - 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) + if (location == "top") { - offset_rel = offset * aspect_ratio_ws; - width_rel = width * aspect_ratio_ws; + width += (0.025 + ((element->parentElement()->hasAttribute("text_is_title") && + static_cast(element->parentElement()->getAttribute("text_is_title"))) + ? 0.075 + : 0.05)) * + (plot_viewport[3] - plot_viewport[2]); + if (element->parentElement()->hasAttribute("offset")) + offset = static_cast(element->parentElement()->getAttribute("offset")); + if (element->parentElement()->hasAttribute("width")) + offset += static_cast(element->parentElement()->getAttribute("width")); } - else + if (location == "left") { - offset_rel = offset / aspect_ratio_ws; - width_rel = width / aspect_ratio_ws; + width += (0.075 + 0.05) * (plot_viewport[1] - plot_viewport[0]); + } + if (location == "bottom") + { + width += (0.075 + 0.05) * (plot_viewport[3] - plot_viewport[2]); + } + if (location == "right") + { + width += (0.075 + 0.05) * (plot_viewport[1] - plot_viewport[0]); } - } - else - { - auto default_diag_factor = static_cast(element->getAttribute("_default_diag_factor")); - offset_rel = offset * diag_factor * default_diag_factor; - width_rel = width * diag_factor * default_diag_factor; } - if (location == "right") - { - max_vp = getMaxViewport(element, true); - render->setViewport(element, viewport[1] + offset_rel, grm_min(viewport[1] + offset_rel + width_rel, max_vp), - viewport[2], viewport[3]); - element->setAttribute("_viewport_x_min_org", viewport[1] + offset_rel); - element->setAttribute("_viewport_x_max_org", grm_min(viewport[1] + offset_rel + width_rel, max_vp)); - element->setAttribute("_viewport_y_min_org", viewport[2]); - element->setAttribute("_viewport_y_max_org", viewport[3]); - } - else if (location == "left") - { - min_vp = getMinViewport(element, true); - render->setViewport(element, grm_max(viewport[0] - (offset_rel + width_rel), min_vp), viewport[0], - viewport[2], viewport[3]); - element->setAttribute("_viewport_x_min_org", grm_max(viewport[0] - (offset_rel + width_rel), min_vp)); - element->setAttribute("_viewport_x_max_org", viewport[0]); - element->setAttribute("_viewport_y_min_org", viewport[2]); - element->setAttribute("_viewport_y_max_org", viewport[3]); - } - else if (location == "top") - { - max_vp = getMaxViewport(element, false); - render->setViewport(element, viewport[0], viewport[1], viewport[3] + offset_rel, - grm_min(viewport[3] + offset_rel + width_rel, max_vp)); - element->setAttribute("_viewport_x_min_org", viewport[0]); - element->setAttribute("_viewport_x_max_org", viewport[1]); - element->setAttribute("_viewport_y_min_org", viewport[3] + offset_rel); - element->setAttribute("_viewport_y_max_org", grm_min(viewport[3] + offset_rel + width_rel, max_vp)); - } - else if (location == "bottom") + setViewportForSideRegionElements(element, offset, width, false); + } + else if (element->localName() == "side_plot_region") + { + double plot_viewport[4]; + double offset = PLOT_DEFAULT_SIDEREGION_OFFSET, width = PLOT_DEFAULT_SIDEREGION_WIDTH; + std::string kind, location; + bool keep_aspect_ratio = false, uniform_data = true, only_quadratic_aspect_ratio = false; + auto plot_parent = element; + getPlotParent(plot_parent); + + plot_viewport[0] = static_cast(plot_parent->getAttribute("plot_x_min")); + plot_viewport[1] = static_cast(plot_parent->getAttribute("plot_x_max")); + plot_viewport[2] = + 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")); + 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")); + + if (keep_aspect_ratio && only_quadratic_aspect_ratio) { - min_vp = getMinViewport(element, false); - render->setViewport(element, viewport[0], viewport[1], - grm_max(viewport[2] - (offset_rel + width_rel), min_vp), viewport[2]); - element->setAttribute("_viewport_x_min_org", viewport[0]); - element->setAttribute("_viewport_x_max_org", viewport[1]); - element->setAttribute("_viewport_y_min_org", grm_max(viewport[2] - (offset_rel + width_rel), min_vp)); - element->setAttribute("_viewport_y_max_org", viewport[2]); + for (const auto &series : plot_parent->querySelectors("central_region")->children()) + { + if (!starts_with(series->localName(), "series_")) continue; + uniform_data = isUniformData(series, render->getContext()); + if (!uniform_data) break; + } + if (kind == "marginal_heatmap" && uniform_data) + uniform_data = isUniformData(plot_parent->children()[0], render->getContext()); + if (uniform_data) + { + double border = + 0.5 * (plot_viewport[1] - plot_viewport[0]) * (1.0 - 1.0 / (DEFAULT_ASPECT_RATIO_FOR_SCALING)); + plot_viewport[0] += border; + plot_viewport[1] -= border; + } } + + if (element->parentElement()->hasAttribute("offset")) + offset = static_cast(element->parentElement()->getAttribute("offset")); + if (element->parentElement()->hasAttribute("width")) + width = static_cast(element->parentElement()->getAttribute("width")); + + setViewportForSideRegionElements(element, offset, width, false); } else if (element->localName() == "colorbar") // TODO: adjust this calculation when texts are included in side_region { @@ -1515,7 +1665,7 @@ static void applyMoveTransformation(const std::shared_ptr &element private_shift = true; } - if (element->localName() == "text") gr_settextoffset(x_shift, -y_shift); + if (element->localName() == "text" && (x_shift != 0 || y_shift != 0)) gr_settextoffset(x_shift, -y_shift); // when the element contains an axes the max viewport must be smaller than normal to respect the axes vp_border_x_min = getMinViewport(element, true); @@ -3515,6 +3665,10 @@ static void processFont(const std::shared_ptr &element) logger((stderr, "Using font: %d with precision %d\n", font, font_precision)); gr_settextfontprec(font, font_precision); } + else + { + if (element->hasAttribute("font")) logger((stderr, "Font precision is missing\n")); + } /* TODO: Implement other datatypes for `font` and `font_precision` */ } @@ -3756,7 +3910,7 @@ static void processMarginalHeatmapKind(const std::shared_ptr &elem continue; if (mkind == "line") { - for (const auto &series : side_region->children()) + for (const auto &series : side_region->querySelectors("side_plot_region")->children()) { // when processing all elements the first side_region has a series with xi while the second side_regions // wasn't processed yet so the series doesn't has the xi attribute; so we skip this side_region/series @@ -3933,7 +4087,7 @@ static void processMarginalHeatmapKind(const std::shared_ptr &elem } else if (mkind == "all") { - for (const auto &series : side_region->children()) + for (const auto &series : side_region->querySelectors("side_plot_region")->children()) { int cnt = 0; auto x_ind = static_cast(element->getAttribute("x_ind")); @@ -4331,7 +4485,7 @@ static void processRelativeCharHeight(const std::shared_ptr &eleme double viewport[4], subplot_viewport[4]; auto plot_element = getSubplotElement(element); double char_height, max_char_height, max_char_height_rel; - std::shared_ptr central_region, central_region_parent, subplot_parent; + std::shared_ptr central_region_parent, subplot_parent; auto kind = static_cast(plot_element->getAttribute("kind")); double diag_factor; double metric_width, metric_height; @@ -4342,24 +4496,18 @@ static void processRelativeCharHeight(const std::shared_ptr &eleme : plot_element; central_region_parent = plot_element; if (kind == "marginal_heatmap") central_region_parent = plot_element->children()[0]; - for (const auto &child : central_region_parent->children()) - { - if (child->localName() == "central_region") - { - central_region = child; - break; - } - } - if (!central_region->hasAttribute("viewport_x_min") || !central_region->hasAttribute("viewport_x_max") || - !central_region->hasAttribute("viewport_y_min") || !central_region->hasAttribute("viewport_y_max")) + if (!element->parentElement()->hasAttribute("viewport_x_min") || + !element->parentElement()->hasAttribute("viewport_x_max") || + !element->parentElement()->hasAttribute("viewport_y_min") || + !element->parentElement()->hasAttribute("viewport_y_max")) { throw NotFoundError("Viewport not found\n"); } - 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")); + viewport[0] = static_cast(element->parentElement()->getAttribute("viewport_x_min")); + viewport[1] = static_cast(element->parentElement()->getAttribute("viewport_x_max")); + viewport[2] = static_cast(element->parentElement()->getAttribute("viewport_y_min")); + viewport[3] = static_cast(element->parentElement()->getAttribute("viewport_y_max")); // is always set otherwise the method wouldn't be called max_char_height = static_cast(element->getAttribute("max_char_height")); @@ -4667,10 +4815,12 @@ static void processTextColorForBackground(const std::shared_ptr &e if (color_lightness < 0.4) { gr_settextcolorind(0); + element->setAttribute("text_color_ind", 0); } else { gr_settextcolorind(1); + element->setAttribute("text_color_ind", 1); } } } @@ -4727,49 +4877,6 @@ std::string tickOrientationIntToString(int tick_orientation) return "down"; } -static void processTitle(const std::shared_ptr &element) -{ - double viewport[4]; - std::shared_ptr side_region = nullptr, plot_parent = element; - del_values del = del_values::update_without_default; - getPlotParent(plot_parent); - - side_region = plot_parent->querySelectors("side_region[location=\"top\"]"); - if (side_region == nullptr) return; - viewport[0] = static_cast(side_region->getAttribute("viewport_x_min")); - viewport[1] = static_cast(side_region->getAttribute("viewport_x_max")); - viewport[2] = static_cast(side_region->getAttribute("viewport_y_min")); - viewport[3] = static_cast(side_region->getAttribute("viewport_y_max")); - - double x = 0.5 * (viewport[0] + viewport[1]); - double y = viewport[3]; - auto title = static_cast(plot_parent->getAttribute("title")); - - if (title.empty()) return; // Empty title is pointless, no need to waste the space for nothing - auto kind = static_cast(plot_parent->getAttribute("kind")); - if (kind == "imshow" || kind == "isosurface") return; // Don't draw a title for imshow and isosurface - if (auto render = std::dynamic_pointer_cast(element->ownerDocument())) - { - // title is unique so child_id isn't needed here - del = del_values(static_cast(side_region->getAttribute("_delete_children"))); - auto title_elem = plot_parent->querySelectors("[name=\"title\"]"); - if ((del != del_values::update_without_default && del != del_values::update_with_default) || - title_elem == nullptr) - { - if (title_elem != nullptr) title_elem->remove(); - title_elem = render->createText(x, y, title); - title_elem->setAttribute("name", "title"); - title_elem->setAttribute("z_index", 2); - render->setTextAlign(title_elem, GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_TOP); - side_region->append(title_elem); - } - else if (title_elem != nullptr) - { - title_elem = render->createText(x, y, title, CoordinateSpace::NDC, title_elem); - } - } -} - static void processTransparency(const std::shared_ptr &element) { gr_settransparency(static_cast(element->getAttribute("transparency"))); @@ -4981,118 +5088,6 @@ void GRM::Render::calculateCharHeight(const std::shared_ptr &eleme processCharHeight(plot_parent); } -static void processXlabel(const std::shared_ptr &element) -{ - double viewport[4], char_height; - std::string kind; - del_values del = del_values::update_without_default; - std::shared_ptr side_region = nullptr, plot_parent = element; - - getPlotParent(plot_parent); - auto coordinate_system = plot_parent->querySelectors("coordinate_system"); - - gr_inqcharheight(&char_height); - side_region = plot_parent->querySelectors("side_region[location=\"bottom\"]"); - if (side_region == nullptr) return; - viewport[0] = static_cast(side_region->getAttribute("viewport_x_min")); - viewport[1] = static_cast(side_region->getAttribute("viewport_x_max")); - viewport[2] = static_cast(side_region->getAttribute("viewport_y_min")); - viewport[3] = static_cast(side_region->getAttribute("viewport_y_max")); - kind = static_cast(plot_parent->getAttribute("kind")); - - double x = 0.5 * (viewport[0] + viewport[1]); - double y = viewport[2] + 0.5 * char_height; - auto x_label = static_cast(coordinate_system->getAttribute("x_label")); - if (x_label.empty()) return; // Empty xlabel is pointless, no need to waste the space for nothing - - if (auto render = std::dynamic_pointer_cast(element->ownerDocument())) - { - del = del_values(static_cast(side_region->getAttribute("_delete_children"))); - auto xlabel_elem = side_region->querySelectors("text[name=\"x_label\"]"); - - if (((del != del_values::update_without_default && del != del_values::update_with_default) || - xlabel_elem == nullptr) && - std::find(kinds_with_possible_label.begin(), kinds_with_possible_label.end(), kind) != - kinds_with_possible_label.end()) - { - if (xlabel_elem != nullptr) xlabel_elem->remove(); - xlabel_elem = render->createText(x, y, x_label); - render->setTextAlign(xlabel_elem, GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_BOTTOM); - side_region->appendChild(xlabel_elem); - } - else if (xlabel_elem != nullptr) - { - if (std::find(kinds_with_possible_label.begin(), kinds_with_possible_label.end(), kind) != - kinds_with_possible_label.end()) - { - render->createText(x, y, x_label, CoordinateSpace::NDC, xlabel_elem); - } - else - { - xlabel_elem->remove(); - } - } - if (xlabel_elem != nullptr) xlabel_elem->setAttribute("name", "x_label"); - } -} - -static void processYlabel(const std::shared_ptr &element) -{ - double viewport[4], char_height; - std::string kind; - del_values del = del_values::update_without_default; - std::shared_ptr plot_parent = element->parentElement(), side_region = nullptr; - - getPlotParent(plot_parent); - auto coordinate_system = plot_parent->querySelectors("coordinate_system"); - auto central_region = coordinate_system->parentElement(); - - gr_inqcharheight(&char_height); - side_region = plot_parent->querySelectors("side_region[location=\"left\"]"); - if (side_region == nullptr) return; - viewport[0] = static_cast(side_region->getAttribute("viewport_x_min")); - viewport[1] = static_cast(side_region->getAttribute("viewport_x_max")); - viewport[2] = static_cast(side_region->getAttribute("viewport_y_min")); - viewport[3] = static_cast(side_region->getAttribute("viewport_y_max")); - kind = static_cast(plot_parent->getAttribute("kind")); - - double x = viewport[0] + 0.5 * char_height; - double y = 0.5 * (viewport[2] + viewport[3]); - auto y_label = static_cast(coordinate_system->getAttribute("y_label")); - if (y_label.empty()) return; // Empty ylabel is pointless, no need to waste the space for nothing - - if (auto render = std::dynamic_pointer_cast(element->ownerDocument())) - { - del = del_values(static_cast(side_region->getAttribute("_delete_children"))); - auto ylabel_elem = side_region->querySelectors("text[name=\"y_label\"]"); - - if (((del != del_values::update_without_default && del != del_values::update_with_default) || - ylabel_elem == nullptr) && - std::find(kinds_with_possible_label.begin(), kinds_with_possible_label.end(), kind) != - kinds_with_possible_label.end()) - { - if (ylabel_elem != nullptr) ylabel_elem->remove(); - ylabel_elem = render->createText(x, y, y_label); - render->setTextAlign(ylabel_elem, GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_TOP); - render->setCharUp(ylabel_elem, -1, 0); - side_region->appendChild(ylabel_elem); - } - else if (ylabel_elem != nullptr) - { - if (std::find(kinds_with_possible_label.begin(), kinds_with_possible_label.end(), kind) != - kinds_with_possible_label.end()) - { - render->createText(x, y, y_label, CoordinateSpace::NDC, ylabel_elem); - } - else - { - ylabel_elem->remove(); - } - } - if (ylabel_elem != nullptr) ylabel_elem->setAttribute("name", "y_label"); - } -} - static void processXTickLabels(const std::shared_ptr &element) { double viewport[4], char_height; @@ -5342,15 +5337,12 @@ void GRM::Render::processAttributes(const std::shared_ptr &element {std::string("text_align_vertical"), processTextAlign}, // the alignment in both directions is set {std::string("text_color_ind"), processTextColorInd}, {std::string("text_encoding"), processTextEncoding}, - {std::string("title"), processTitle}, {std::string("viewport"), processViewport}, {std::string("ws_viewport_x_min"), processWSViewport}, // the xmin element can be used here cause all 4 are required {std::string("ws_window_x_min"), processWSWindow}, // the xmin element can be used here cause all 4 are required {std::string("x_flip"), processFlip}, // y_flip is also set - {std::string("x_label"), processXlabel}, {std::string("x_tick_labels"), processXTickLabels}, - {std::string("y_label"), processYlabel}, {std::string("y_tick_labels"), processYTickLabels}, {std::string("z_index"), processZIndex}, }; @@ -7212,8 +7204,7 @@ static void processLegend(const std::shared_ptr &element, const st } else { - unsigned int num_labels = labels.size(); - std::shared_ptr fr, dr; + std::shared_ptr fr, dr, text; int label_child_id = 0; double viewport[4]; @@ -7288,7 +7279,6 @@ static void processLegend(const std::shared_ptr &element, const st } if (labelGroup != nullptr) { - std::shared_ptr fr, dr, text; if (del != del_values::update_without_default && del != del_values::update_with_default) { fr = render->createFillRect(viewport[0] + 0.02 * scale_factor, viewport[0] + 0.04 * scale_factor, @@ -8223,7 +8213,7 @@ static void processHist(const std::shared_ptr &element, const std: y_max = static_cast(element->getAttribute("y_range_max")); if (std::isnan(y_min)) y_min = 0.0; - if (element->parentElement()->hasAttribute("marginal_heatmap_side_plot")) + if (element->parentElement()->parentElement()->hasAttribute("marginal_heatmap_side_plot")) { std::shared_ptr marginal_heatmap; for (const auto &children : plot_parent->children()) @@ -10938,8 +10928,8 @@ static void processStairs(const std::shared_ptr &element, const st del_values del = del_values::update_without_default; int child_id = 0; - if (element->parentElement()->hasAttribute("marginal_heatmap_side_plot")) - element_context = element->parentElement()->parentElement(); + if (element->parentElement()->parentElement()->hasAttribute("marginal_heatmap_side_plot")) + element_context = element->parentElement()->parentElement()->parentElement(); if (!element_context->hasAttribute("x")) throw NotFoundError("Stairs series is missing required attribute x-data.\n"); auto x = static_cast(element_context->getAttribute("x")); @@ -10978,7 +10968,7 @@ static void processStairs(const std::shared_ptr &element, const st del = del_values(static_cast(element->getAttribute("_delete_children"))); clearOldChildren(&del, element); - if (element->parentElement()->hasAttribute("marginal_heatmap_side_plot")) + if (element->parentElement()->parentElement()->hasAttribute("marginal_heatmap_side_plot")) { double y_max = 0; unsigned int z_length = 0; @@ -11010,7 +11000,7 @@ static void processStairs(const std::shared_ptr &element, const st (*context)["xi" + str] = xi_vec; element->setAttribute("xi", "xi" + str); - processMarginalHeatmapSidePlot(element->parentElement()); + processMarginalHeatmapSidePlot(element->parentElement()->parentElement()); processMarginalHeatmapKind(element_context); } else @@ -11809,7 +11799,9 @@ static void processMarginalHeatmapPlot(const std::shared_ptr &elem { sub_group = global_render->createSeries("hist"); sub_group->setAttribute("_child_id", child_id++); - side_region->append(sub_group); + auto side_plot_region = global_render->createSidePlotRegion(); + side_region->append(side_plot_region); + side_plot_region->append(sub_group); } else { @@ -11850,13 +11842,16 @@ static void processMarginalHeatmapPlot(const std::shared_ptr &elem } // special case for marginal_heatmap_kind line - when new indices != -1 are received the 2 lines should be // displayed - sub_group = side_region->querySelectors("[_child_id=0]"); + sub_group = side_region->querySelectors("series_stairs[_child_id=0]"); + auto side_plot_region = side_region->querySelectors("side_plot_region"); if ((del != del_values::update_without_default && del != del_values::update_with_default) || (sub_group == nullptr && static_cast(element->getAttribute("_update_required")))) { sub_group = global_render->createSeries("stairs"); sub_group->setAttribute("_child_id", child_id++); - side_region->append(sub_group); + if (!side_plot_region) side_plot_region = global_render->createSidePlotRegion(); + side_region->append(side_plot_region); + side_plot_region->append(sub_group); } else { @@ -11894,7 +11889,7 @@ static void processMarginalHeatmapPlot(const std::shared_ptr &elem auto tmp = element->querySelectorsAll("series_hist"); for (const auto &child : tmp) { - if (!child->parentElement()->hasAttribute("marginal_heatmap_side_plot")) continue; + if (!child->parentElement()->parentElement()->hasAttribute("marginal_heatmap_side_plot")) continue; if (static_cast(child->getAttribute("kind")) == "hist" || static_cast(child->getAttribute("kind")) == "stairs") { @@ -12391,6 +12386,7 @@ static void processText(const std::shared_ptr &element, const std: */ gr_savestate(); double tbx[4], tby[4]; + int text_color_ind = 1; bool text_fits = true; auto x = static_cast(element->getAttribute("x")); auto y = static_cast(element->getAttribute("y")); @@ -12398,6 +12394,8 @@ static void processText(const std::shared_ptr &element, const std: auto available_width = static_cast(element->getAttribute("width")); auto available_height = static_cast(element->getAttribute("height")); auto space = static_cast(static_cast(element->getAttribute("space"))); + if (element->hasAttribute("text_color_ind")) + text_color_ind = static_cast(element->getAttribute("text_color_ind")); applyMoveTransformation(element); if (space == CoordinateSpace::WC) @@ -12410,8 +12408,8 @@ static void processText(const std::shared_ptr &element, const std: gr_inqtext(x, y, &str[0], tbx, tby); auto minmax_x = std::minmax_element(std::begin(tbx), std::end(tbx)); auto minmax_y = std::minmax_element(std::begin(tby), std::end(tby)); - auto width = (double)(minmax_x.second - minmax_x.first); - auto height = (double)(minmax_y.second - minmax_y.first); + auto width = static_cast((minmax_x.second - minmax_x.first)); + auto height = static_cast((minmax_y.second - minmax_y.first)); if (width > available_width && height > available_height) { gr_setcharup(0.0, 1.0); @@ -12435,10 +12433,89 @@ static void processText(const std::shared_ptr &element, const std: } } } - if (text_fits && redraw_ws) gr_text(x, y, &str[0]); + if (text_fits && redraw_ws) + { + gr_settextcolorind(text_color_ind); // needed to have a visible text after update + gr_text(x, y, &str[0]); + } gr_restorestate(); } +static void processTextRegion(const std::shared_ptr &element, + const std::shared_ptr &context) +{ + double viewport[4], char_height; + double x, y; + std::string kind, location, text; + bool is_title; + del_values del = del_values::update_without_default; + std::shared_ptr plot_parent = element->parentElement(), side_region = element->parentElement(), + text_elem; + getPlotParent(plot_parent); + + del = del_values(static_cast(element->getAttribute("_delete_children"))); + clearOldChildren(&del, element); + + gr_inqcharheight(&char_height); + calculateViewport(element); + applyMoveTransformation(element); + + 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")); + kind = static_cast(plot_parent->getAttribute("kind")); + location = static_cast(side_region->getAttribute("location")); + is_title = side_region->hasAttribute("text_is_title") && static_cast(side_region->getAttribute("text_is_title")); + text = static_cast(side_region->getAttribute("text_content")); + + if (location == "left") + { + x = viewport[0] + 0.5 * char_height; + y = 0.5 * (viewport[2] + viewport[3]); + } + else if (location == "right") + { + x = viewport[1] - 0.5 * char_height; + y = 0.5 * (viewport[2] + viewport[3]); + } + else if (location == "bottom") + { + x = 0.5 * (viewport[0] + viewport[1]); + y = viewport[2] + 0.5 * char_height; + } + else if (location == "top") + { + x = 0.5 * (viewport[0] + viewport[1]); + y = viewport[3]; + if (!is_title) y -= 0.5 * char_height; + } + + if ((del != del_values::update_without_default && del != del_values::update_with_default) && !text.empty()) + { + text_elem = global_render->createText(x, y, text); + text_elem->setAttribute("_child_id", 0); + element->appendChild(text_elem); + } + else + { + if (!text.empty()) + { + text_elem = element->querySelectors("text[_child_id=\"0\"]"); + if (text_elem) global_render->createText(x, y, text, CoordinateSpace::NDC, text_elem); + } + } + if (text_elem) + { + if (location == "left" || location == "top") + global_render->setTextAlign(text_elem, GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_TOP); + if (location == "bottom" || location == "bottom") + global_render->setTextAlign(text_elem, GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_BOTTOM); + if (location == "top" && is_title) text_elem->setAttribute("z_index", 2); + if (location == "left" || location == "right") global_render->setCharUp(text_elem, -1, 0); + } +} + static void processTitles3d(const std::shared_ptr &element, const std::shared_ptr &context) { /*! @@ -13463,29 +13540,47 @@ static void plotCoordinateRanges(const std::shared_ptr &element, static void processSideRegion(const std::shared_ptr &element, const std::shared_ptr &context) { + int child_id = 0; + del_values del = del_values::update_without_default; std::shared_ptr plot_parent = element; getPlotParent(plot_parent); - calculateViewport(element); - applyMoveTransformation(element); - GRM::Render::processViewport(element); - GRM::Render::processWindow(element); /* needs to be set before space 3d is processed */ - GRM::Render::processScale(plot_parent); /* needs to be set before flip is processed */ - for (const auto &child : element->children()) + del = del_values(static_cast(element->getAttribute("_delete_children"))); + clearOldChildren(&del, element); + + if (element->hasAttribute("text_content")) { - if (child->localName() == "text" && static_cast(child->getAttribute("name")) == "x_label") - { - processXlabel(child); - } - if (child->localName() == "text" && static_cast(child->getAttribute("name")) == "y_label") + auto kind = static_cast(plot_parent->getAttribute("kind")); + auto text = static_cast(element->getAttribute("text_content")); + auto location = static_cast(element->getAttribute("location")); + + if (((del != del_values::update_without_default && del != del_values::update_with_default)) && !text.empty() && + kind != "imshow" && + (std::find(kinds_3d.begin(), kinds_3d.end(), kind) == kinds_3d.end() || location == "top")) { - processYlabel(child); + auto text_elem = global_render->createTextRegion(); + text_elem->setAttribute("_child_id", child_id++); + element->appendChild(text_elem); } - if (child->localName() == "text" && static_cast(child->getAttribute("name")) == "title") + else { - processTitle(child); + auto text_elem = element->querySelectors("text_region[_child_id=\"" + std::to_string(child_id++) + "\"]"); + if (text_elem) global_render->createTextRegion(text_elem); } } + + calculateViewport(element); + applyMoveTransformation(element); + GRM::Render::processViewport(element); + GRM::Render::processWindow(element); /* needs to be set before space 3d is processed */ + GRM::Render::processScale(plot_parent); /* needs to be set before flip is processed */ +} + +static void processSidePlotRegion(const std::shared_ptr &element, + const std::shared_ptr &context) +{ + calculateViewport(element); + applyMoveTransformation(element); } static void processCoordinateSystem(const std::shared_ptr &element, @@ -13798,8 +13893,6 @@ static void processCoordinateSystem(const std::shared_ptr &element { global_render->setOriginPosition(axes, "high", "high"); } - if (element->hasAttribute("x_label") && axes != nullptr) processXlabel(axes); - if (element->hasAttribute("y_label") && axes != nullptr) processYlabel(axes); } } applyMoveTransformation(element); @@ -14040,43 +14133,25 @@ static void processPlot(const std::shared_ptr &element, const std: std::shared_ptr side_region; auto kind = static_cast(element->getAttribute("kind")); - // create side_region for labels, title, colorbar and marginal_heatmap_plot if they don't exist yet - if (central_region_parent->localName() == "marginal_heatmap_plot" || - str_equals_any(kind, "contour", "contourf", "hexbin", "heatmap", "nonuniformheatmap", "surface", "tricontour", - "trisurface", "volume", "marginal_heatmap", "quiver", "polar_heatmap", "nonuniformpolar_heatmap")) + if (!element->querySelectors("side_region[location=\"right\"]")) { - if (!element->querySelectors("side_region[location=\"right\"]")) - { - side_region = global_render->createSideRegion("right"); - central_region_parent->append(side_region); - } + side_region = global_render->createSideRegion("right"); + central_region_parent->append(side_region); } - if (central_region_parent->localName() == "marginal_heatmap_plot" || - (element->hasAttribute("title_margin") && static_cast(element->getAttribute("title_margin")))) + if (!element->querySelectors("side_region[location=\"top\"]")) { - if (!element->querySelectors("side_region[location=\"top\"]")) - { - side_region = global_render->createSideRegion("top"); - central_region_parent->append(side_region); - } + side_region = global_render->createSideRegion("top"); + central_region_parent->append(side_region); } - if (central_region->hasAttribute("y_label_margin") && - static_cast(central_region->getAttribute("y_label_margin"))) + if (!element->querySelectors("side_region[location=\"left\"]")) { - if (!element->querySelectors("side_region[location=\"left\"]")) - { - side_region = global_render->createSideRegion("left"); - central_region_parent->append(side_region); - } + side_region = global_render->createSideRegion("left"); + central_region_parent->append(side_region); } - if (central_region->hasAttribute("x_label_margin") && - static_cast(central_region->getAttribute("x_label_margin"))) + if (!element->querySelectors("side_region[location=\"bottom\"]")) { - if (!element->querySelectors("side_region[location=\"bottom\"]")) - { - side_region = global_render->createSideRegion("bottom"); - central_region_parent->append(side_region); - } + side_region = global_render->createSideRegion("bottom"); + central_region_parent->append(side_region); } } @@ -14196,14 +14271,16 @@ static void processElement(const std::shared_ptr &element, const s {std::string("polymarker_3d"), PushDrawableToZQueue(processPolymarker3d)}, {std::string("series"), processSeries}, {std::string("side_region"), processSideRegion}, + {std::string("side_plot_region"), processSidePlotRegion}, {std::string("text"), PushDrawableToZQueue(processText)}, + {std::string("text_region"), processTextRegion}, {std::string("titles_3d"), PushDrawableToZQueue(processTitles3d)}, }; /* Modifier */ if (str_equals_any(element->localName(), "axes_text_group", "central_region", "figure", "plot", "label", "labels_group", "root", "x_tick_label_group", "y_tick_label_group", "layout_grid_element", - "side_region")) + "side_region", "text_region", "side_plot_region")) { bool old_state = automatic_update; automatic_update = false; @@ -14240,6 +14317,8 @@ static void processElement(const std::shared_ptr &element, const s calculateViewport(element); } if (element->localName() == "side_region") processSideRegion(element, context); + if (element->localName() == "text_region") processTextRegion(element, context); + if (element->localName() == "side_plot_region") processSidePlotRegion(element, context); GRM::Render::processAttributes(element); automatic_update = old_state; if (element->localName() != "root") applyMoveTransformation(element); @@ -14315,7 +14394,7 @@ static void processElement(const std::shared_ptr &element, const s automatic_update = old_state; } else if (automatic_update && static_cast(global_root->getAttribute("_modified")) || - element->parentElement()->hasAttribute("marginal_heatmap_side_plot")) + element->parentElement()->parentElement()->hasAttribute("marginal_heatmap_side_plot")) { bool old_state = automatic_update; automatic_update = false; @@ -15094,7 +15173,6 @@ std::vector GRM::Render::getDefaultAndTooltip(const std::shared_ptr {std::string("tick_orientation"), std::vector{"None", "The orientation of the axes ticks"}}, {std::string("tick_size"), std::vector{"0.005", "The size of the ticks"}}, {std::string("title"), std::vector{"None", "The plot title"}}, - {std::string("title_margin"), std::vector{"None", "Sets if there is a title margin"}}, {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"}}, @@ -15137,7 +15215,6 @@ std::vector GRM::Render::getDefaultAndTooltip(const std::shared_ptr {std::string("x_ind"), std::vector{"-1", "An index which is used to highlight a specific x-position"}}, {std::string("x_label"), std::vector{"None", "The label of the x-axis"}}, - {std::string("x_label_margin"), std::vector{"None", "Sets if there is a x-label margin"}}, {std::string("x_label_3d"), std::vector{"None", "The label of the x-axis"}}, {std::string("x_lim_max"), std::vector{"None", "The ending x-limit"}}, {std::string("x_lim_min"), std::vector{"None", "The beginning x-limit"}}, @@ -15178,7 +15255,6 @@ std::vector GRM::Render::getDefaultAndTooltip(const std::shared_ptr {std::string("y_ind"), std::vector{"-1", "An index which is used to highlight a specific y-position"}}, {std::string("y_label"), std::vector{"None", "The label of the y-axis"}}, - {std::string("y_label_margin"), std::vector{"None", "Sets if there is a y-label margin"}}, {std::string("y_label_3d"), std::vector{"None", "The label of the y-axis"}}, {std::string("y_labels"), std::vector{"None", "References the y-labels stored in the context"}}, {std::string("y_lim_max"), std::vector{"None", "The ending y-limit"}}, @@ -16405,6 +16481,20 @@ std::shared_ptr GRM::Render::createSideRegion(std::string location return element; } +std::shared_ptr GRM::Render::createTextRegion(const std::shared_ptr &ext_element) +{ + std::shared_ptr element = (ext_element == nullptr) ? createElement("text_region") : ext_element; + + return element; +} + +std::shared_ptr GRM::Render::createSidePlotRegion(const std::shared_ptr &ext_element) +{ + std::shared_ptr element = (ext_element == nullptr) ? createElement("side_plot_region") : ext_element; + + return element; +} + /* ~~~~~~~~~~~~~~~~~~~~~~~~~ modifier functions~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -17369,6 +17459,8 @@ void updateFilter(const std::shared_ptr &element, const std::strin { side_region_child->remove(); } + if (child->hasAttribute("marginal_heatmap_side_plot")) + child->removeAttribute("marginal_heatmap_side_plot"); } if (child->localName() == "central_region") { @@ -17394,7 +17486,7 @@ void updateFilter(const std::shared_ptr &element, const std::strin new_series->append(side_region_child); if (side_region_child->querySelectors("colorbar")) { - side_region_child->querySelectors("colorbar")->remove(); + side_region_child->querySelectors("colorbar")->parentElement()->remove(); side_region_child->setAttribute("offset", PLOT_DEFAULT_SIDEREGION_OFFSET); side_region_child->setAttribute("width", PLOT_DEFAULT_SIDEREGION_WIDTH); } @@ -17449,20 +17541,6 @@ void updateFilter(const std::shared_ptr &element, const std::strin child->remove(); } element->remove(); - if (static_cast(element->getAttribute("kind")) == "marginal_heatmap") - { - // move title and central_region into marginal_heatmap_plot - new_series->append(central_region); - for (const auto &child : central_region_parent->children()) - { - if (child->localName() == "text" && - static_cast(child->getAttribute("name")) == "title") - { - new_series->append(child); - break; - } - } - } new_series->setAttribute("_update_required", true); new_series->setAttribute("_delete_children", 2); } @@ -17616,7 +17694,9 @@ void updateFilter(const std::shared_ptr &element, const std::strin if (coordinate_system) { - std::string old_type = static_cast(coordinate_system->getAttribute("plot_type")); + auto left_side_region = plot->querySelectors("side_region[location=\"left\"]"); + auto bottom_side_region = plot->querySelectors("side_region[location=\"bottom\"]"); + auto old_type = static_cast(coordinate_system->getAttribute("plot_type")); if (grplot && old_type == "2d" && new_type == "2d") // special case which will reset the tick_orientation when kind is changed { @@ -17630,13 +17710,60 @@ void updateFilter(const std::shared_ptr &element, const std::strin coordinate_system->setAttribute("_update_required", true); coordinate_system->setAttribute("_delete_children", static_cast(del_values::recreate_all_children)); + + if (old_type == "2d") + { + if (bottom_side_region->hasAttribute("text_content")) + { + coordinate_system->setAttribute( + "x_label", + static_cast(bottom_side_region->getAttribute("text_content"))); + bottom_side_region->removeAttribute("text_content"); + bottom_side_region->setAttribute("_update_required", true); + + auto text_child = bottom_side_region->querySelectors("text_region"); + if (text_child != nullptr) bottom_side_region->removeChild(text_child); + } + if (left_side_region->hasAttribute("text_content")) + { + coordinate_system->setAttribute( + "y_label", static_cast(left_side_region->getAttribute("text_content"))); + left_side_region->removeAttribute("text_content"); + left_side_region->setAttribute("_update_required", true); + + auto text_child = left_side_region->querySelectors("text_region"); + if (text_child != nullptr) left_side_region->removeChild(text_child); + } + } + else if (old_type == "3d" && new_type == "2d") + { + if (coordinate_system->hasAttribute("x_label")) + { + bottom_side_region->setAttribute( + "text_content", static_cast(coordinate_system->getAttribute("x_label"))); + bottom_side_region->setAttribute("_update_required", true); + coordinate_system->removeAttribute("x_label"); + } + if (coordinate_system->hasAttribute("y_label")) + { + left_side_region->setAttribute( + "text_content", static_cast(coordinate_system->getAttribute("y_label"))); + left_side_region->setAttribute("_update_required", true); + coordinate_system->removeAttribute("y_label"); + } + } } if (new_kind == "imshow" || new_kind == "isosurface") { + auto top_side_region = plot->querySelectors("side_region[location=\"top\"]"); + + left_side_region->setAttribute("_update_required", true); + left_side_region->setAttribute("_delete_children", 2); + bottom_side_region->setAttribute("_update_required", true); + bottom_side_region->setAttribute("_delete_children", 2); + top_side_region->setAttribute("_update_required", true); + top_side_region->setAttribute("_delete_children", 2); coordinate_system->setAttribute("hide", true); - - std::shared_ptr title = plot->querySelectors("text[name=\"title\"]"); - if (title) title->remove(); } else if (old_kind == "imshow" || old_kind == "isosurface") { @@ -17680,16 +17807,18 @@ void updateFilter(const std::shared_ptr &element, const std::strin } if (std::find(colorbar_group.begin(), colorbar_group.end(), new_kind) == colorbar_group.end()) { - std::shared_ptr side_region, colorbar = plot->querySelectors("colorbar"); + std::shared_ptr side_region, colorbar = plot->querySelectors("colorbar"), side_plot; if (colorbar) { - side_region = colorbar->parentElement(); + side_plot = colorbar->parentElement(); + side_region = side_plot->parentElement(); for (const auto &child : colorbar->children()) { child->remove(); } colorbar->remove(); side_region->remove(); + side_plot->remove(); } } else @@ -17697,7 +17826,8 @@ void updateFilter(const std::shared_ptr &element, const std::strin double offset; int colors; std::shared_ptr side_region = plot->querySelectors("side_region[location=\"right\"]"), - colorbar = plot->querySelectors("colorbar"); + colorbar = plot->querySelectors("colorbar"), + side_plot = side_region->querySelectors("side_plot_region"); std::tie(offset, colors) = getColorbarAttributes(new_kind, plot); if (side_region == nullptr) @@ -17706,6 +17836,8 @@ void updateFilter(const std::shared_ptr &element, const std::strin side_region->setAttribute("width", PLOT_DEFAULT_COLORBAR_WIDTH); side_region->setAttribute("_update_required", true); + if (side_plot == nullptr) side_plot = global_render->createSidePlotRegion(); + colorbar = global_render->createColorbar(colors, nullptr, colorbar); colorbar->setAttribute("max_char_height", PLOT_DEFAULT_COLORBAR_MAX_CHAR_HEIGHT); @@ -17713,7 +17845,8 @@ void updateFilter(const std::shared_ptr &element, const std::strin colorbar->setAttribute("_delete_children", static_cast(del_values::recreate_all_children)); if (!plot->querySelectors("side_region[location=\"right\"]")) plot->append(side_region); - if (!plot->querySelectors("colorbar")) side_region->append(colorbar); + if (!side_region->querySelectors("side_plot_region")) side_region->append(side_plot); + if (!plot->querySelectors("colorbar")) side_plot->append(colorbar); } } } @@ -17928,6 +18061,10 @@ void updateFilter(const std::shared_ptr &element, const std::strin } else if (attr == "location") { + if (element->hasAttribute("x_scale_ndc")) element->removeAttribute("x_scale_ndc"); + if (element->hasAttribute("x_shift_ndc")) element->removeAttribute("x_shift_ndc"); + if (element->hasAttribute("y_scale_ndc")) element->removeAttribute("y_scale_ndc"); + if (element->hasAttribute("y_shift_ndc")) element->removeAttribute("y_shift_ndc"); resetOldBoundingBoxes(element); } else if (element->localName() == "integral" && diff --git a/lib/grm/src/grm/interaction.cxx b/lib/grm/src/grm/interaction.cxx index 33845655f..627557c54 100644 --- a/lib/grm/src/grm/interaction.cxx +++ b/lib/grm/src/grm/interaction.cxx @@ -361,7 +361,7 @@ err_t tooltip_list_entry_delete(tooltip_list_entry_t entry) static void moveTransformationHelper(const std::shared_ptr &element, double ndc_x, double ndc_y, double xshift, double yshift, bool is_movable) { - double tmp = 0, x_with_shift, y_with_shift, x_ndc, y_ndc; + double x_with_shift, y_with_shift, x_ndc, y_ndc; double old_x_shift = 0, old_y_shift = 0, wc_x_shift, wc_y_shift; int width, height, max_width_height; std::string post_fix = "_wc"; @@ -579,7 +579,7 @@ int grm_input(const grm_args_t *input_args) if (static_cast(subplot_element->getAttribute("x_log"))) { - for (int j = 0; j < x_length; j++) + for (int j = 0; j < (int)x_length; j++) { if (!grm_isnan(x_series_vec[j])) { @@ -592,7 +592,7 @@ int grm_input(const grm_args_t *input_args) } if (static_cast(subplot_element->getAttribute("y_log"))) { - for (int j = 0; j < y_length; j++) + for (int j = 0; j < (int)y_length; j++) { if (!grm_isnan(y_series_vec[j])) { @@ -638,7 +638,7 @@ int grm_input(const grm_args_t *input_args) if (static_cast(subplot_element->getAttribute("x_log"))) { - for (int j = 0; j < x_length - x_offset; j++) + for (int j = 0; j < (int)x_length - x_offset; j++) { double step_x = (x_series_vec[x_length - 1] - x_min) / (x_length - x_offset); double tmp = 0, a = x_min + j * step_x, b = x_min + (j + 1) * step_x; @@ -652,7 +652,7 @@ int grm_input(const grm_args_t *input_args) } if (static_cast(subplot_element->getAttribute("y_log"))) { - for (int j = 0; j < y_length - y_offset; j++) + for (int j = 0; j < (int)y_length - y_offset; j++) { double step_y = (y_series_vec[y_length - 1] - y_min) / (y_length - y_offset); double tmp = 0, a = y_min + j * step_y, b = y_min + (j + 1) * step_y; @@ -670,7 +670,7 @@ int grm_input(const grm_args_t *input_args) { double tmp = 0; xind_d = 0; - for (int j = 0; j < x_length - x_offset; j++) + for (int j = 0; j < (int)x_length - x_offset; j++) { if (tmp + x_steps[j] > (x - x_0)) break; tmp += x_steps[j]; @@ -683,7 +683,7 @@ int grm_input(const grm_args_t *input_args) { double tmp = 0; yind_d = 0; - for (int j = 0; j < y_length - y_offset; j++) + for (int j = 0; j < (int)y_length - y_offset; j++) { if (tmp + y_steps[j] < (y - y_0)) break; tmp += y_steps[j]; @@ -720,9 +720,6 @@ int grm_input(const grm_args_t *input_args) if (childKind == "hist") { // reset bar colors - bool is_horizontal = - static_cast(child->getAttribute("orientation")) == "horizontal"; - // bar level for (auto &bars : child->children()) { @@ -741,8 +738,6 @@ int grm_input(const grm_args_t *input_args) break; } - int fillColorInd = - static_cast(innerFillGroup->getAttribute("fill_color_ind")); if (xind != -1) { innerFillGroup->children()[xind]->removeAttribute("fill_color_ind"); @@ -992,7 +987,6 @@ int grm_is3d(const int x, const int y) { int width, height, max_width_height; double ndc_x, ndc_y; - const char *kind; GRM::Render::getFigureSize(&width, &height, nullptr, nullptr); max_width_height = grm_max(width, height); @@ -1236,7 +1230,7 @@ grm_accumulated_tooltip_info_t *grm_get_accumulated_tooltip_x(int mouse_x, int m err_t get_tooltips(int mouse_x, int mouse_y, err_t (*tooltip_callback)(int, int, grm_tooltip_info_t *)) { - double *x_series, *y_series, *z_series, x, y, x_min, x_max, y_min, y_max, mindiff = DBL_MAX, diff; + double x, y, x_min, x_max, y_min, y_max, mindiff = DBL_MAX, diff; double x_range_min, x_range_max, y_range_min, y_range_max, x_px, y_px; int width, height, max_width_height; std::vector labels; @@ -1305,10 +1299,12 @@ err_t get_tooltips(int mouse_x, int mouse_y, err_t (*tooltip_callback)(int, int, auto axes_vec = subplot_element->querySelectorsAll("axes"); auto coordinate_system = subplot_element->querySelectors("coordinate_system"); + auto left_side_region = subplot_element->querySelectors("side_region[location=\"left\"]"); + auto bottom_side_region = subplot_element->querySelectors("side_region[location=\"bottom\"]"); std::vector> label_vec; - if (coordinate_system && coordinate_system->hasAttribute("x_label") && coordinate_system->hasAttribute("y_label")) - label_vec.push_back(coordinate_system); + if (bottom_side_region && bottom_side_region->hasAttribute("text_content")) label_vec.push_back(bottom_side_region); + if (left_side_region && left_side_region->hasAttribute("text_content")) label_vec.push_back(left_side_region); if (label_vec.empty()) { @@ -1317,22 +1313,22 @@ err_t get_tooltips(int mouse_x, int mouse_y, err_t (*tooltip_callback)(int, int, } else { - if (!label_vec[0]->hasAttribute("x_label")) + if (!label_vec[0]->hasAttribute("text_content")) { info->xlabel = (char *)"x"; } else { - static std::string xlabel = static_cast(label_vec[0]->getAttribute("x_label")); + static auto xlabel = static_cast(label_vec[0]->getAttribute("text_content")); info->xlabel = (char *)xlabel.c_str(); } - if (!label_vec[0]->hasAttribute("y_label")) + if (!label_vec[1]->hasAttribute("text_content")) { info->ylabel = (char *)"y"; } else { - static std::string ylabel = static_cast(label_vec[0]->getAttribute("y_label")); + static auto ylabel = static_cast(label_vec[1]->getAttribute("text_content")); info->ylabel = (char *)ylabel.c_str(); } } @@ -1549,7 +1545,7 @@ err_t get_tooltips(int mouse_x, int mouse_y, err_t (*tooltip_callback)(int, int, if (static_cast(subplot_element->getAttribute("x_log"))) { - for (int j = 0; j < x_length; j++) + for (int j = 0; j < (int)x_length; j++) { if (!grm_isnan(x_series_vec[j])) { @@ -1562,7 +1558,7 @@ err_t get_tooltips(int mouse_x, int mouse_y, err_t (*tooltip_callback)(int, int, } if (static_cast(subplot_element->getAttribute("y_log"))) { - for (int j = 0; j < y_length; j++) + for (int j = 0; j < (int)y_length; j++) { if (!grm_isnan(y_series_vec[j])) { @@ -1587,7 +1583,7 @@ err_t get_tooltips(int mouse_x, int mouse_y, err_t (*tooltip_callback)(int, int, if (static_cast(subplot_element->getAttribute("x_log"))) { - for (int j = 0; j < x_length - x_offset; j++) + for (int j = 0; j < (int)x_length - x_offset; j++) { double step_x = (x_series_vec[x_length - 1] - x_min) / (x_length - x_offset); double tmp = 0, a = x_min + j * step_x, b = x_min + (j + 1) * step_x; @@ -1601,7 +1597,7 @@ err_t get_tooltips(int mouse_x, int mouse_y, err_t (*tooltip_callback)(int, int, } if (static_cast(subplot_element->getAttribute("y_log"))) { - for (int j = 0; j < y_length - y_offset; j++) + for (int j = 0; j < (int)y_length - y_offset; j++) { double step_y = (y_series_vec[y_length - 1] - y_min) / (y_length - y_offset); double tmp = 0, a = y_min + j * step_y, b = y_min + (j + 1) * step_y; @@ -1632,7 +1628,7 @@ err_t get_tooltips(int mouse_x, int mouse_y, err_t (*tooltip_callback)(int, int, { double tmp = 0; x_series_idx = x_offset; - for (int j = 0; j < x_length - x_offset; j++) + for (int j = 0; j < (int)x_length - x_offset; j++) { if (tmp + x_steps[j] > (mouse_x - x_0)) break; tmp += x_steps[j]; @@ -1646,7 +1642,7 @@ err_t get_tooltips(int mouse_x, int mouse_y, err_t (*tooltip_callback)(int, int, { double tmp = 0; y_series_idx = y_offset; - for (int j = 0; j < y_length - y_offset; j++) + for (int j = 0; j < (int)y_length - y_offset; j++) { if (tmp + y_steps[j] < (mouse_y - y_0)) break; tmp += y_steps[j]; @@ -1702,22 +1698,22 @@ err_t get_tooltips(int mouse_x, int mouse_y, err_t (*tooltip_callback)(int, int, } else { - if (!label_vec[0]->hasAttribute("x_label")) + if (!label_vec[0]->hasAttribute("text_content")) { info->xlabel = (char *)"x"; } else { - static std::string xlabel = static_cast(label_vec[0]->getAttribute("x_label")); + static auto xlabel = static_cast(label_vec[0]->getAttribute("text_content")); info->xlabel = (char *)xlabel.c_str(); } - if (!label_vec[0]->hasAttribute("y_label")) + if (!label_vec[1]->hasAttribute("text_content")) { info->ylabel = (char *)"y"; } else { - static std::string ylabel = static_cast(label_vec[0]->getAttribute("y_label")); + static auto ylabel = static_cast(label_vec[1]->getAttribute("text_content")); info->ylabel = (char *)ylabel.c_str(); } } diff --git a/lib/grm/src/grm/plot.cxx b/lib/grm/src/grm/plot.cxx index 94fff7894..a8c837b7e 100644 --- a/lib/grm/src/grm/plot.cxx +++ b/lib/grm/src/grm/plot.cxx @@ -3303,7 +3303,11 @@ err_t plot_pie(grm_args_t *subplot_args) } if (grm_args_values(subplot_args, "title", "s", &title)) { - group->parentElement()->setAttribute("title", title); + auto side_region = global_render->createElement("side_region"); + group->parentElement()->append(side_region); + side_region->setAttribute("text_content", title); + side_region->setAttribute("location", "top"); + side_region->setAttribute("text_is_title", true); } global_root->setAttribute("_id", id++); @@ -3595,23 +3599,46 @@ err_t plot_draw_axes(grm_args_t *args, unsigned int pass) if (pass == 1 && grm_args_values(args, "title", "s", &title)) { - if (strcmp(kind, "marginal_heatmap") == 0) + auto side_region = global_render->createElement("side_region"); + current_central_region_element->parentElement()->append(side_region); + side_region->setAttribute("text_content", title); + side_region->setAttribute("location", "top"); + side_region->setAttribute("text_is_title", true); + } + + if (grm_args_values(args, "x_label", "s", &x_label)) + { + if (type == "3d") { - group->parentElement()->parentElement()->parentElement()->setAttribute("title", title); + group->setAttribute("x_label", x_label); } else { - group->parentElement()->parentElement()->setAttribute("title", title); + if (!current_central_region_element->parentElement()->querySelectors("side_region[location=\"bottom\"]")) + { + auto side_region = global_render->createElement("side_region"); + current_central_region_element->parentElement()->append(side_region); + side_region->setAttribute("text_content", x_label); + side_region->setAttribute("location", "bottom"); + } } } - - if (grm_args_values(args, "x_label", "s", &x_label)) - { - group->setAttribute("x_label", x_label); - } if (grm_args_values(args, "y_label", "s", &y_label)) { - group->setAttribute("y_label", y_label); + if (type == "3d") + { + group->setAttribute("y_label", y_label); + } + else + { + if (!current_central_region_element->parentElement()->querySelectors("side_region[location=\"left\"]")) + { + auto side_region = global_render->createElement("side_region"); + current_central_region_element->parentElement()->append(side_region); + side_region->setAttribute("text_content", y_label); + side_region->setAttribute("location", "left"); + } + } } if (grm_args_values(args, "z_label", "s", &z_label)) { @@ -3662,8 +3689,16 @@ err_t plot_draw_polar_axes(grm_args_t *args) { subGroup->setAttribute("phi_flip", phi_flip); } - if (!grm_args_values(args, "title", "s", &title)) title = ""; - group->parentElement()->setAttribute("title", title); + + if (grm_args_values(args, "title", "s", &title)) + { + auto side_region = global_render->createElement("side_region"); + current_central_region_element->parentElement()->append(side_region); + side_region->setAttribute("text_content", title); + side_region->setAttribute("location", "top"); + side_region->setAttribute("text_is_title", true); + } + return ERROR_NONE; } @@ -3744,8 +3779,10 @@ err_t plot_draw_colorbar(grm_args_t *subplot_args, double off, unsigned int colo auto side_region = global_render->createElement("side_region"); group->append(side_region); + auto side_plot_region = global_render->createElement("side_plot_region"); + side_region->append(side_plot_region); auto colorbar = global_render->createColorbar(colors); - side_region->append(colorbar); + side_plot_region->append(colorbar); colorbar->setAttribute("x_flip", 0); colorbar->setAttribute("y_flip", 0); @@ -4109,12 +4146,14 @@ err_t classes_polar_histogram(grm_args_t *subplot_args) err_t error = ERROR_NONE; std::shared_ptr plot_group = edit_figure->lastChildElement(); - std::shared_ptr series_group = edit_figure->lastChildElement()->lastChildElement()->lastChildElement(); + std::shared_ptr series_group = (current_central_region_element) + ? current_central_region_element->lastChildElement() + : getCentralRegion()->lastChildElement(); std::shared_ptr context = global_render->getContext(); - int id = static_cast(global_root->getAttribute("_id")); + auto id = static_cast(global_root->getAttribute("_id")); global_root->setAttribute("_id", id++); auto str = std::to_string(id); @@ -4673,18 +4712,6 @@ int plot_process_subplot_args(grm_args_t *subplot_args) } auto central_region = getCentralRegion(); - if (grm_args_values(subplot_args, "y_label", "s", &y_label)) - { - central_region->setAttribute("y_label_margin", 1); - } - if (grm_args_values(subplot_args, "x_label", "s", &x_label)) - { - central_region->setAttribute("x_label_margin", 1); - } - if (grm_args_values(subplot_args, "title", "s", &title)) - { - group->setAttribute("title_margin", 1); - } if (grm_args_values(subplot_args, "keep_aspect_ratio", "i", &keep_aspect_ratio)) { group->setAttribute("keep_aspect_ratio", keep_aspect_ratio); From 2e8a56e3fafeb727b122d849efbfaf56d885ba3b Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Wed, 5 Jun 2024 08:55:14 +0200 Subject: [PATCH 08/24] GR: revise the 'axis' structure --- lib/gr/gr.c | 65 +++++++++++++++++++++++++++-------------------------- lib/gr/gr.h | 4 ++-- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 7343fd43d..8d13d7908 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -5313,7 +5313,6 @@ void gr_axis(char which, axis_t *axis) double wn[4], vp[4]; double x_min, x_max, y_min, y_max; int scale_option, base, decade, exponent; - double tick; double epsilon; int i, j, k; double a, a0, tbx[4], tby[4]; @@ -5337,33 +5336,35 @@ void gr_axis(char which, axis_t *axis) scale_option = GR_OPTION_X_LOG; base = lx.basex; if (scale_option & GR_OPTION_X_LOG == 0) gr_adjustrange(&x_min, &x_max); - if (is_nan(axis->axis_min)) axis->axis_min = x_min; - if (is_nan(axis->axis_max)) axis->axis_max = x_max; - if (is_nan(axis->axis_org)) axis->axis_org = y_min; + if (is_nan(axis->min)) axis->min = x_min; + if (is_nan(axis->max)) axis->max = x_max; + if (is_nan(axis->org)) axis->org = y_min; } else if (which == 'Y') { scale_option = GR_OPTION_Y_LOG; base = lx.basey; if (scale_option & GR_OPTION_Y_LOG == 0) gr_adjustrange(&y_min, &y_max); - if (is_nan(axis->axis_min)) axis->axis_min = y_min; - if (is_nan(axis->axis_max)) axis->axis_max = y_max; - if (is_nan(axis->axis_org)) axis->axis_org = x_min; + if (is_nan(axis->min)) axis->min = y_min; + if (is_nan(axis->max)) axis->max = y_max; + if (is_nan(axis->org)) axis->org = x_min; } + if (is_nan(axis->tick_size)) axis->tick_size = 0.0075; + if (scale_option & lx.scale_options) { - axis->num_tick_labels = igauss(blog(base, axis->axis_max / axis->axis_min)) + 2; + axis->num_tick_labels = igauss(blog(base, axis->max / axis->min)) + 2; axis->tick_label = (tick_label_t *)xcalloc(axis->num_tick_labels, sizeof(tick_label_t)); axis->num_ticks = axis->num_tick_labels * base; axis->ticks = (tick_t *)xcalloc(axis->num_ticks, sizeof(tick_t)); - a0 = pow(base, gauss(blog(base, axis->axis_min))); - i = ipred(axis->axis_min / a0); + a0 = pow(base, gauss(blog(base, axis->min))); + i = ipred(axis->min / a0); a = a0 + i * a0; - decade = igauss(blog(base, axis->axis_min / axis->axis_org)); + decade = igauss(blog(base, axis->min / axis->org)); j = k = 0; - while (a <= axis->axis_max) + while (a <= axis->max) { axis->ticks[j].value = a; axis->ticks[j++].is_major = (i == 0); @@ -5401,23 +5402,23 @@ void gr_axis(char which, axis_t *axis) } else { - tick = gr_tick(axis->axis_min, axis->axis_max); + if (is_nan(axis->tick)) axis->tick = gr_tick(axis->min, axis->max); if (axis->major_count == 0) axis->major_count = 1; - axis->num_ticks = (int)((axis->axis_max - axis->axis_min) / tick + 0.5) + 1; + axis->num_ticks = (int)((axis->max - axis->min) / axis->tick + 0.5) + 1; axis->ticks = (double *)xcalloc(axis->num_ticks, sizeof(tick_t)); axis->num_tick_labels = axis->num_ticks / axis->major_count; axis->tick_label = (tick_label_t *)xcalloc(axis->num_tick_labels, sizeof(tick_label_t)); - epsilon = FEPS * (axis->axis_max - axis->axis_min); + epsilon = FEPS * (axis->max - axis->min); - i = isucc(axis->axis_min / tick); - a = i * tick; + i = isucc(axis->min / axis->tick); + a = i * axis->tick; j = k = 0; - while (a <= axis->axis_max + epsilon) + while (a <= axis->max + epsilon) { axis->ticks[j].value = a; axis->ticks[j++].is_major = (i % axis->major_count == 0); - gr_getformat(&formatReference, axis->axis_min, a, axis->axis_max, tick, axis->major_count); + gr_getformat(&formatReference, axis->min, a, axis->max, axis->tick, axis->major_count); if (i % axis->major_count == 0) { s = (char *)xcalloc(256, sizeof(char)); @@ -5429,7 +5430,7 @@ void gr_axis(char which, axis_t *axis) k++; } i++; - a = i * tick; + a = i * axis->tick; } } } @@ -5453,22 +5454,22 @@ void gr_draw_axis(char which, axis_t *axis) if (which == 'X') { tick = axis->tick_size * (wn[3] - wn[2]) / (vp[3] - vp[2]); - minor_tick = y_log(y_lin(axis->axis_org) + tick); - major_tick = y_log(y_lin(axis->axis_org) + 2 * tick); - label_org = y_log(y_lin(axis->axis_org) - tick); - pline(axis->axis_min, axis->axis_org); - pline(axis->axis_max, axis->axis_org); + minor_tick = y_log(y_lin(axis->org) + tick); + major_tick = y_log(y_lin(axis->org) + 2 * tick); + label_org = y_log(y_lin(axis->org) - tick); + pline(axis->min, axis->org); + pline(axis->max, axis->org); end_pline(); gks_set_text_align(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_TOP); } else { tick = axis->tick_size * (wn[1] - wn[0]) / (vp[1] - vp[0]); - minor_tick = x_log(x_lin(axis->axis_org) + tick); - major_tick = x_log(x_lin(axis->axis_org) + 2 * tick); - label_org = x_log(x_lin(axis->axis_org) - tick); - pline(axis->axis_org, axis->axis_min); - pline(axis->axis_org, axis->axis_max); + minor_tick = x_log(x_lin(axis->org) + tick); + major_tick = x_log(x_lin(axis->org) + 2 * tick); + label_org = x_log(x_lin(axis->org) - tick); + pline(axis->org, axis->min); + pline(axis->org, axis->max); end_pline(); gks_set_text_align(GKS_K_TEXT_HALIGN_RIGHT, GKS_K_TEXT_VALIGN_HALF); } @@ -5478,13 +5479,13 @@ void gr_draw_axis(char which, axis_t *axis) tick = axis->ticks[i].is_major ? major_tick : minor_tick; if (which == 'X') { - pline(axis->ticks[i].value, axis->axis_org); + pline(axis->ticks[i].value, axis->org); pline(axis->ticks[i].value, tick); end_pline(); } else { - pline(axis->axis_org, axis->ticks[i].value); + pline(axis->org, axis->ticks[i].value); pline(tick, axis->ticks[i].value); end_pline(); } diff --git a/lib/gr/gr.h b/lib/gr/gr.h index d8f20e491..eee42c595 100644 --- a/lib/gr/gr.h +++ b/lib/gr/gr.h @@ -195,8 +195,8 @@ typedef struct typedef struct { - int dry_run; - double axis_min, axis_max, axis_org; + double min, max; + double tick, org; int major_count; int num_ticks; tick_t *ticks; From 9dab728153776e632a9854a656d7bfbf120e6b5c Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Wed, 5 Jun 2024 12:39:24 +0200 Subject: [PATCH 09/24] GR: did some cosmetic changes --- lib/gr/gr.c | 28 ++++++++++++++-------------- lib/gr/gr.h | 3 +-- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 8d13d7908..2a2c3ac1e 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -5355,7 +5355,7 @@ void gr_axis(char which, axis_t *axis) if (scale_option & lx.scale_options) { axis->num_tick_labels = igauss(blog(base, axis->max / axis->min)) + 2; - axis->tick_label = (tick_label_t *)xcalloc(axis->num_tick_labels, sizeof(tick_label_t)); + axis->tick_labels = (tick_label_t *)xcalloc(axis->num_tick_labels, sizeof(tick_label_t)); axis->num_ticks = axis->num_tick_labels * base; axis->ticks = (tick_t *)xcalloc(axis->num_ticks, sizeof(tick_t)); @@ -5377,10 +5377,10 @@ void gr_axis(char which, axis_t *axis) exponent = iround(blog(base, a)); s = (char *)xcalloc(256, sizeof(char)); snprintf(s, 255, "%d^{%d}", base, exponent); - axis->tick_label[k].tick = a; - axis->tick_label[k].label = replace_minus_sign(s); - gr_inqtext(0, 0, axis->tick_label[k].label, tbx, tby); - axis->tick_label[k].width = tbx[2] - tbx[0]; + axis->tick_labels[k].tick = a; + axis->tick_labels[k].label = replace_minus_sign(s); + gr_inqtext(0, 0, axis->tick_labels[k].label, tbx, tby); + axis->tick_labels[k].width = tbx[2] - tbx[0]; k++; } } @@ -5407,7 +5407,7 @@ void gr_axis(char which, axis_t *axis) axis->num_ticks = (int)((axis->max - axis->min) / axis->tick + 0.5) + 1; axis->ticks = (double *)xcalloc(axis->num_ticks, sizeof(tick_t)); axis->num_tick_labels = axis->num_ticks / axis->major_count; - axis->tick_label = (tick_label_t *)xcalloc(axis->num_tick_labels, sizeof(tick_label_t)); + axis->tick_labels = (tick_label_t *)xcalloc(axis->num_tick_labels, sizeof(tick_label_t)); epsilon = FEPS * (axis->max - axis->min); @@ -5423,10 +5423,10 @@ void gr_axis(char which, axis_t *axis) { s = (char *)xcalloc(256, sizeof(char)); gr_ftoa(s, a, &formatReference); - axis->tick_label[k].tick = a; - axis->tick_label[k].label = replace_minus_sign(s); - gr_inqtext(0, 0, axis->tick_label[k].label, tbx, tby); - axis->tick_label[k].width = tbx[2] - tbx[0]; + axis->tick_labels[k].tick = a; + axis->tick_labels[k].label = replace_minus_sign(s); + gr_inqtext(0, 0, axis->tick_labels[k].label, tbx, tby); + axis->tick_labels[k].width = tbx[2] - tbx[0]; k++; } i++; @@ -5494,9 +5494,9 @@ void gr_draw_axis(char which, axis_t *axis) for (i = 0; i < axis->num_tick_labels; i++) { if (which == 'X') - text2d(axis->tick_label[i].tick, label_org, axis->tick_label[i].label); + text2d(axis->tick_labels[i].tick, label_org, axis->tick_labels[i].label); else - text2d(label_org, axis->tick_label[i].tick, axis->tick_label[i].label); + text2d(label_org, axis->tick_labels[i].tick, axis->tick_labels[i].label); } } @@ -5505,9 +5505,9 @@ void gr_free_axis(axis_t *axis) int i; for (i = 0; i < axis->num_tick_labels; i++) { - free(axis->tick_label[i].label); + free(axis->tick_labels[i].label); } - free(axis->tick_label); + free(axis->tick_labels); free(axis->ticks); } diff --git a/lib/gr/gr.h b/lib/gr/gr.h index eee42c595..e1f74f12c 100644 --- a/lib/gr/gr.h +++ b/lib/gr/gr.h @@ -201,9 +201,8 @@ typedef struct int num_ticks; tick_t *ticks; int num_tick_labels; - tick_label_t *tick_label; + tick_label_t *tick_labels; double tick_size; - int draw_grid_line; } axis_t; DLLEXPORT void gr_initgr(void); From b7877b530383e6924d7cdafed1432d5997f8392a Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Wed, 5 Jun 2024 14:37:21 +0200 Subject: [PATCH 10/24] GR: stick to the GR naming scheme --- lib/gr/gr.c | 4 ++-- lib/gr/gr.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 2a2c3ac1e..ecb044f3a 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -5435,7 +5435,7 @@ void gr_axis(char which, axis_t *axis) } } -void gr_draw_axis(char which, axis_t *axis) +void gr_drawaxis(char which, axis_t *axis) { int errind, tnr; double wn[4], vp[4]; @@ -5500,7 +5500,7 @@ void gr_draw_axis(char which, axis_t *axis) } } -void gr_free_axis(axis_t *axis) +void gr_freeaxis(axis_t *axis) { int i; for (i = 0; i < axis->num_tick_labels; i++) diff --git a/lib/gr/gr.h b/lib/gr/gr.h index e1f74f12c..fd6b989af 100644 --- a/lib/gr/gr.h +++ b/lib/gr/gr.h @@ -291,8 +291,8 @@ DLLEXPORT void gr_axeslbl(double, double, double, double, int, int, double, void (*)(double, double, const char *, double), void (*)(double, double, const char *, double)); DLLEXPORT void gr_axis(char, axis_t *); -DLLEXPORT void gr_draw_axis(char, axis_t *); -DLLEXPORT void gr_free_axis(axis_t *); +DLLEXPORT void gr_drawaxis(char, axis_t *); +DLLEXPORT void gr_freeaxis(axis_t *); DLLEXPORT void gr_grid(double, double, double, double, int, int); DLLEXPORT void gr_grid3d(double, double, double, double, double, double, int, int, int); DLLEXPORT void gr_verrorbars(int, double *, double *, double *, double *); From 167ec0fa41e45a310de80bbed3ee31e91629f41b Mon Sep 17 00:00:00 2001 From: Verbov Date: Tue, 4 Jun 2024 10:47:48 +0200 Subject: [PATCH 11/24] small cleanup for part of errorbars code, added support for xyz-files, enable more flexible viewport calculation for side_plot_region, added options for the user to specify which columns are for x,y and the errors; updated README, added an attribute for errorbars which respect equal up and down error --- lib/grm/grplot/README.md | 21 +- lib/grm/src/grm/dom_render/render.cxx | 124 ++++--- lib/grm/src/grm/import.cxx | 457 +++++++++++++++++++++----- lib/grm/src/grm/import_int.hxx | 8 +- lib/grm/src/grm/plot.cxx | 28 +- 5 files changed, 500 insertions(+), 138 deletions(-) diff --git a/lib/grm/grplot/README.md b/lib/grm/grplot/README.md index ee023670e..01611052a 100644 --- a/lib/grm/grplot/README.md +++ b/lib/grm/grplot/README.md @@ -28,6 +28,13 @@ For plots where multiple columns are read there is also a parameter that allows - `columns`: define the columns of the file which should be respected in the plot. The default is all columns. If all columns from x to y should be drawn use `x:y`. The `y` is necessary even when all lines from `x` to the end should be drawn. To select more than 1 specific column use the `,` without whitespace as separator. +For one-dimensional data sets there are also options to define if inside the data-file there are not just y- and maybe error-values included. These parameters are similar to the previous `column` parameter and can also be used in combination with it. + +- `x_columns`: define the columns of the file which should represent the x-data. This is useful if multiple lines are included inside the plot where their y-values are not always on the same x-points. Another use case would be non-linear x-values which can not automatically generated by 'GR Plot'. +- `y_columns`: define the columns of the file which should represent the y-data. This parameter is only useful if either `x_columns` or `error_columns` is also used. Any column not included by those two is interpreted as y-values. +- `error_columns`: define the columns of the file which should represent the error-data. Depending on the status of the flag parameter `equal_up_and_down_error` this parameter must include either one or two columns for each y-column which should have error bars later. If this parameter is used and includes enough columns it is not needed to set `error` to enable the error bars. It can still be set to edit the resulting error bars. +- `xye_file`: defines a special case of the data-file. In this case the first column represents the x-, the second the y- and the third and maybe fourth column the error-values. The parameter can only have the values 0 and 1. If this parameter has the value 1 it is not needed to set `error` to enable the error bars. It can still be set to edit the resulting error bars. + There are more key-value parameters. These parameters only effect specific plot types. For example `bar_width` only makes sense, when bars are drawn. All possible parameters are: `accelerate`, `algorithm`, `bar_color`, `bar_width`, `bin_counts`, `bin_edges`, `c`, `colormap`, `draw_edges`, `edge_color`, `edge_width`, `grplot`, `int_lim`, `isovalue`, `kind`, `levels`, `line_spec`, `major_h`, `marginal_heatmap_kind`, `marker_type`, `num_bins`, `normalization`, `orientation`, `phi_flip`, `rotation`, `scatter_z`, `stairs`, `step_where`, `style`, `tilt`, `transformation`, `x_bins`, `x_colormap`, `x_flip`, `y_bins`, `y_colormap`, `y_flip`, `y_labels` @@ -102,11 +109,11 @@ Values are seperated through commas (`,`), e.g. `3, 5`. The next line after the header may contain the column labels. If the data does not have a label a blank line can be used instead. Data items are separated with a tab character (`\t`). Depending on the plot type the data is interpreted differently. The following list shows how the data is treated for different plot types. -1. `contour`, `contourf`, `heatmap`, `imshow`, `marginal_heatmap`, `surface`, `wireframe`: The expected data is a matrix. Each element of the matrix is displayed according to its position inside the matrix. These elements are interpreted as values in x- and y-direction. With the parameter `use_bins:1` the first row and column of the data is interpreted as information about the `x_range` and `y_range`. -2. `line`, `scatter`: One or more columns are expected here. Each column will be displayed in a single plot. The values inside the columns gets therefore interpreted as y-values. +1. `contour`, `contourf`, `heatmap`, `imshow`, `marginal_heatmap`, `surface`, `wireframe`: The expected data is a matrix. Each element of the matrix is displayed according to its position inside the matrix. These elements are interpreted as values in x- and y-direction. With the parameter `use_bins:1` the first row and column of the data is interpreted as information about the `x_range` and `y_range`. If the x- or y-values are non-linear this can not be handled by the previous explained data-format. For that there is another format which allows to define the x-, y- and z-values. Each values will stand in a different column while the x- and y-values will loop so that all columns have the same amount of rows. The x-values has to change first. To use this option the parameter flag `xyz_file` with the value of `1` can be used. +2. `line`, `scatter`: One or more columns are expected here. Each column will be displayed in a single plot. The values inside the columns gets therefore interpreted as y-values. In combination with the `error` parameter every 2nd (and 3rd) column gets interpreted as error-values. More information are found by the `error` parameter itself. There is also another option which allows the user to define which columns include the x-, y- and error-values. For this the parameters `x_columns`, `y_columns` and `error_columns` can be used. 3. `isosurface`, `volume`: The expected data are multiple matrices. Each matrix represents a slice inside the volume. 4. `plot3`, `scatter`, `scatter3`, `tricontour`, `trisurface`: Three columns with data are expected, representing the x-, y- and z-data. -5. `barplot`, `hist`, `stem`, `stairs`: One column is expected which represents the y-data. +5. `barplot`, `hist`, `stem`, `stairs`: One column is expected which represents the y-data. In combination with the `error` parameter the 2nd (and 3rd) column gets interpreted as error-values. More information are found by the `error` parameter itself. There is also another option which allows the user to define which column include the x-, y- and error-values. For this the parameters `x_columns`, `y_columns` and `error_columns` can be used. For these kinds only one column for x and y is expected. 6. `pie`: The expected data are 1-4 lines. The first line represents the data which should be displayed. The next 3 rows are used to set the RGB of the pie charts. Each row stands for one RGB element. 7. `polar_histogram`: One column is expected which represents the values. 8. `polar`: The expected data are two columns containing the angles and values. @@ -139,7 +146,7 @@ Possible parameters for the bar plot are: - `downward_scap_color`: Defines the downward scap color of the error bars as an integer. - `upward_scap_color`: Defines the upward scap color of the error bars as an integer. - Note: If the error of the bars is to be displayed, the last two columns of the data are used for the error. The syntax of this parameter is: + Note: If the error of the bars is to be displayed, the last two columns of the data are used for the error. If the up and down error are equal use `equal_up_and_down_error` with a value of `1`. In that case only one column is expected for the error data instead of the two. This parameter flag also does not need the `error` parameter to be set. Like by the `error` parameter some of the columns inside the data will get interpreted as error-values. The syntax of this parameter is: `error:{{error_bar_color:`color_index`},{downward_scap_color:`color_index`},{upward_scap_color:`color_index`}}` @@ -237,7 +244,7 @@ Possible parameters for the histogram are: - `downward_scap_color`: Defines the downward scap color of the error bars. - `upward_scap_color`: Defines the upward scap color of the error bars. - Note: If the error of the bars is to be displayed, the last two columns of the data are used for the error. The syntax of this parameter is: + Note: If the error of the bars is to be displayed, the last two columns of the data are used for the error. If the up and down error are equal use `equal_up_and_down_error` with a value of `1`. In that case only one column is expected for the error data instead of the two. This parameter flag also does not need the `error` parameter to be set. Like by the `error` parameter some of the columns inside the data will get interpreted as error-values. The syntax of this parameter is: `error:{{error_bar_color:`color_index`},{downward_scap_color:`color_index`},{upward_scap_color:`color_index`}}` 5. `orientation`: This parameter defines the orientation of the displayed bars. They can either be drawn `horizontal` or `vertical` while the default is `horizontal`. @@ -280,7 +287,7 @@ Possible parameters for the line plot are: - `downward_scap_color`: Defines the downward scap color of the error bars. The value of this parameter has to be an integer. - `upward_scap_color`: Defines the upward scap color of the error bars. The value of this parameter has to be an integer. - Note: If the error of the points is to be displayed, the last two columns of each pair of three columns from the data are used for the error. This means for a dataset with eight columns that columns 2, 3, 5, 6 are used for the errorbars. The syntax of this parameter is: + Note: If the error of the points is to be displayed, the last two columns of each pair of three columns from the data are used for the error. This means for a dataset with eight columns that columns 2, 3, 5, 6 are used for the error bars. If the up and down error are equal use `equal_up_and_down_error` with a value of `1`. In that case only one column is expected for the error data instead of the two. This parameter flag also does not need the `error` parameter to be set. Like by the `error` parameter some of the columns inside the data will get interpreted as error-values. The syntax of this parameter is: `error:{{error_bar_color:`color_index`},{downward_scap_color:`color_index`},{upward_scap_color:`color_index`}}` @@ -376,7 +383,7 @@ Possible parameters for the scatter plot are: - `downward_scap_color`: Defines the downward scap color of the error bars. The value of this parameter has to be an integer. - `upward_scap_color`: Defines the upward scap color of the error bars. The value of this parameter has to be an integer. - Note: If the error of the points is to be displayed, the last two columns of the data are used for the error. The syntax of this parameter is: + Note: If the error of the points is to be displayed, the last two columns of the data are used for the error. If the up and down error are equal use `equal_up_and_down_error` with a value of `1`. In that case only one column is expected for the error data instead of the two. This parameter flag also does not need the `error` parameter to be set. Like by the `error` parameter some of the columns inside the data will get interpreted as error-values. The syntax of this parameter is: `error:{{error_bar_color:`color_index`},{downward_scap_color:`color_index`},{upward_scap_color:`color_index`}}` diff --git a/lib/grm/src/grm/dom_render/render.cxx b/lib/grm/src/grm/dom_render/render.cxx index 9ecf91922..45598ff0e 100644 --- a/lib/grm/src/grm/dom_render/render.cxx +++ b/lib/grm/src/grm/dom_render/render.cxx @@ -722,6 +722,44 @@ 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) +{ + if (side_region->querySelectors("side_plot_region") || + (side_region->hasAttribute("marginal_heatmap_side_plot") && + static_cast(side_region->getAttribute("marginal_heatmap_side_plot")))) + { + *margin += inc; + if (aspect_ratio_scale) + { + if (aspect_ratio_ws > start_aspect_ratio_ws) + { + *margin /= (start_aspect_ratio_ws / aspect_ratio_ws); + } + else + { + if (aspect_ratio_ws < 1) *margin /= aspect_ratio_ws; + } + } + } +} + +static void capSidePlotMarginInNonKeepAspectRatio(const std::shared_ptr side_region, double *margin, + std::string kind) +{ + if (side_region->querySelectors("side_plot_region")) + { + if (str_equals_any(kind, "surface", "volume", "trisurface")) + { + *margin = grm_max(0.12, *margin); + } + else + { + *margin = grm_max(0.075, *margin); + } + } +} + static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptr element, double *vp_x_min, double *vp_x_max, double *vp_y_min, double *vp_y_max, bool diag_factor = false) @@ -823,31 +861,37 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptr start_aspect_ratio_ws) - { - right_margin /= (start_aspect_ratio_ws / aspect_ratio_ws); - } - else - { - if (aspect_ratio_ws < 1) right_margin /= aspect_ratio_ws; - } + // TODO: Overwork this condition and max value workaround + capSidePlotMarginInNonKeepAspectRatio(left_side_region, &left_margin, kind); + capSidePlotMarginInNonKeepAspectRatio(right_side_region, &right_margin, kind); + capSidePlotMarginInNonKeepAspectRatio(bottom_side_region, &bottom_margin, kind); + capSidePlotMarginInNonKeepAspectRatio(top_side_region, &top_margin, kind); } + // margin respects text in the specific side_region + if (left_text_margin) left_margin = 0.05; + if (right_text_margin) right_margin = 0.05; + if (bottom_text_margin) bottom_margin = 0.05; + + // calculate text impact for top_margin and adjust all margins if defined by attributes if (kind == "marginal_heatmap") { - top_margin = right_margin + (top_text_margin ? top_text_is_title ? 0.075 : 0.05 : 0.025); + top_margin += (right_margin - top_margin) + (top_text_margin ? top_text_is_title ? 0.075 : 0.05 : 0.025); if (keep_aspect_ratio && uniform_data && only_quadratic_aspect_ratio) { @@ -890,21 +934,6 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptr &element) { if (location == "top") { - width += (0.025 + ((element->parentElement()->hasAttribute("text_is_title") && - static_cast(element->parentElement()->getAttribute("text_is_title"))) - ? 0.075 - : 0.05)) * - (plot_viewport[3] - plot_viewport[2]); + if (!element->parentElement()->hasAttribute("marginal_heatmap_side_plot")) + { + width += (0.025 + ((element->parentElement()->hasAttribute("text_is_title") && + static_cast(element->parentElement()->getAttribute("text_is_title"))) + ? 0.075 + : 0.05)) * + (plot_viewport[3] - plot_viewport[2]); + } if (element->parentElement()->hasAttribute("offset")) offset = static_cast(element->parentElement()->getAttribute("offset")); if (element->parentElement()->hasAttribute("width")) @@ -3906,6 +3938,7 @@ static void processMarginalHeatmapKind(const std::shared_ptr &elem for (const auto &side_region : element->children()) { if (!side_region->hasAttribute("marginal_heatmap_side_plot") || + !side_region->querySelectors("side_plot_region") || static_cast(element->getAttribute("_delete_children")) >= 2) continue; if (mkind == "line") @@ -6511,7 +6544,7 @@ static void processErrorBars(const std::shared_ptr &element, const std::vector absolute_upwards_vec, absolute_downwards_vec, relative_upwards_vec, relative_downwards_vec; std::string absolute_upwards, absolute_downwards, relative_upwards, relative_downwards; double absolute_upwards_flt, relative_upwards_flt, absolute_downwards_flt, relative_downwards_flt; - unsigned int upwards_length, downwards_length, i; + unsigned int i; int scale_options, color_upward_scap, color_downward_scap, color_error_bar; double marker_size, x_min, x_max, y_min, y_max, tick, a, b, e_upwards, e_downwards, x_value; double line_x[2], line_y[2]; @@ -6558,25 +6591,21 @@ static void processErrorBars(const std::shared_ptr &element, const { absolute_downwards = static_cast(element->getAttribute("absolute_downwards")); absolute_downwards_vec = GRM::get>((*context)[absolute_downwards]); - downwards_length = absolute_downwards_vec.size(); } if (element->hasAttribute("relative_downwards")) { relative_downwards = static_cast(element->getAttribute("relative_downwards")); relative_downwards_vec = GRM::get>((*context)[relative_downwards]); - downwards_length = absolute_downwards_vec.size(); } if (element->hasAttribute("absolute_upwards")) { absolute_upwards = static_cast(element->getAttribute("absolute_upwards")); absolute_upwards_vec = GRM::get>((*context)[absolute_upwards]); - upwards_length = absolute_upwards_vec.size(); } if (element->hasAttribute("relative_upwards")) { relative_upwards = static_cast(element->getAttribute("relative_upwards")); relative_upwards_vec = GRM::get>((*context)[relative_upwards]); - upwards_length = absolute_upwards_vec.size(); } if (element->hasAttribute("absolute_downwards_flt")) absolute_downwards_flt = static_cast(element->getAttribute("absolute_downwards_flt")); @@ -6703,7 +6732,7 @@ static void processErrorBar(const std::shared_ptr &element, const { double scap_x_min, scap_x_max, e_upwards = FLT_MAX, e_downwards = FLT_MAX; double error_bar_x, error_bar_y_min, error_bar_y_max; - int color_upward_scap, color_downward_scap, color_error_bar; + int color_upward_scap = 0, color_downward_scap = 0, color_error_bar; std::shared_ptr line; del_values del = del_values::update_without_default; int child_id = 0; @@ -17495,6 +17524,11 @@ void updateFilter(const std::shared_ptr &element, const std::strin // create marginal_heatmap_plot as central_region father central_region->parentElement()->insertBefore(new_series, central_region); new_series->append(central_region); + // declare which side_region contains the marginal_heatmap side_plot + new_series->querySelectors("side_region[location=\"top\"]") + ->setAttribute("marginal_heatmap_side_plot", 1); + new_series->querySelectors("side_region[location=\"right\"]") + ->setAttribute("marginal_heatmap_side_plot", 1); } else { diff --git a/lib/grm/src/grm/import.cxx b/lib/grm/src/grm/import.cxx index d64085655..eab9d4456 100644 --- a/lib/grm/src/grm/import.cxx +++ b/lib/grm/src/grm/import.cxx @@ -32,6 +32,7 @@ static std::map key_to_types{ {"edge_color", "ddd"}, {"edge_color", "i"}, {"edge_width", "d"}, + {"equal_up_and_down_error", "i"}, {"error", "a"}, {"grplot", "i"}, {"ind_bar_color", "nA"}, @@ -75,6 +76,8 @@ static std::map key_to_types{ {"x_log", "i"}, {"x_range", "dd"}, {"x_tick_labels", "nS"}, + {"xye_file", "i"}, + {"xyz_file", "i"}, {"y_bins", "i"}, {"y_colormap", "i"}, {"y_flip", "i"}, @@ -119,10 +122,13 @@ static std::map container_to_types{{"downward_scap_co {"upward_scap_color", "i"}, {"width", "d"}}; -/* ~~~~~~~~~~~~~~~~~~~~~~~~~ scatter interpretation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +/* ~~~~~~~~~~~~~~~~~~~~~~~~~ global flags defined by the user input ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ static int scatter_with_z = 0; static int use_bins = 0; +static int equal_up_and_down_error = 0; +static int xye_file = 0; +static int xyz_file = 0; /* ========================= functions ============================================================================== */ @@ -149,19 +155,9 @@ std::string normalize_line(const std::string &str) return s; } -err_t read_data_file(const std::string &path, std::vector>> &data, - std::vector &labels, grm_args_t *args, const char *colms, PlotRange *ranges) +err_t parse_columns(std::list *columns, const char *colms) { - std::string line; std::string token; - std::ifstream file_path(path); - std::istream &cin_path = std::cin; - std::list columns; - bool depth_change = true; - int depth = 0, max_col = -1; - int linecount = 0; - - /* read the columns from the colms string also converts slicing into numbers */ std::stringstream scol(colms); while (std::getline(scol, token, ',') && token.length()) { @@ -205,14 +201,14 @@ err_t read_data_file(const std::string &path, std::vectorymin = *columns.begin(); - } + if (!(*columns).empty()) (*columns).sort(); + return ERROR_NONE; +} + +err_t read_data_file(const std::string &path, std::vector>> &data, + std::vector &x_data, std::vector &error_data, std::vector &labels, + grm_args_t *args, const char *colms, const char *x_colms, const char *y_colms, const char *e_colms, + PlotRange *ranges) +{ + std::string line; + std::string token; + std::ifstream file_path(path); + std::istream &cin_path = std::cin; + std::list columns, x_columns, y_columns, e_columns; + bool depth_change = true; + int depth = 0, max_col = -1; + int linecount = 0; + err_t error = ERROR_NONE; + + /* read the columns from the colms string also converts slicing into numbers */ + if ((error = parse_columns(&columns, colms)) != ERROR_NONE) return error; + if (!columns.empty()) ranges->ymin = *columns.begin(); + /* read the columns from the x_colms, y_colms and e_colms string also converts slicing into numbers */ + if ((error = parse_columns(&x_columns, x_colms)) != ERROR_NONE) return error; + if ((error = parse_columns(&y_columns, y_colms)) != ERROR_NONE) return error; + if ((error = parse_columns(&e_columns, e_colms)) != ERROR_NONE) return error; std::istream &file = (path == "-") ? cin_path : file_path; /* read the lines from the file */ @@ -440,6 +457,12 @@ err_t read_data_file(const std::string &path, std::vectorfile_path = ""; args->file_columns = ""; + args->file_x_columns = ""; + args->file_y_columns = ""; + args->file_error_columns = ""; return args; } @@ -540,6 +566,7 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) std::string s; size_t row, col, rows, cols, depth; std::vector>> file_data; + std::vector x_data, error_data; std::vector labels; std::vector labels_c; std::vector series; @@ -561,14 +588,39 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) fprintf(stderr, "File not found (%s)\n", file_args->file_path.c_str()); return 0; } - if (read_data_file(file_args->file_path, file_data, labels, args, file_args->file_columns.c_str(), &ranges)) + + grm_args_values(args, "kind", "s", &kind); + if (!str_equals_any(kind, "barplot", "hist", "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, 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; } + if (!file_data.empty()) { depth = file_data.size(); cols = file_data[0].size(); + if (str_equals_any(kind, "barplot", "hist", "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; } @@ -594,7 +646,6 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) } } - grm_args_values(args, "kind", "s", &kind); if ((strcmp(kind, "line") == 0 || (strcmp(kind, "scatter") == 0 && !scatter_with_z)) && ((rows >= 50 && cols >= 50) || (use_bins && rows >= 49 && cols >= 49))) { @@ -611,18 +662,42 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) if (!str_equals_any(kind, "contour", "contourf", "heatmap", "imshow", "marginal_heatmap", "surface", "wireframe")) { - use_bins = 0; // this parameter is only for surface and similar types + // 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")) { - std::vector xi(cols), yi(rows), zi(rows * cols); - - if (cols <= 1) + int x_dim = cols, y_dim = rows, z_dim = rows * cols; + if (cols <= 1 || (xyz_file && cols < 3)) { fprintf(stderr, "Insufficient data for plot type (%s)\n", kind); 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 (use_bins) { try @@ -636,20 +711,39 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) labels[0].c_str(), labels[cols - 1].c_str()); } } - adjust_ranges(&ranges.xmin, &ranges.xmax, 0.0, (double)cols - 1.0); - adjust_ranges(&ranges.ymin, &ranges.ymax, 0.0, (double)rows - 1.0); + adjust_ranges(&ranges.xmin, &ranges.xmax, 0.0, (double)x_dim - 1.0); + adjust_ranges(&ranges.ymin, &ranges.ymax, 0.0, (double)y_dim - 1.0); ranges.ymax = (ranges.ymax <= ranges.ymin) ? ranges.ymax + ranges.ymin : ranges.ymax; - for (col = 0; col < cols; ++col) + if (xyz_file) { - xi[col] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)col / ((double)cols - 1)); for (row = 0; row < rows; ++row) { - if (col == 0) + if (row < x_dim) { - yi[row] = ranges.ymin + (ranges.ymax - ranges.ymin) * ((double)row / ((double)rows - 1)); + 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 + { + 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 (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]; } } @@ -659,47 +753,78 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) 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 < rows * cols; ++elem) + for (elem = 0; elem < z_dim; ++elem) { zi[elem] = ranges.zmin + (ranges.zmax - ranges.zmin) * (zi[elem] - min_val) / (max_val - min_val); } } /* for imshow plot */ - grm_args_push(args, "c", "nD", rows * cols, zi.data()); - grm_args_push(args, "c_dims", "ii", cols, rows); + 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", cols, xi.data()); - grm_args_push(args, "y", "nD", rows, yi.data()); - grm_args_push(args, "z", "nD", rows * cols, zi.data()); + 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; + grm_args_t *error = nullptr; std::vector error_vec; std::vector x(rows); - int err = 0; + std::vector filtered_error_columns; + int err = 0, col_group_elem = 3, down_err_off = 2; const char *spec; adjust_ranges(&ranges.xmin, &ranges.xmax, 0.0, (double)rows - 1.0); + cols += x_data.size() + error_data.size(); - for (row = 0; row < rows; row++) + // calculate x-data if x_data is empty which means no x given + if (x_data.empty()) + { + for (row = 0; row < rows; row++) + { + x[row] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)row / ((double)rows - 1)); + } + } + // 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) { - x[row] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)row / ((double)rows - 1)); + 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; } + // 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) { double min_val = INFINITY, max_val = -INFINITY; for (col = 0; col < cols - err; col++) { min_val = std::min( - min_val, *std::min_element(std::begin(file_data[depth][col]), std::end(file_data[depth][col]))); + 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]), std::end(file_data[depth][col]))); + 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) * @@ -709,6 +834,7 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) } } + // calculate the error data if (grm_args_values(args, "error", "a", &error)) { int i; @@ -716,45 +842,126 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) std::vector errors_up(rows); std::vector errors_down(rows); - if (cols < 3) + 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 { - err = floor(cols / 3); - error_vec.resize(err); - for (col = 0; col < err; col++) + 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, "downward_scap_color", "i", &color_down)) + grm_args_push(error_vec[col], "downward_scap_color", "i", color_down); + if (grm_args_values(error, "upward_scap_color", "i", &color_up)) + grm_args_push(error_vec[col], "upward_scap_color", "i", color_up); + } + err *= down_err_off; + } + else { - error_vec[col] = grm_args_new(); - for (i = 0; i < rows; i++) + int cnt = 0; + err = 0; + error_vec.resize(equal_up_and_down_error ? error_data.size() : error_data.size() / 2); + for (int error_col : error_data) { - errors_up[i] = file_data[depth][col + 1 + col * 2][i]; - errors_down[i] = file_data[depth][col + 2 + col * 2][i]; + error_vec[cnt] = grm_args_new(); + 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[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); + } + if (grm_args_values(error, "error_bar_color", "i", &color)) + grm_args_push(error_vec[cnt], "error_bar_color", "i", color); + if (grm_args_values(error, "downward_scap_color", "i", &color_down)) + grm_args_push(error_vec[cnt], "downward_scap_color", "i", color_down); + if (grm_args_values(error, "upward_scap_color", "i", &color_up)) + grm_args_push(error_vec[cnt], "upward_scap_color", "i", color_up); + cnt += 1; } - 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, "downward_scap_color", "i", &color_down)) - grm_args_push(error_vec[col], "downward_scap_color", "i", color_down); - if (grm_args_values(error, "upward_scap_color", "i", &color_up)) - grm_args_push(error_vec[col], "upward_scap_color", "i", color_up); } - err *= 2; + } + } + int y_cnt = 0, x_cnt = 0, err_cnt = 0; + if (!x_data.empty() || !error_data.empty()) + { + for (col = 0; col < cols - x_data.size() - error_data.size(); col++) + { + series[col] = grm_args_new(); } } for (col = 0; col < cols - err; col++) { - series[col] = grm_args_new(); - grm_args_push(series[col], "x", "nD", rows, x.data()); - grm_args_push(series[col], "y", "nD", rows, file_data[depth][col + ((col < err / 2) ? col * 2 : err)].data()); - if (!labels.empty()) + if (x_data.empty() && error_data.empty()) { - labels_c.push_back(labels[col].c_str()); + series[col] = grm_args_new(); + 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) grm_args_push(series[col], "error", "a", error_vec[col]); + if (!labels.empty()) + { + 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); + } + else + { + if (std::find(x_data.begin(), x_data.end(), col) != x_data.end()) + { + grm_args_push(series[x_cnt], "x", "nD", rows, file_data[depth][col].data()); + x_cnt += 1; + } + else if (std::find(error_data.begin(), error_data.end(), col) == error_data.end()) + { + grm_args_push(series[y_cnt], "y", "nD", rows, file_data[depth][col].data()); + if (!labels.empty()) labels_c.push_back(labels[col].c_str()); + if (grm_args_values(args, "line_spec", "s", &spec)) + grm_args_push(series[y_cnt], "line_spec", "s", spec); + y_cnt += 1; + } + else if (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; + } } - if (col < err / 2) grm_args_push(series[col], "error", "a", error_vec[col]); - if (grm_args_values(args, "line_spec", "s", &spec)) grm_args_push(series[col], "line_spec", "s", spec); } + cols -= x_data.size() + error_data.size(); grm_args_push(args, "series", "nA", cols - err, series.data()); if (!labels_c.empty()) { @@ -834,7 +1041,8 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) { std::vector x(rows); double xmin, xmax, ymin, ymax; - grm_args_t *error; + int y_ind = 0; + grm_args_t *error = nullptr; if (strcmp(kind, "barplot") == 0) { @@ -844,26 +1052,63 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) { adjust_ranges(&ranges.xmin, &ranges.xmax, 0.0, (double)rows - 1.0); } - for (row = 0; row < rows; row++) + if (x_data.empty()) + { + for (row = 0; row < rows; row++) + { + x[row] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)row / ((double)rows - 1)); + } + } + else + { + x = file_data[depth][x_data[0]]; + if (!grm_args_values(args, "x_range", "dd", &xmin, &xmax)) + { + xmin = *std::min_element(std::begin(file_data[depth][x_data[0]]), std::end(file_data[depth][x_data[0]])); + xmax = *std::max_element(std::begin(file_data[depth][x_data[0]]), std::end(file_data[depth][x_data[0]])); + adjust_ranges(&ranges.xmin, &ranges.xmax, xmin, xmax); + grm_args_push(args, "x_range", "dd", ranges.xmin, ranges.xmax); + } + else + { + /* apply y_range to the data */ + xmin = *std::min_element(std::begin(file_data[depth][x_data[0]]), std::end(file_data[depth][x_data[0]])); + xmax = *std::max_element(std::begin(file_data[depth][x_data[0]]), std::end(file_data[depth][x_data[0]])); + for (row = 0; row < rows; ++row) + { + x[row] = ranges.xmin + (ranges.xmax - ranges.xmin) / (xmax - xmin) * + ((double)file_data[depth][x_data[0]][row] - xmin); + } + } + } + if (!x_data.empty() || !error_data.empty()) { - x[row] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)row / ((double)rows - 1)); + for (col = 0; col < cols + x_data.size() + error_data.size(); col++) + { + if (std::find(error_data.begin(), error_data.end(), col) == error_data.end() && + std::find(x_data.begin(), x_data.end(), col) == x_data.end()) + { + y_ind = col; + break; + } + } } if (!grm_args_values(args, "y_range", "dd", &ymin, &ymax)) { - ymin = *std::min_element(std::begin(file_data[depth][0]), std::end(file_data[depth][0])); - ymax = *std::max_element(std::begin(file_data[depth][0]), std::end(file_data[depth][0])); + ymin = *std::min_element(std::begin(file_data[depth][y_ind]), std::end(file_data[depth][y_ind])); + ymax = *std::max_element(std::begin(file_data[depth][y_ind]), std::end(file_data[depth][y_ind])); adjust_ranges(&ranges.ymin, &ranges.ymax, std::min(0.0, ymin), ymax); grm_args_push(args, "y_range", "dd", ranges.ymin, ranges.ymax); } else { /* apply y_range to the data */ - ymin = *std::min_element(std::begin(file_data[depth][0]), std::end(file_data[depth][0])); - ymax = *std::max_element(std::begin(file_data[depth][0]), std::end(file_data[depth][0])); + ymin = *std::min_element(std::begin(file_data[depth][y_ind]), std::end(file_data[depth][y_ind])); + ymax = *std::max_element(std::begin(file_data[depth][y_ind]), std::end(file_data[depth][y_ind])); for (row = 0; row < rows; ++row) { - file_data[depth][0][row] = - ranges.ymin + (ranges.ymax - ranges.ymin) / (ymax - 0) * ((double)file_data[depth][0][row] - 0); + file_data[depth][y_ind][row] = + ranges.ymin + (ranges.ymax - ranges.ymin) / (ymax - 0) * ((double)file_data[depth][y_ind][row] - 0); } } if (!grm_args_values(args, "x_range", "dd", &xmin, &xmax)) @@ -873,21 +1118,33 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) grm_args_push(args, "x", "nD", rows, x.data()); /* for barplot */ - grm_args_push(args, "y", "nD", rows, file_data[depth][0].data()); + grm_args_push(args, "y", "nD", rows, file_data[depth][y_ind].data()); /* for hist */ - grm_args_push(args, "weights", "nD", rows, file_data[depth][0].data()); + grm_args_push(args, "weights", "nD", rows, file_data[depth][y_ind].data()); /* for stairs */ - grm_args_push(args, "z", "nD", rows, file_data[depth][0].data()); + grm_args_push(args, "z", "nD", rows, file_data[depth][y_ind].data()); /* the needed calculation to get the errorbars out of the data */ - if (grm_args_values(args, "error", "a", &error)) + if (grm_args_values(args, "error", "a", &error) || xye_file || equal_up_and_down_error) { int nbins, i; + int col_group_elem = 3, down_err_off = 2; std::vector errors_up(rows); std::vector errors_down(rows); std::vector bins; - if (cols < 3) + 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; + } + if (cols < col_group_elem || (!error_data.empty() && error_data.size() < down_err_off)) { fprintf(stderr, "Not enough data for error parameter\n"); } @@ -905,8 +1162,16 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) { for (i = 0; i < nbins; i++) { - errors_up[i] = file_data[depth][1][i]; - errors_down[i] = file_data[depth][2][i]; + if (error_data.empty()) + { + errors_up[i] = file_data[depth][1][i]; + errors_down[i] = file_data[depth][down_err_off][i]; + } + else + { + errors_up[i] = file_data[depth][error_data[0]][i]; + errors_down[i] = file_data[depth][equal_up_and_down_error ? error_data[0] : error_data[1]][i]; + } } grm_args_push(error, "relative", "nDD", nbins, errors_up.data(), errors_down.data()); } @@ -1089,6 +1354,18 @@ int convert_inputstream_into_args(grm_args_t *args, grm_file_args_t *file_args, { file_args->file_columns = token.substr(8, token.length() - 1); } + else if (starts_with(token, "x_columns:")) + { + file_args->file_x_columns = token.substr(10, token.length() - 1); + } + else if (starts_with(token, "y_columns:")) + { + file_args->file_y_columns = token.substr(10, token.length() - 1); + } + else if (starts_with(token, "error_columns:")) + { + file_args->file_error_columns = token.substr(14, token.length() - 1); + } else { size_t pos = token.find(delim); @@ -1279,6 +1556,20 @@ int convert_inputstream_into_args(grm_args_t *args, grm_file_args_t *file_args, else if (strcmp(search->first.c_str(), "use_bins") == 0) { use_bins = std::stoi(value); + if (use_bins) xyz_file = 0; + } + else if (strcmp(search->first.c_str(), "equal_up_and_down_error") == 0) + { + equal_up_and_down_error = std::stoi(value); + } + else if (strcmp(search->first.c_str(), "xye_file") == 0) + { + xye_file = std::stoi(value); + } + else if (strcmp(search->first.c_str(), "xyz_file") == 0) + { + xyz_file = std::stoi(value); + if (xyz_file) use_bins = 0; } else { diff --git a/lib/grm/src/grm/import_int.hxx b/lib/grm/src/grm/import_int.hxx index 26988e7ab..b80d051db 100644 --- a/lib/grm/src/grm/import_int.hxx +++ b/lib/grm/src/grm/import_int.hxx @@ -30,6 +30,9 @@ struct _grm_file_args_t { std::string file_path; std::string file_columns; + std::string file_x_columns; + std::string file_y_columns; + std::string file_error_columns; }; /* ========================= functions ============================================================================== */ @@ -37,8 +40,11 @@ struct _grm_file_args_t /* ~~~~~~~~~~~~~~~~~~~~~~~~~ import ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ std::string normalize_line(const std::string &str); +err_t parse_columns(std::list *columns, const char *colms); err_t read_data_file(const std::string &path, std::vector>> &data, - std::vector &labels, grm_args_t *args, const char *colms, PlotRange *ranges); + std::vector &x_data, std::vector &error_data, std::vector &labels, + grm_args_t *args, const char *colms, const char *x_colms, const char *y_colms, const char *e_colms, + PlotRange *ranges); int convert_inputstream_into_args(grm_args_t *args, grm_file_args_t *file_args, int argc, char **argv, PlotRange *ranges); grm_file_args_t *grm_file_args_new(); diff --git a/lib/grm/src/grm/plot.cxx b/lib/grm/src/grm/plot.cxx index a8c837b7e..463bb1f37 100644 --- a/lib/grm/src/grm/plot.cxx +++ b/lib/grm/src/grm/plot.cxx @@ -2620,6 +2620,22 @@ err_t plot_marginal_heatmap(grm_args_t *subplot_args) if (grm_args_values(*current_series, "algorithm", "s", &algorithm)) subGroup->setAttribute("algorithm", algorithm); } + + std::shared_ptr top_side_region; + if (!subGroup->querySelectors("side_region[location=\"top\"]")) + { + top_side_region = global_render->createSideRegion("top"); + subGroup->append(top_side_region); + } + else + { + top_side_region = subGroup->querySelectors("side_region[location=\"top\"]"); + } + top_side_region->setAttribute("marginal_heatmap_side_plot", 1); + auto right_side_region = global_render->createSideRegion("right"); + right_side_region->setAttribute("marginal_heatmap_side_plot", 1); + subGroup->append(right_side_region); + grm_args_push(subplot_args, "kind", "s", "marginal_heatmap"); global_root->setAttribute("_id", ++id); @@ -3303,8 +3319,16 @@ err_t plot_pie(grm_args_t *subplot_args) } if (grm_args_values(subplot_args, "title", "s", &title)) { - auto side_region = global_render->createElement("side_region"); - group->parentElement()->append(side_region); + std::shared_ptr side_region; + if (!group->parentElement()->querySelectors("side_region[location=\"top\"]")) + { + side_region = global_render->createElement("side_region"); + group->parentElement()->append(side_region); + } + else + { + side_region = group->parentElement()->querySelectors("side_region[location=\"top\"]"); + } side_region->setAttribute("text_content", title); side_region->setAttribute("location", "top"); side_region->setAttribute("text_is_title", true); From e7087db6a11871e556b97fe1b3a16cd9986d9f97 Mon Sep 17 00:00:00 2001 From: Verbov Date: Fri, 7 Jun 2024 09:34:20 +0200 Subject: [PATCH 12/24] rename scap to cap, fixed small problem with error_columns, fixed test-mode with editor enabled so f.e. the integral test works --- lib/grm/grplot/README.md | 28 ++++----- lib/grm/grplot/grplot_widget.cxx | 2 +- lib/grm/grplot/qtterm/sender/sender_hist.c | 4 +- .../grm/dom_render/graphics_tree/schema.xsd | 12 ++-- lib/grm/src/grm/dom_render/render.cxx | 63 +++++++++---------- lib/grm/src/grm/import.cxx | 33 +++++----- lib/grm/src/grm/plot.cxx | 10 +-- 7 files changed, 77 insertions(+), 75 deletions(-) diff --git a/lib/grm/grplot/README.md b/lib/grm/grplot/README.md index 01611052a..f920b2b55 100644 --- a/lib/grm/grplot/README.md +++ b/lib/grm/grplot/README.md @@ -45,11 +45,11 @@ The valid container parameters are `error`, `ind_bar_color`, `ind_edge_color`, ` Each parameter container can contain one or more of the following parameters: -1. `downward_scap_color`: only available for `error` +1. `downwards_cap_color`: only available for `error` 2. `error_bar_color`: only available for `error` 3. `indices`: only available for `ind_bar_color`, `ind_edge_color`, `ind_edge_width` 4. `rgb`: only available for `ind_bar_color`, `ind_edge_color` -5. `upward_scap_color`: only available for `error` +5. `upwards_cap_color`: only available for `error` 6. `width`: only available for `ind_edge_width` Container parameters follow a different syntax than normal parameters. The parameters inside the container are enclosed by `{` `}`. If more than one parameter is given to the container each of the key-value pairs is enclosed by `{` `}` while the parameters are seperated by `,` without whitespace characters. @@ -143,12 +143,12 @@ Possible parameters for the bar plot are: 4. `edge_width`: This parameter defines the width of all edges inside the plot except those which are referred with `ind_edge_width`. The value of this parameter is a double number where its default is 1.0. 5. `error`: With this parameter the relative error of each bar can be displayed. The values for this parameter are key-value pairs with the following keys: - `error_bar_color`: Defines the color of the error bars as an integer. If no color is given an error is raised. - - `downward_scap_color`: Defines the downward scap color of the error bars as an integer. - - `upward_scap_color`: Defines the upward scap color of the error bars as an integer. + - `downwards_cap_color`: Defines the downwards cap color of the error bars as an integer. + - `upwards_cap_color`: Defines the upwards cap color of the error bars as an integer. Note: If the error of the bars is to be displayed, the last two columns of the data are used for the error. If the up and down error are equal use `equal_up_and_down_error` with a value of `1`. In that case only one column is expected for the error data instead of the two. This parameter flag also does not need the `error` parameter to be set. Like by the `error` parameter some of the columns inside the data will get interpreted as error-values. The syntax of this parameter is: - `error:{{error_bar_color:`color_index`},{downward_scap_color:`color_index`},{upward_scap_color:`color_index`}}` + `error:{{error_bar_color:`color_index`},{downwards_cap_color:`color_index`},{upwards_cap_color:`color_index`}}` 6. `ind_bar_color`: With this parameter the color of specific bars can be changed. The value of this parameter are key-value pairs with the following keys: - `indices`: The index number of the bar, which color should be changed. @@ -241,12 +241,12 @@ Possible parameters for the histogram are: 3. `edge_color`: This parameter defines the color of all edges inside the plot. The value of this parameter can either be an integer which represents a color index or three doubles which represents the RGB value of the color. If the parameter is omitted color 1 (black) will be used. 4. `error`: With this parameter the relative error of each bar can be displayed. The parameters are key-value pairs with the following keys: - `error_bar_color`: Defines the color of the error bars. If no color is given an error is raised. - - `downward_scap_color`: Defines the downward scap color of the error bars. - - `upward_scap_color`: Defines the upward scap color of the error bars. + - `downwards_cap_color`: Defines the downwards cap color of the error bars. + - `upwards_cap_color`: Defines the upwards cap color of the error bars. Note: If the error of the bars is to be displayed, the last two columns of the data are used for the error. If the up and down error are equal use `equal_up_and_down_error` with a value of `1`. In that case only one column is expected for the error data instead of the two. This parameter flag also does not need the `error` parameter to be set. Like by the `error` parameter some of the columns inside the data will get interpreted as error-values. The syntax of this parameter is: - `error:{{error_bar_color:`color_index`},{downward_scap_color:`color_index`},{upward_scap_color:`color_index`}}` + `error:{{error_bar_color:`color_index`},{downwards_cap_color:`color_index`},{upwards_cap_color:`color_index`}}` 5. `orientation`: This parameter defines the orientation of the displayed bars. They can either be drawn `horizontal` or `vertical` while the default is `horizontal`. ### IMSHOW @@ -284,12 +284,12 @@ Possible parameters for the line plot are: 1. `error`: With this parameter the relative error of each line can be displayed. The value of this parameter are key-value pairs with the following keys: - `error_bar_color`: Defines the color of the error bars. The value of this parameter has to be an integer. When no color is given this leads to an error. - - `downward_scap_color`: Defines the downward scap color of the error bars. The value of this parameter has to be an integer. - - `upward_scap_color`: Defines the upward scap color of the error bars. The value of this parameter has to be an integer. + - `downwards_cap_color`: Defines the downwards cap color of the error bars. The value of this parameter has to be an integer. + - `upwards_cap_color`: Defines the upwards cap color of the error bars. The value of this parameter has to be an integer. Note: If the error of the points is to be displayed, the last two columns of each pair of three columns from the data are used for the error. This means for a dataset with eight columns that columns 2, 3, 5, 6 are used for the error bars. If the up and down error are equal use `equal_up_and_down_error` with a value of `1`. In that case only one column is expected for the error data instead of the two. This parameter flag also does not need the `error` parameter to be set. Like by the `error` parameter some of the columns inside the data will get interpreted as error-values. The syntax of this parameter is: - `error:{{error_bar_color:`color_index`},{downward_scap_color:`color_index`},{upward_scap_color:`color_index`}}` + `error:{{error_bar_color:`color_index`},{downwards_cap_color:`color_index`},{upwards_cap_color:`color_index`}}` 2. `int_limits_high`: This parameter defines the upper limits of all integrals. @@ -380,12 +380,12 @@ Possible parameters for the scatter plot are: 1. `error`: With this parameter the relative error of each line can be displayed. The value of this parameter are key-value pairs with the following keys: - `error_bar_color`: Defines the color of the error bars. The value of this parameter has to be an integer. When no color is given this leads to an error. - - `downward_scap_color`: Defines the downward scap color of the error bars. The value of this parameter has to be an integer. - - `upward_scap_color`: Defines the upward scap color of the error bars. The value of this parameter has to be an integer. + - `downwards_cap_color`: Defines the downwards cap color of the error bars. The value of this parameter has to be an integer. + - `upwards_cap_color`: Defines the upwards cap color of the error bars. The value of this parameter has to be an integer. Note: If the error of the points is to be displayed, the last two columns of the data are used for the error. If the up and down error are equal use `equal_up_and_down_error` with a value of `1`. In that case only one column is expected for the error data instead of the two. This parameter flag also does not need the `error` parameter to be set. Like by the `error` parameter some of the columns inside the data will get interpreted as error-values. The syntax of this parameter is: - `error:{{error_bar_color:`color_index`},{downward_scap_color:`color_index`},{upward_scap_color:`color_index`}}` + `error:{{error_bar_color:`color_index`},{downwards_cap_color:`color_index`},{upwards_cap_color:`color_index`}}` 2. `marker_type`: This parameter defines the style of the visualized data points, where the effect belonging to the numbers is the same as for [gr_setmarkertype](https://gr-framework.org/c-gr.html?highlight=gr_setmarkertype#_CPPv416gr_setmarkertypei). diff --git a/lib/grm/grplot/grplot_widget.cxx b/lib/grm/grplot/grplot_widget.cxx index 253c2358d..b78e14c20 100644 --- a/lib/grm/grplot/grplot_widget.cxx +++ b/lib/grm/grplot/grplot_widget.cxx @@ -428,7 +428,7 @@ GRPlotWidget::GRPlotWidget(QMainWindow *parent, int argc, char **argv) QObject::connect(add_element_action, SIGNAL(triggered()), this, SLOT(add_element_slot())); add_element_action->setVisible(false); - menu->addMenu(editor_menu); + if (strcmp(argv[1], "--test") != 0 && !test_commands_stream) menu->addMenu(editor_menu); } global_root = grm_get_document_root(); } diff --git a/lib/grm/grplot/qtterm/sender/sender_hist.c b/lib/grm/grplot/qtterm/sender/sender_hist.c index 341f46b86..5a7bdf925 100644 --- a/lib/grm/grplot/qtterm/sender/sender_hist.c +++ b/lib/grm/grplot/qtterm/sender/sender_hist.c @@ -45,8 +45,8 @@ static void test_plot(void) error = grm_args_new(); grm_args_push(error, "relative", "nDD", NBINS, errors[0], errors[1]); - grm_args_push(error, "upward_scap_color", "i", 2); - grm_args_push(error, "downward_scap_color", "i", 3); + grm_args_push(error, "upwards_cap_color", "i", 2); + grm_args_push(error, "downwards_cap_color", "i", 3); grm_args_push(error, "error_bar_color", "i", 4); args = grm_args_new(); 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 5b5f95b7b..0ca4d9264 100644 --- a/lib/grm/src/grm/dom_render/graphics_tree/schema.xsd +++ b/lib/grm/src/grm/dom_render/graphics_tree/schema.xsd @@ -545,13 +545,13 @@ - + - + @@ -1220,16 +1220,16 @@ - + + + - - - + diff --git a/lib/grm/src/grm/dom_render/render.cxx b/lib/grm/src/grm/dom_render/render.cxx index 45598ff0e..800b319c1 100644 --- a/lib/grm/src/grm/dom_render/render.cxx +++ b/lib/grm/src/grm/dom_render/render.cxx @@ -6545,7 +6545,7 @@ static void processErrorBars(const std::shared_ptr &element, const std::string absolute_upwards, absolute_downwards, relative_upwards, relative_downwards; double absolute_upwards_flt, relative_upwards_flt, absolute_downwards_flt, relative_downwards_flt; unsigned int i; - int scale_options, color_upward_scap, color_downward_scap, color_error_bar; + int scale_options, color_upwards_cap, color_downwards_cap, color_error_bar; double marker_size, x_min, x_max, y_min, y_max, tick, a, b, e_upwards, e_downwards, x_value; double line_x[2], line_y[2]; std::vector x_vec, y_vec; @@ -6637,11 +6637,11 @@ static void processErrorBars(const std::shared_ptr &element, const gr_inqlinecolorind(&color_error_bar); // special case for barplot if (kind == "barplot") color_error_bar = static_cast(element->parentElement()->getAttribute("line_color_ind")); - color_upward_scap = color_downward_scap = color_error_bar; - if (element->hasAttribute("upward_scap_color")) - color_upward_scap = static_cast(element->getAttribute("upward_scap_color")); - if (element->hasAttribute("downward_scap_color")) - color_downward_scap = static_cast(element->getAttribute("downward_scap_color")); + color_upwards_cap = color_downwards_cap = color_error_bar; + if (element->hasAttribute("upwards_cap_color")) + color_upwards_cap = static_cast(element->getAttribute("upwards_cap_color")); + if (element->hasAttribute("downwards_cap_color")) + color_downwards_cap = static_cast(element->getAttribute("downwards_cap_color")); if (element->hasAttribute("error_bar_color")) color_error_bar = static_cast(element->getAttribute("error_bar_color")); @@ -6710,17 +6710,17 @@ static void processErrorBars(const std::shared_ptr &element, const if (e_upwards != FLT_MAX) { error_bar->setAttribute("e_upwards", e_upwards); - error_bar->setAttribute("upward_scap_color", color_upward_scap); + error_bar->setAttribute("upwards_cap_color", color_upwards_cap); } if (e_downwards != FLT_MAX) { error_bar->setAttribute("e_downwards", e_downwards); - error_bar->setAttribute("downward_scap_color", color_downward_scap); + error_bar->setAttribute("downwards_cap_color", color_downwards_cap); } if (e_downwards != FLT_MAX || e_upwards != FLT_MAX) { - error_bar->setAttribute("scap_x_min", line_x[0]); - error_bar->setAttribute("scap_x_max", line_x[1]); + error_bar->setAttribute("cap_x_min", line_x[0]); + error_bar->setAttribute("cap_x_max", line_x[1]); } } } @@ -6730,9 +6730,9 @@ static void processErrorBars(const std::shared_ptr &element, const static void processErrorBar(const std::shared_ptr &element, const std::shared_ptr &context) { - double scap_x_min, scap_x_max, e_upwards = FLT_MAX, e_downwards = FLT_MAX; + double cap_x_min, cap_x_max, e_upwards = FLT_MAX, e_downwards = FLT_MAX; double error_bar_x, error_bar_y_min, error_bar_y_max; - int color_upward_scap = 0, color_downward_scap = 0, color_error_bar; + int color_upwards_cap = 0, color_downwards_cap = 0, color_error_bar; std::shared_ptr line; del_values del = del_values::update_without_default; int child_id = 0; @@ -6746,20 +6746,20 @@ static void processErrorBar(const std::shared_ptr &element, const error_bar_y_max = static_cast(element->getAttribute("error_bar_y_max")); color_error_bar = static_cast(element->getAttribute("error_bar_color")); - if (element->hasAttribute("scap_x_min")) scap_x_min = static_cast(element->getAttribute("scap_x_min")); - if (element->hasAttribute("scap_x_max")) scap_x_max = static_cast(element->getAttribute("scap_x_max")); + if (element->hasAttribute("cap_x_min")) cap_x_min = static_cast(element->getAttribute("cap_x_min")); + if (element->hasAttribute("cap_x_max")) cap_x_max = static_cast(element->getAttribute("cap_x_max")); if (element->hasAttribute("e_upwards")) e_upwards = static_cast(element->getAttribute("e_upwards")); if (element->hasAttribute("e_downwards")) e_downwards = static_cast(element->getAttribute("e_downwards")); - if (element->hasAttribute("upward_scap_color")) - color_upward_scap = static_cast(element->getAttribute("upward_scap_color")); - if (element->hasAttribute("downward_scap_color")) - color_downward_scap = static_cast(element->getAttribute("downward_scap_color")); + if (element->hasAttribute("upwards_cap_color")) + color_upwards_cap = static_cast(element->getAttribute("upwards_cap_color")); + if (element->hasAttribute("downwards_cap_color")) + color_downwards_cap = static_cast(element->getAttribute("downwards_cap_color")); - if (e_upwards != FLT_MAX && color_upward_scap >= 0) + if (e_upwards != FLT_MAX && color_upwards_cap >= 0) { if (del != del_values::update_without_default && del != del_values::update_with_default) { - line = global_render->createPolyline(scap_x_min, scap_x_max, e_upwards, e_upwards, 0, 0.0, color_upward_scap); + line = global_render->createPolyline(cap_x_min, cap_x_max, e_upwards, e_upwards, 0, 0.0, color_upwards_cap); line->setAttribute("_child_id", child_id++); element->append(line); } @@ -6767,17 +6767,16 @@ static void processErrorBar(const std::shared_ptr &element, const { line = element->querySelectors("polyline[_child_id=" + std::to_string(child_id++) + "]"); if (line != nullptr) - global_render->createPolyline(scap_x_min, scap_x_max, e_upwards, e_upwards, 0, 0.0, color_upward_scap, - line); + global_render->createPolyline(cap_x_min, cap_x_max, e_upwards, e_upwards, 0, 0.0, color_upwards_cap, line); } } - if (e_downwards != FLT_MAX && color_downward_scap >= 0) + if (e_downwards != FLT_MAX && color_downwards_cap >= 0) { if (del != del_values::update_without_default && del != del_values::update_with_default) { - line = global_render->createPolyline(scap_x_min, scap_x_max, e_downwards, e_downwards, 0, 0.0, - color_downward_scap); + line = global_render->createPolyline(cap_x_min, cap_x_max, e_downwards, e_downwards, 0, 0.0, + color_downwards_cap); line->setAttribute("_child_id", child_id++); element->append(line); } @@ -6785,7 +6784,7 @@ static void processErrorBar(const std::shared_ptr &element, const { line = element->querySelectors("polyline[_child_id=" + std::to_string(child_id++) + "]"); if (line != nullptr) - global_render->createPolyline(scap_x_min, scap_x_max, e_downwards, e_downwards, 0, 0.0, color_downward_scap, + global_render->createPolyline(cap_x_min, cap_x_max, e_downwards, e_downwards, 0, 0.0, color_downwards_cap, line); } } @@ -15006,6 +15005,8 @@ std::vector GRM::Render::getDefaultAndTooltip(const std::shared_ptr {std::string("c_lim_min"), std::vector{"NAN", "The beginning color limit"}}, {std::string("c_range_max"), std::vector{"None", "The ending color-value"}}, {std::string("c_range_min"), std::vector{"None", "The beginning color-value"}}, + {std::string("cap_y_max"), std::vector{"None", "The y-value for the upwards cap"}}, + {std::string("cap_y_min"), std::vector{"None", "The y-value for the downwards cap"}}, {std::string("char_height"), std::vector{"None", "The height of the chars"}}, {std::string("char_up_x"), std::vector{"None", "Upside char angle in x-direction of the text"}}, {std::string("char_up_y"), std::vector{"None", "Upside char angle in y-direction of the text"}}, @@ -15031,7 +15032,7 @@ std::vector GRM::Render::getDefaultAndTooltip(const std::shared_ptr std::vector{"0", "Sets if the parameters for movable transformation in x-direction gets ignored."}}, {std::string("disable_y_trans"), std::vector{"0", "Sets if the parameters for movable transformation in y-direction gets ignored."}}, - {std::string("downward_scap_color"), std::vector{"None", "The color value for the downward scaps"}}, + {std::string("downwards_cap_color"), std::vector{"None", "The color value for the downwards caps"}}, {std::string("hide"), std::vector{"1", "Determines if the element will be visible or not"}}, {std::string("draw_edges"), std::vector{"0", "Used in combination with x- and y-colormap to set if edges are drawn"}}, @@ -15040,7 +15041,7 @@ std::vector GRM::Render::getDefaultAndTooltip(const std::shared_ptr {std::string("edge_width"), std::vector{"None", "The width of all edges"}}, {std::string("end_angle"), std::vector{"None", "The end angle of the element"}}, {std::string("error_bar_color"), - std::vector{"None", "The color value for the middle error-bar scaps"}}, + std::vector{"None", "The color value for the middle error-bar caps"}}, {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"}}, @@ -15157,8 +15158,6 @@ std::vector GRM::Render::getDefaultAndTooltip(const std::shared_ptr {std::string("resample_method"), std::vector{"None", "The used resample method"}}, {std::string("rings"), std::vector{"None", "The number of rings for polar coordinate systems"}}, {std::string("scale"), std::vector{"None", "The set scale"}}, - {std::string("scap_y_max"), std::vector{"None", "The y-value for the upward scap"}}, - {std::string("scap_y_min"), std::vector{"None", "The y-value for the downward scap"}}, {std::string("select_specific_xform"), std::vector{ "None", "Selects a predefined transformation from world coordinates to normalized device coordinates"}}, @@ -15206,7 +15205,7 @@ std::vector GRM::Render::getDefaultAndTooltip(const std::shared_ptr {std::string("transformation"), std::vector{"5", "The used transformation"}}, {std::string("transparency"), std::vector{"None", "Sets the transparency value"}}, {std::string("u"), std::vector{"None", "References the u-values stored in the context"}}, - {std::string("upward_scap_color"), std::vector{"None", "The color value for the upward scaps"}}, + {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"}}, {std::string("viewport_x_max"), std::vector{"None", "The ending viewport x-coordinate"}}, {std::string("viewport_x_min"), std::vector{"None", "The beginning viewport x-coordinate"}}, @@ -17227,7 +17226,7 @@ void updateFilter(const std::shared_ptr &element, const std::strin "fill_color_rgb", "fill_color_ind", "line_color_rgb", "line_color_ind", "text", "x1", "x2", "y1", "y2", }; std::vector error_bar{ - "e_downwards", "e_upwards", "error_bar_x", "error_bar_y_max", "error_bar_y_min", "scap_x_max", "scap_x_min", + "cap_x_max", "cap_x_min", "e_downwards", "e_upwards", "error_bar_x", "error_bar_y_max", "error_bar_y_min", }; std::vector marginal_heatmap_plot{ "algorithm", "marginal_heatmap_kind", "x", "x_flip", "y", "y_flip", "z", diff --git a/lib/grm/src/grm/import.cxx b/lib/grm/src/grm/import.cxx index eab9d4456..56fed4a56 100644 --- a/lib/grm/src/grm/import.cxx +++ b/lib/grm/src/grm/import.cxx @@ -114,12 +114,12 @@ static std::map key_alias = { static std::map container_params{ {"error", "a"}, {"ind_bar_color", "nA"}, {"ind_edge_color", "nA"}, {"ind_edge_width", "nA"}}; -static std::map container_to_types{{"downward_scap_color", "i"}, +static std::map container_to_types{{"downwards_cap_color", "i"}, {"error_bar_color", "i"}, {"indices", "i"}, {"indices", "nI"}, {"rgb", "ddd"}, - {"upward_scap_color", "i"}, + {"upwards_cap_color", "i"}, {"width", "d"}}; /* ~~~~~~~~~~~~~~~~~~~~~~~~~ global flags defined by the user input ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -864,10 +864,10 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) 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, "downward_scap_color", "i", &color_down)) - grm_args_push(error_vec[col], "downward_scap_color", "i", color_down); - if (grm_args_values(error, "upward_scap_color", "i", &color_up)) - grm_args_push(error_vec[col], "upward_scap_color", "i", color_up); + 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; } @@ -876,9 +876,18 @@ int grm_interactive_plot_from_file(grm_args_t *args, int argc, char **argv) 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) { - error_vec[cnt] = grm_args_new(); for (i = 0; i < rows; i++) { if (equal_up_and_down_error) @@ -897,19 +906,13 @@ 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[cnt / (equal_up_and_down_error ? 1 : 2)], "relative", "nDD", rows, - errors_up.data(), errors_down.data()); + 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); } - if (grm_args_values(error, "error_bar_color", "i", &color)) - grm_args_push(error_vec[cnt], "error_bar_color", "i", color); - if (grm_args_values(error, "downward_scap_color", "i", &color_down)) - grm_args_push(error_vec[cnt], "downward_scap_color", "i", color_down); - if (grm_args_values(error, "upward_scap_color", "i", &color_up)) - grm_args_push(error_vec[cnt], "upward_scap_color", "i", color_up); cnt += 1; } } diff --git a/lib/grm/src/grm/plot.cxx b/lib/grm/src/grm/plot.cxx index 463bb1f37..b7d879b0d 100644 --- a/lib/grm/src/grm/plot.cxx +++ b/lib/grm/src/grm/plot.cxx @@ -3895,7 +3895,7 @@ err_t plot_draw_error_bars(grm_args_t *series_args, unsigned int x_length) *relative_downwards = nullptr; double absolute_upwards_flt, relative_upwards_flt, absolute_downwards_flt, relative_downwards_flt; unsigned int upwards_length, downwards_length, i; - int color_upward_scap, color_downward_scap, color_error_bar; + int color_upwards_cap, color_downwards_cap, color_error_bar; std::shared_ptr group = (current_central_region_element) ? current_central_region_element->lastChildElement() @@ -3974,10 +3974,10 @@ err_t plot_draw_error_bars(grm_args_t *series_args, unsigned int x_length) if (error_container != nullptr) { - if (grm_args_values(error_container, "upward_scap_color", "i", &color_upward_scap)) - subGroup->setAttribute("upward_scap_color", color_upward_scap); - if (grm_args_values(error_container, "downward_scap_color", "i", &color_downward_scap)) - subGroup->setAttribute("downward_scap_color", color_downward_scap); + if (grm_args_values(error_container, "upwards_cap_color", "i", &color_upwards_cap)) + subGroup->setAttribute("upwards_cap_color", color_upwards_cap); + if (grm_args_values(error_container, "downwards_cap_color", "i", &color_downwards_cap)) + subGroup->setAttribute("downwards_cap_color", color_downwards_cap); if (grm_args_values(error_container, "error_bar_color", "i", &color_error_bar)) subGroup->setAttribute("error_bar_color", color_error_bar); } From 2e32f01f2664485f3723862046a0a825604f2587 Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Tue, 11 Jun 2024 09:59:53 +0200 Subject: [PATCH 13/24] GR: optionally draw grid lines in gr_drawaxis() --- lib/gr/gr.c | 66 +++++++++++++++++++++++++++++++++++++++++++++-------- lib/gr/gr.h | 2 +- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index ecb044f3a..198dcd3ed 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -5435,10 +5435,10 @@ void gr_axis(char which, axis_t *axis) } } -void gr_drawaxis(char which, axis_t *axis) +void gr_drawaxis(char which, axis_t *axis, int draw_axis_line, int draw_grid_lines) { - int errind, tnr; - double wn[4], vp[4]; + int errind, tnr, ltype, color, clsw; + double wn[4], vp[4], width, clrt[4]; double tick, minor_tick, major_tick, label_org; int i; @@ -5449,6 +5449,14 @@ void gr_drawaxis(char which, axis_t *axis) gks_inq_current_xformno(&errind, &tnr); gks_inq_xform(tnr, &errind, wn, vp); + /* save linetype, line width, line color and clipping indicator */ + + gks_inq_pline_linetype(&errind, <ype); + gks_inq_pline_linewidth(&errind, &width); + gks_inq_pline_color_index(&errind, &color); + gks_inq_clip(&errind, &clsw, clrt); + + gks_set_pline_linetype(GKS_K_LINETYPE_SOLID); gks_set_clipping(GKS_K_NOCLIP); if (which == 'X') @@ -5457,9 +5465,12 @@ void gr_drawaxis(char which, axis_t *axis) minor_tick = y_log(y_lin(axis->org) + tick); major_tick = y_log(y_lin(axis->org) + 2 * tick); label_org = y_log(y_lin(axis->org) - tick); - pline(axis->min, axis->org); - pline(axis->max, axis->org); - end_pline(); + if (draw_axis_line) + { + start_pline(axis->min, axis->org); + pline(axis->max, axis->org); + end_pline(); + } gks_set_text_align(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_TOP); } else @@ -5468,12 +5479,44 @@ void gr_drawaxis(char which, axis_t *axis) minor_tick = x_log(x_lin(axis->org) + tick); major_tick = x_log(x_lin(axis->org) + 2 * tick); label_org = x_log(x_lin(axis->org) - tick); - pline(axis->org, axis->min); - pline(axis->org, axis->max); - end_pline(); + if (draw_axis_line) + { + start_pline(axis->org, axis->min); + pline(axis->org, axis->max); + end_pline(); + } gks_set_text_align(GKS_K_TEXT_HALIGN_RIGHT, GKS_K_TEXT_VALIGN_HALF); } + if (draw_grid_lines) + { + for (i = 0; i < axis->num_ticks; i++) + { + if (color != 0) + gks_set_pline_color_index(axis->ticks[i].is_major ? 88 : 90); + else + gks_set_pline_linewidth(axis->ticks[i].is_major ? 2.0 : 1.0); + + if (which == 'X') + { + pline(axis->ticks[i].value, wn[2]); + pline(axis->ticks[i].value, wn[3]); + end_pline(); + } + else + { + pline(wn[0], axis->ticks[i].value); + pline(wn[1], axis->ticks[i].value); + end_pline(); + } + } + + /* restore line width, line color */ + + gks_set_pline_linewidth(width); + gks_set_pline_color_index(color); + } + for (i = 0; i < axis->num_ticks; i++) { tick = axis->ticks[i].is_major ? major_tick : minor_tick; @@ -5498,6 +5541,11 @@ void gr_drawaxis(char which, axis_t *axis) else text2d(label_org, axis->tick_labels[i].tick, axis->tick_labels[i].label); } + + /* restore linetype and clipping indicator */ + + gks_set_pline_linetype(ltype); + gks_set_clipping(clsw); } void gr_freeaxis(axis_t *axis) diff --git a/lib/gr/gr.h b/lib/gr/gr.h index fd6b989af..ee50b7701 100644 --- a/lib/gr/gr.h +++ b/lib/gr/gr.h @@ -291,7 +291,7 @@ DLLEXPORT void gr_axeslbl(double, double, double, double, int, int, double, void (*)(double, double, const char *, double), void (*)(double, double, const char *, double)); DLLEXPORT void gr_axis(char, axis_t *); -DLLEXPORT void gr_drawaxis(char, axis_t *); +DLLEXPORT void gr_drawaxis(char, axis_t *, int, int); DLLEXPORT void gr_freeaxis(axis_t *); DLLEXPORT void gr_grid(double, double, double, double, int, int); DLLEXPORT void gr_grid3d(double, double, double, double, double, double, int, int, int); From fad6f608b799b0cdcbfef02e0b9af307e907a740 Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Tue, 11 Jun 2024 12:37:23 +0200 Subject: [PATCH 14/24] GR: avoid duplicate lines --- lib/gr/gr.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 198dcd3ed..aa98c4fb7 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -5441,6 +5441,7 @@ void gr_drawaxis(char which, axis_t *axis, int draw_axis_line, int draw_grid_lin double wn[4], vp[4], width, clrt[4]; double tick, minor_tick, major_tick, label_org; int i; + double epsilon; check_autoinit; @@ -5490,6 +5491,8 @@ void gr_drawaxis(char which, axis_t *axis, int draw_axis_line, int draw_grid_lin if (draw_grid_lines) { + epsilon = FEPS * (axis->max - axis->min); + for (i = 0; i < axis->num_ticks; i++) { if (color != 0) @@ -5497,17 +5500,20 @@ void gr_drawaxis(char which, axis_t *axis, int draw_axis_line, int draw_grid_lin else gks_set_pline_linewidth(axis->ticks[i].is_major ? 2.0 : 1.0); - if (which == 'X') + if (fabs(axis->ticks[i].value - axis->min) > epsilon || draw_axis_line == 0) { - pline(axis->ticks[i].value, wn[2]); - pline(axis->ticks[i].value, wn[3]); - end_pline(); - } - else - { - pline(wn[0], axis->ticks[i].value); - pline(wn[1], axis->ticks[i].value); - end_pline(); + if (which == 'X') + { + pline(axis->ticks[i].value, wn[2]); + pline(axis->ticks[i].value, wn[3]); + end_pline(); + } + else + { + pline(wn[0], axis->ticks[i].value); + pline(wn[1], axis->ticks[i].value); + end_pline(); + } } } From 937a7692f57f556995960d623cade554883a9d75 Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Tue, 11 Jun 2024 15:35:13 +0200 Subject: [PATCH 15/24] GR: consider the origin when positioning labels --- lib/gr/gr.c | 59 ++++++++++++++++++++++++++++++++++++----------------- lib/gr/gr.h | 1 + 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index aa98c4fb7..190f7110c 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -5338,7 +5338,8 @@ void gr_axis(char which, axis_t *axis) if (scale_option & GR_OPTION_X_LOG == 0) gr_adjustrange(&x_min, &x_max); if (is_nan(axis->min)) axis->min = x_min; if (is_nan(axis->max)) axis->max = x_max; - if (is_nan(axis->org)) axis->org = y_min; + if (is_nan(axis->org)) axis->org = x_min; + if (is_nan(axis->position)) axis->position = y_min; } else if (which == 'Y') { @@ -5347,7 +5348,8 @@ void gr_axis(char which, axis_t *axis) if (scale_option & GR_OPTION_Y_LOG == 0) gr_adjustrange(&y_min, &y_max); if (is_nan(axis->min)) axis->min = y_min; if (is_nan(axis->max)) axis->max = y_max; - if (is_nan(axis->org)) axis->org = x_min; + if (is_nan(axis->org)) axis->org = y_min; + if (is_nan(axis->position)) axis->position = x_min; } if (is_nan(axis->tick_size)) axis->tick_size = 0.0075; @@ -5439,7 +5441,7 @@ void gr_drawaxis(char which, axis_t *axis, int draw_axis_line, int draw_grid_lin { int errind, tnr, ltype, color, clsw; double wn[4], vp[4], width, clrt[4]; - double tick, minor_tick, major_tick, label_org; + double tick, minor_tick, major_tick, label_position; int i; double epsilon; @@ -5463,30 +5465,49 @@ void gr_drawaxis(char which, axis_t *axis, int draw_axis_line, int draw_grid_lin if (which == 'X') { tick = axis->tick_size * (wn[3] - wn[2]) / (vp[3] - vp[2]); - minor_tick = y_log(y_lin(axis->org) + tick); - major_tick = y_log(y_lin(axis->org) + 2 * tick); - label_org = y_log(y_lin(axis->org) - tick); + minor_tick = y_log(y_lin(axis->position) + tick); + major_tick = y_log(y_lin(axis->position) + 2 * tick); + label_position = y_log(y_lin(axis->position) + 3 * tick); if (draw_axis_line) { - start_pline(axis->min, axis->org); - pline(axis->max, axis->org); + start_pline(axis->min, axis->position); + pline(axis->max, axis->position); end_pline(); } + if (y_lin(axis->position) <= 0.5 * (y_lin(wn[2] + y_lin(wn[3])))) + { + gks_set_text_align(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_TOP); + if (tick > 0) label_position = y_log(y_lin(axis->position) - tick); + } + else + { + gks_set_text_align(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_BOTTOM); + if (tick < 0) label_position = y_log(y_lin(axis->position) - tick); + } gks_set_text_align(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_TOP); } else { tick = axis->tick_size * (wn[1] - wn[0]) / (vp[1] - vp[0]); - minor_tick = x_log(x_lin(axis->org) + tick); - major_tick = x_log(x_lin(axis->org) + 2 * tick); - label_org = x_log(x_lin(axis->org) - tick); + minor_tick = x_log(x_lin(axis->position) + tick); + major_tick = x_log(x_lin(axis->position) + 2 * tick); + label_position = x_log(x_lin(axis->position) + 3 * tick); if (draw_axis_line) { - start_pline(axis->org, axis->min); - pline(axis->org, axis->max); + start_pline(axis->position, axis->min); + pline(axis->position, axis->max); end_pline(); } - gks_set_text_align(GKS_K_TEXT_HALIGN_RIGHT, GKS_K_TEXT_VALIGN_HALF); + if (x_lin(axis->position) <= 0.5 * (x_lin(wn[0] + x_lin(wn[1])))) + { + gks_set_text_align(GKS_K_TEXT_HALIGN_RIGHT, GKS_K_TEXT_VALIGN_HALF); + if (tick > 0) label_position = x_log(x_lin(axis->position) - tick); + } + else + { + gks_set_text_align(GKS_K_TEXT_HALIGN_LEFT, GKS_K_TEXT_VALIGN_HALF); + if (tick < 0) label_position = x_log(x_lin(axis->position) - tick); + } } if (draw_grid_lines) @@ -5500,7 +5521,7 @@ void gr_drawaxis(char which, axis_t *axis, int draw_axis_line, int draw_grid_lin else gks_set_pline_linewidth(axis->ticks[i].is_major ? 2.0 : 1.0); - if (fabs(axis->ticks[i].value - axis->min) > epsilon || draw_axis_line == 0) + if (fabs(axis->ticks[i].value - axis->org) > epsilon || draw_axis_line == 0) { if (which == 'X') { @@ -5528,13 +5549,13 @@ void gr_drawaxis(char which, axis_t *axis, int draw_axis_line, int draw_grid_lin tick = axis->ticks[i].is_major ? major_tick : minor_tick; if (which == 'X') { - pline(axis->ticks[i].value, axis->org); + pline(axis->ticks[i].value, axis->position); pline(axis->ticks[i].value, tick); end_pline(); } else { - pline(axis->org, axis->ticks[i].value); + pline(axis->position, axis->ticks[i].value); pline(tick, axis->ticks[i].value); end_pline(); } @@ -5543,9 +5564,9 @@ void gr_drawaxis(char which, axis_t *axis, int draw_axis_line, int draw_grid_lin for (i = 0; i < axis->num_tick_labels; i++) { if (which == 'X') - text2d(axis->tick_labels[i].tick, label_org, axis->tick_labels[i].label); + text2d(axis->tick_labels[i].tick, label_position, axis->tick_labels[i].label); else - text2d(label_org, axis->tick_labels[i].tick, axis->tick_labels[i].label); + text2d(label_position, axis->tick_labels[i].tick, axis->tick_labels[i].label); } /* restore linetype and clipping indicator */ diff --git a/lib/gr/gr.h b/lib/gr/gr.h index ee50b7701..2cb7f2255 100644 --- a/lib/gr/gr.h +++ b/lib/gr/gr.h @@ -197,6 +197,7 @@ typedef struct { double min, max; double tick, org; + double position; int major_count; int num_ticks; tick_t *ticks; From 75ffe26f551c5355e340d8fb0863e43e52312c3f Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Thu, 13 Jun 2024 14:43:44 +0200 Subject: [PATCH 16/24] GR: add gr_drawaxes() function --- lib/gr/gr.c | 167 +++++++++++++++++++++++++++++++++++++--------------- lib/gr/gr.h | 7 ++- 2 files changed, 126 insertions(+), 48 deletions(-) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 190f7110c..eaad9b6e8 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -5437,10 +5437,10 @@ void gr_axis(char which, axis_t *axis) } } -void gr_drawaxis(char which, axis_t *axis, int draw_axis_line, int draw_grid_lines) +void gr_drawaxis(char which, axis_t *axis) { - int errind, tnr, ltype, color, clsw; - double wn[4], vp[4], width, clrt[4]; + int errind, tnr, ltype, clsw; + double wn[4], vp[4], clrt[4]; double tick, minor_tick, major_tick, label_position; int i; double epsilon; @@ -5452,11 +5452,9 @@ void gr_drawaxis(char which, axis_t *axis, int draw_axis_line, int draw_grid_lin gks_inq_current_xformno(&errind, &tnr); gks_inq_xform(tnr, &errind, wn, vp); - /* save linetype, line width, line color and clipping indicator */ + /* save linetype and clipping indicator */ gks_inq_pline_linetype(&errind, <ype); - gks_inq_pline_linewidth(&errind, &width); - gks_inq_pline_color_index(&errind, &color); gks_inq_clip(&errind, &clsw, clrt); gks_set_pline_linetype(GKS_K_LINETYPE_SOLID); @@ -5468,7 +5466,7 @@ void gr_drawaxis(char which, axis_t *axis, int draw_axis_line, int draw_grid_lin minor_tick = y_log(y_lin(axis->position) + tick); major_tick = y_log(y_lin(axis->position) + 2 * tick); label_position = y_log(y_lin(axis->position) + 3 * tick); - if (draw_axis_line) + if (axis->draw_axis_line) { start_pline(axis->min, axis->position); pline(axis->max, axis->position); @@ -5484,7 +5482,6 @@ void gr_drawaxis(char which, axis_t *axis, int draw_axis_line, int draw_grid_lin gks_set_text_align(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_BOTTOM); if (tick < 0) label_position = y_log(y_lin(axis->position) - tick); } - gks_set_text_align(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_TOP); } else { @@ -5492,7 +5489,7 @@ void gr_drawaxis(char which, axis_t *axis, int draw_axis_line, int draw_grid_lin minor_tick = x_log(x_lin(axis->position) + tick); major_tick = x_log(x_lin(axis->position) + 2 * tick); label_position = x_log(x_lin(axis->position) + 3 * tick); - if (draw_axis_line) + if (axis->draw_axis_line) { start_pline(axis->position, axis->min); pline(axis->position, axis->max); @@ -5510,40 +5507,6 @@ void gr_drawaxis(char which, axis_t *axis, int draw_axis_line, int draw_grid_lin } } - if (draw_grid_lines) - { - epsilon = FEPS * (axis->max - axis->min); - - for (i = 0; i < axis->num_ticks; i++) - { - if (color != 0) - gks_set_pline_color_index(axis->ticks[i].is_major ? 88 : 90); - else - gks_set_pline_linewidth(axis->ticks[i].is_major ? 2.0 : 1.0); - - if (fabs(axis->ticks[i].value - axis->org) > epsilon || draw_axis_line == 0) - { - if (which == 'X') - { - pline(axis->ticks[i].value, wn[2]); - pline(axis->ticks[i].value, wn[3]); - end_pline(); - } - else - { - pline(wn[0], axis->ticks[i].value); - pline(wn[1], axis->ticks[i].value); - end_pline(); - } - } - } - - /* restore line width, line color */ - - gks_set_pline_linewidth(width); - gks_set_pline_color_index(color); - } - for (i = 0; i < axis->num_ticks; i++) { tick = axis->ticks[i].is_major ? major_tick : minor_tick; @@ -5561,12 +5524,122 @@ void gr_drawaxis(char which, axis_t *axis, int draw_axis_line, int draw_grid_lin } } - for (i = 0; i < axis->num_tick_labels; i++) + if (axis->major_count > 0) { - if (which == 'X') - text2d(axis->tick_labels[i].tick, label_position, axis->tick_labels[i].label); + for (i = 0; i < axis->num_tick_labels; i++) + { + if (which == 'X') + text2d(axis->tick_labels[i].tick, label_position, axis->tick_labels[i].label); + else + text2d(label_position, axis->tick_labels[i].tick, axis->tick_labels[i].label); + } + } + + /* restore linetype and clipping indicator */ + + gks_set_pline_linetype(ltype); + gks_set_clipping(clsw); +} + +static void draw_axis_grid(char which, axis_t *axis) +{ + int errind, tnr, color; + double wn[4], vp[4], width, epsilon; + int i; + + /* inquire current normalization transformation */ + + gks_inq_current_xformno(&errind, &tnr); + gks_inq_xform(tnr, &errind, wn, vp); + + /* save line width and line color */ + + gks_inq_pline_linewidth(&errind, &width); + gks_inq_pline_color_index(&errind, &color); + + gks_inq_pline_color_index(&errind, &color); + + epsilon = FEPS * (axis->max - axis->min); + + for (i = 0; i < axis->num_ticks; i++) + { + if (color != 0) + gks_set_pline_color_index(axis->ticks[i].is_major ? 88 : 90); else - text2d(label_position, axis->tick_labels[i].tick, axis->tick_labels[i].label); + gks_set_pline_linewidth(axis->ticks[i].is_major ? 2.0 : 1.0); + + if (fabs(axis->ticks[i].value - axis->org) > epsilon) + { + if (which == 'X') + { + pline(axis->ticks[i].value, wn[2]); + pline(axis->ticks[i].value, wn[3]); + end_pline(); + } + else + { + pline(wn[0], axis->ticks[i].value); + pline(wn[1], axis->ticks[i].value); + end_pline(); + } + } + } + + /* restore line width and line color */ + + gks_set_pline_linewidth(width); + gks_set_pline_color_index(color); +} + +void gr_drawaxes(axis_t *x_axis, axis_t *y_axis, int options) +{ + int errind, tnr, ltype, clsw; + double wn[4], vp[4], clrt[4]; + double tick, minor_tick, major_tick, label_position; + int i; + double epsilon; + + check_autoinit; + + /* inquire current normalization transformation */ + + gks_inq_current_xformno(&errind, &tnr); + gks_inq_xform(tnr, &errind, wn, vp); + + /* save linetype and clipping indicator */ + + gks_inq_pline_linetype(&errind, <ype); + gks_inq_clip(&errind, &clsw, clrt); + + gks_set_pline_linetype(GKS_K_LINETYPE_SOLID); + gks_set_clipping(GKS_K_NOCLIP); + + if ((options & GR_AXES_WITH_GRID) != 0) + { + draw_axis_grid('X', x_axis); + draw_axis_grid('Y', y_axis); + } + + gr_drawaxis('X', x_axis); + gr_drawaxis('Y', y_axis); + + if ((options & GR_AXES_WITH_FRAME) != 0) + { + axis_t axis; + + memcpy(&axis, x_axis, sizeof(axis_t)); + x_axis->position = wn[3]; + x_axis->tick_size = -x_axis->tick_size; + x_axis->major_count = -x_axis->major_count; + gr_drawaxis('X', x_axis); + memcpy(x_axis, &axis, sizeof(axis_t)); + + memcpy(&axis, y_axis, sizeof(axis_t)); + y_axis->position = wn[1]; + y_axis->tick_size = -y_axis->tick_size; + y_axis->major_count = -y_axis->major_count; + gr_drawaxis('Y', y_axis); + memcpy(y_axis, &axis, sizeof(axis_t)); } /* restore linetype and clipping indicator */ diff --git a/lib/gr/gr.h b/lib/gr/gr.h index 2cb7f2255..97253f1cc 100644 --- a/lib/gr/gr.h +++ b/lib/gr/gr.h @@ -204,8 +204,12 @@ typedef struct int num_tick_labels; tick_label_t *tick_labels; double tick_size; + int draw_axis_line; } axis_t; +#define GR_AXES_WITH_GRID (1 << 0) +#define GR_AXES_WITH_FRAME (1 << 1) + DLLEXPORT void gr_initgr(void); DLLEXPORT int gr_debug(void); DLLEXPORT void gr_opengks(void); @@ -292,7 +296,8 @@ DLLEXPORT void gr_axeslbl(double, double, double, double, int, int, double, void (*)(double, double, const char *, double), void (*)(double, double, const char *, double)); DLLEXPORT void gr_axis(char, axis_t *); -DLLEXPORT void gr_drawaxis(char, axis_t *, int, int); +DLLEXPORT void gr_drawaxis(char, axis_t *); +DLLEXPORT void gr_drawaxes(axis_t *, axis_t *, int); DLLEXPORT void gr_freeaxis(axis_t *); DLLEXPORT void gr_grid(double, double, double, double, int, int); DLLEXPORT void gr_grid3d(double, double, double, double, double, double, int, int, int); From 59b98d779845646ec79f355784cb3f9ae2d67245 Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Mon, 17 Jun 2024 10:34:46 +0200 Subject: [PATCH 17/24] GR: allow the drawing of individual axes --- lib/gr/gr.c | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index eaad9b6e8..2a3228b86 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -5616,30 +5616,36 @@ void gr_drawaxes(axis_t *x_axis, axis_t *y_axis, int options) if ((options & GR_AXES_WITH_GRID) != 0) { - draw_axis_grid('X', x_axis); - draw_axis_grid('Y', y_axis); + if (x_axis != NULL) draw_axis_grid('X', x_axis); + if (y_axis != NULL) draw_axis_grid('Y', y_axis); } - gr_drawaxis('X', x_axis); - gr_drawaxis('Y', y_axis); + if (x_axis != NULL) gr_drawaxis('X', x_axis); + if (y_axis != NULL) gr_drawaxis('Y', y_axis); if ((options & GR_AXES_WITH_FRAME) != 0) { axis_t axis; - memcpy(&axis, x_axis, sizeof(axis_t)); - x_axis->position = wn[3]; - x_axis->tick_size = -x_axis->tick_size; - x_axis->major_count = -x_axis->major_count; - gr_drawaxis('X', x_axis); - memcpy(x_axis, &axis, sizeof(axis_t)); - - memcpy(&axis, y_axis, sizeof(axis_t)); - y_axis->position = wn[1]; - y_axis->tick_size = -y_axis->tick_size; - y_axis->major_count = -y_axis->major_count; - gr_drawaxis('Y', y_axis); - memcpy(y_axis, &axis, sizeof(axis_t)); + if (x_axis != NULL) + { + memcpy(&axis, x_axis, sizeof(axis_t)); + x_axis->position = wn[3]; + x_axis->tick_size = -x_axis->tick_size; + x_axis->major_count = -x_axis->major_count; + gr_drawaxis('X', x_axis); + memcpy(x_axis, &axis, sizeof(axis_t)); + } + + if (y_axis != NULL) + { + memcpy(&axis, y_axis, sizeof(axis_t)); + y_axis->position = wn[1]; + y_axis->tick_size = -y_axis->tick_size; + y_axis->major_count = -y_axis->major_count; + gr_drawaxis('Y', y_axis); + memcpy(y_axis, &axis, sizeof(axis_t)); + } } /* restore linetype and clipping indicator */ From ff2528ee5c442b02517e9081772395812df379b9 Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Mon, 17 Jun 2024 12:02:06 +0200 Subject: [PATCH 18/24] GR: preserve label position --- lib/gr/gr.c | 3 ++- lib/gr/gr.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 2a3228b86..9692c3603 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -5534,6 +5534,7 @@ void gr_drawaxis(char which, axis_t *axis) text2d(label_position, axis->tick_labels[i].tick, axis->tick_labels[i].label); } } + axis->label_position = label_position; /* restore linetype and clipping indicator */ @@ -5595,7 +5596,7 @@ void gr_drawaxes(axis_t *x_axis, axis_t *y_axis, int options) { int errind, tnr, ltype, clsw; double wn[4], vp[4], clrt[4]; - double tick, minor_tick, major_tick, label_position; + double tick, minor_tick, major_tick; int i; double epsilon; diff --git a/lib/gr/gr.h b/lib/gr/gr.h index 97253f1cc..962ffa95e 100644 --- a/lib/gr/gr.h +++ b/lib/gr/gr.h @@ -201,9 +201,10 @@ typedef struct int major_count; int num_ticks; tick_t *ticks; + double tick_size; int num_tick_labels; tick_label_t *tick_labels; - double tick_size; + double label_position; int draw_axis_line; } axis_t; From f913cd0dd2f47ffc58c9fbe689891e138ec12789 Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Mon, 17 Jun 2024 13:08:37 +0200 Subject: [PATCH 19/24] GR: move the calculation of the label position to gr_axis() --- lib/gr/gr.c | 65 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 9692c3603..9ea2eaec5 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -5313,7 +5313,7 @@ void gr_axis(char which, axis_t *axis) double wn[4], vp[4]; double x_min, x_max, y_min, y_max; int scale_option, base, decade, exponent; - double epsilon; + double tick, epsilon; int i, j, k; double a, a0, tbx[4], tby[4]; char *s; @@ -5331,29 +5331,55 @@ void gr_axis(char which, axis_t *axis) y_min = wn[2]; y_max = wn[3]; + if (is_nan(axis->tick_size)) axis->tick_size = 0.0075; + if (which == 'X') { scale_option = GR_OPTION_X_LOG; base = lx.basex; - if (scale_option & GR_OPTION_X_LOG == 0) gr_adjustrange(&x_min, &x_max); + if ((lx.scale_options & GR_OPTION_X_LOG) == 0) gr_adjustrange(&x_min, &x_max); if (is_nan(axis->min)) axis->min = x_min; if (is_nan(axis->max)) axis->max = x_max; if (is_nan(axis->org)) axis->org = x_min; if (is_nan(axis->position)) axis->position = y_min; + if (is_nan(axis->label_position)) + { + tick = axis->tick_size * (wn[3] - wn[2]) / (vp[3] - vp[2]); + axis->label_position = y_log(y_lin(axis->position) + 3 * tick); + if (y_lin(axis->position) <= 0.5 * (y_lin(wn[2] + y_lin(wn[3])))) + { + if (tick > 0) axis->label_position = y_log(y_lin(axis->position) - tick); + } + else + { + if (tick < 0) axis->label_position = y_log(y_lin(axis->position) - tick); + } + } } else if (which == 'Y') { scale_option = GR_OPTION_Y_LOG; base = lx.basey; - if (scale_option & GR_OPTION_Y_LOG == 0) gr_adjustrange(&y_min, &y_max); + if ((lx.scale_options & GR_OPTION_Y_LOG) == 0) gr_adjustrange(&y_min, &y_max); if (is_nan(axis->min)) axis->min = y_min; if (is_nan(axis->max)) axis->max = y_max; if (is_nan(axis->org)) axis->org = y_min; if (is_nan(axis->position)) axis->position = x_min; + if (is_nan(axis->label_position)) + { + tick = axis->tick_size * (wn[1] - wn[0]) / (vp[1] - vp[0]); + axis->label_position = x_log(x_lin(axis->position) + 3 * tick); + if (x_lin(axis->position) <= 0.5 * (x_lin(wn[0] + x_lin(wn[1])))) + { + if (tick > 0) axis->label_position = x_log(x_lin(axis->position) - tick); + } + else + { + if (tick < 0) axis->label_position = x_log(x_lin(axis->position) - tick); + } + } } - if (is_nan(axis->tick_size)) axis->tick_size = 0.0075; - if (scale_option & lx.scale_options) { axis->num_tick_labels = igauss(blog(base, axis->max / axis->min)) + 2; @@ -5441,7 +5467,7 @@ void gr_drawaxis(char which, axis_t *axis) { int errind, tnr, ltype, clsw; double wn[4], vp[4], clrt[4]; - double tick, minor_tick, major_tick, label_position; + double tick, minor_tick, major_tick; int i; double epsilon; @@ -5465,7 +5491,6 @@ void gr_drawaxis(char which, axis_t *axis) tick = axis->tick_size * (wn[3] - wn[2]) / (vp[3] - vp[2]); minor_tick = y_log(y_lin(axis->position) + tick); major_tick = y_log(y_lin(axis->position) + 2 * tick); - label_position = y_log(y_lin(axis->position) + 3 * tick); if (axis->draw_axis_line) { start_pline(axis->min, axis->position); @@ -5473,22 +5498,15 @@ void gr_drawaxis(char which, axis_t *axis) end_pline(); } if (y_lin(axis->position) <= 0.5 * (y_lin(wn[2] + y_lin(wn[3])))) - { - gks_set_text_align(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_TOP); - if (tick > 0) label_position = y_log(y_lin(axis->position) - tick); - } + gks_set_text_align(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_TOP); else - { - gks_set_text_align(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_BOTTOM); - if (tick < 0) label_position = y_log(y_lin(axis->position) - tick); - } + gks_set_text_align(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_BOTTOM); } else { tick = axis->tick_size * (wn[1] - wn[0]) / (vp[1] - vp[0]); minor_tick = x_log(x_lin(axis->position) + tick); major_tick = x_log(x_lin(axis->position) + 2 * tick); - label_position = x_log(x_lin(axis->position) + 3 * tick); if (axis->draw_axis_line) { start_pline(axis->position, axis->min); @@ -5496,15 +5514,9 @@ void gr_drawaxis(char which, axis_t *axis) end_pline(); } if (x_lin(axis->position) <= 0.5 * (x_lin(wn[0] + x_lin(wn[1])))) - { - gks_set_text_align(GKS_K_TEXT_HALIGN_RIGHT, GKS_K_TEXT_VALIGN_HALF); - if (tick > 0) label_position = x_log(x_lin(axis->position) - tick); - } + gks_set_text_align(GKS_K_TEXT_HALIGN_RIGHT, GKS_K_TEXT_VALIGN_HALF); else - { - gks_set_text_align(GKS_K_TEXT_HALIGN_LEFT, GKS_K_TEXT_VALIGN_HALF); - if (tick < 0) label_position = x_log(x_lin(axis->position) - tick); - } + gks_set_text_align(GKS_K_TEXT_HALIGN_LEFT, GKS_K_TEXT_VALIGN_HALF); } for (i = 0; i < axis->num_ticks; i++) @@ -5529,12 +5541,11 @@ void gr_drawaxis(char which, axis_t *axis) for (i = 0; i < axis->num_tick_labels; i++) { if (which == 'X') - text2d(axis->tick_labels[i].tick, label_position, axis->tick_labels[i].label); + text2d(axis->tick_labels[i].tick, axis->label_position, axis->tick_labels[i].label); else - text2d(label_position, axis->tick_labels[i].tick, axis->tick_labels[i].label); + text2d(axis->label_position, axis->tick_labels[i].tick, axis->tick_labels[i].label); } } - axis->label_position = label_position; /* restore linetype and clipping indicator */ From 76895903c1b34d41bf5590ebe3ec5de92339e9df Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Mon, 17 Jun 2024 16:41:07 +0200 Subject: [PATCH 20/24] Fix RHEL system dependencies builds with installed libxml2 On RHEL 9 based Linux distributions, `libxml2` is found in `/lib64` by default since the `PATH` variable is inspected by CMake to find potential prefix paths (`/bin` is present in `PATH` before `/usr/bin`). This causes errors because no `/include` directory is present on Linux and this is not checked by the LibXml2 config file. Therefore, disable the inspection of the `PATH` variable by setting `NO_SYSTEM_ENVIRONMENT_PATH` option. See for more details. --- CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d7d80249b..ed36d99d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,7 +117,13 @@ if(GR_USE_BUNDLED_LIBRARIES) # (`ONLY_CMAKE_FIND_ROOT_PATH` option is not inherited to `find_package` calls within the LibXml2 config file) find_package(LibXml2 NO_MODULE ONLY_CMAKE_FIND_ROOT_PATH) else() - find_package(LibXml2 NO_MODULE) + # On RHEL 9 based Linux distributions, `libxml2` is found in `/lib64` by default since the `PATH` variable is + # inspected by CMake to find potential prefix paths (`/bin` is present in `PATH` before `/usr/bin`). This causes + # errors because no `/include` directory is present on Linux and this is not checked by the LibXml2 config file. + # Therefore, disable the inspection of the `PATH` variable by setting `NO_SYSTEM_ENVIRONMENT_PATH` option. + # See for more details. + find_package(LibXml2 NO_MODULE NO_SYSTEM_ENVIRONMENT_PATH) endif() # Find the following packages only in 3rdparty, if `GR_USE_BUNDLED_LIBRARIES` is set From abbf5806553c5698ee77037135404fbd47af7090 Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Mon, 17 Jun 2024 17:15:57 +0200 Subject: [PATCH 21/24] GR: do not adjust axis range --- lib/gr/gr.c | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 9ea2eaec5..2488c59fe 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -5337,7 +5337,6 @@ void gr_axis(char which, axis_t *axis) { scale_option = GR_OPTION_X_LOG; base = lx.basex; - if ((lx.scale_options & GR_OPTION_X_LOG) == 0) gr_adjustrange(&x_min, &x_max); if (is_nan(axis->min)) axis->min = x_min; if (is_nan(axis->max)) axis->max = x_max; if (is_nan(axis->org)) axis->org = x_min; @@ -5360,7 +5359,6 @@ void gr_axis(char which, axis_t *axis) { scale_option = GR_OPTION_Y_LOG; base = lx.basey; - if ((lx.scale_options & GR_OPTION_Y_LOG) == 0) gr_adjustrange(&y_min, &y_max); if (is_nan(axis->min)) axis->min = y_min; if (is_nan(axis->max)) axis->max = y_max; if (is_nan(axis->org)) axis->org = y_min; @@ -5465,7 +5463,7 @@ void gr_axis(char which, axis_t *axis) void gr_drawaxis(char which, axis_t *axis) { - int errind, tnr, ltype, clsw; + int errind, tnr, ltype, clsw, halign, valign; double wn[4], vp[4], clrt[4]; double tick, minor_tick, major_tick; int i; @@ -5497,10 +5495,6 @@ void gr_drawaxis(char which, axis_t *axis) pline(axis->max, axis->position); end_pline(); } - if (y_lin(axis->position) <= 0.5 * (y_lin(wn[2] + y_lin(wn[3])))) - gks_set_text_align(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_TOP); - else - gks_set_text_align(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_BOTTOM); } else { @@ -5513,10 +5507,6 @@ void gr_drawaxis(char which, axis_t *axis) pline(axis->position, axis->max); end_pline(); } - if (x_lin(axis->position) <= 0.5 * (x_lin(wn[0] + x_lin(wn[1])))) - gks_set_text_align(GKS_K_TEXT_HALIGN_RIGHT, GKS_K_TEXT_VALIGN_HALF); - else - gks_set_text_align(GKS_K_TEXT_HALIGN_LEFT, GKS_K_TEXT_VALIGN_HALF); } for (i = 0; i < axis->num_ticks; i++) @@ -5538,12 +5528,35 @@ void gr_drawaxis(char which, axis_t *axis) if (axis->major_count > 0) { - for (i = 0; i < axis->num_tick_labels; i++) + if (axis->num_tick_labels > 0) { + /* save text alignment */ + gks_inq_text_align(&errind, &halign, &valign); + if (which == 'X') - text2d(axis->tick_labels[i].tick, axis->label_position, axis->tick_labels[i].label); + { + if (axis->position <= wn[2]) + gks_set_text_align(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_TOP); + else + gks_set_text_align(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_BOTTOM); + } else - text2d(axis->label_position, axis->tick_labels[i].tick, axis->tick_labels[i].label); + { + if (axis->position <= wn[0]) + gks_set_text_align(GKS_K_TEXT_HALIGN_RIGHT, GKS_K_TEXT_VALIGN_HALF); + else + gks_set_text_align(GKS_K_TEXT_HALIGN_LEFT, GKS_K_TEXT_VALIGN_HALF); + } + for (i = 0; i < axis->num_tick_labels; i++) + { + if (which == 'X') + text2d(axis->tick_labels[i].tick, axis->label_position, axis->tick_labels[i].label); + else + text2d(axis->label_position, axis->tick_labels[i].tick, axis->tick_labels[i].label); + } + + /* restore text alignment */ + gks_set_text_align(halign, valign); } } From ce6d9a7238d042264f813a408b8da85768bc8a40 Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Tue, 18 Jun 2024 07:55:36 +0200 Subject: [PATCH 22/24] GR: fix calculation of number of ticks and labels --- lib/gr/gr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 2488c59fe..63217469e 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -5458,6 +5458,8 @@ void gr_axis(char which, axis_t *axis) i++; a = i * axis->tick; } + axis->num_ticks = j; + axis->num_tick_labels = k; } } From b253d9094f112ab5205908ef9bd1fdef5f94cd07 Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Tue, 18 Jun 2024 08:51:45 +0200 Subject: [PATCH 23/24] GR: fix usage of major tick count --- lib/gr/gr.c | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 63217469e..325975e58 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -5429,11 +5429,19 @@ void gr_axis(char which, axis_t *axis) else { if (is_nan(axis->tick)) axis->tick = gr_tick(axis->min, axis->max); - if (axis->major_count == 0) axis->major_count = 1; + if (axis->major_count > 0) axis->tick /= axis->major_count; axis->num_ticks = (int)((axis->max - axis->min) / axis->tick + 0.5) + 1; axis->ticks = (double *)xcalloc(axis->num_ticks, sizeof(tick_t)); - axis->num_tick_labels = axis->num_ticks / axis->major_count; - axis->tick_labels = (tick_label_t *)xcalloc(axis->num_tick_labels, sizeof(tick_label_t)); + if (axis->major_count > 0) + { + axis->num_tick_labels = axis->num_ticks / axis->major_count; + axis->tick_labels = (tick_label_t *)xcalloc(axis->num_tick_labels, sizeof(tick_label_t)); + } + else + { + axis->num_tick_labels = 0; + axis->tick_labels = NULL; + } epsilon = FEPS * (axis->max - axis->min); @@ -5443,17 +5451,24 @@ void gr_axis(char which, axis_t *axis) while (a <= axis->max + epsilon) { axis->ticks[j].value = a; - axis->ticks[j++].is_major = (i % axis->major_count == 0); - gr_getformat(&formatReference, axis->min, a, axis->max, axis->tick, axis->major_count); - if (i % axis->major_count == 0) + if (axis->major_count > 0) { - s = (char *)xcalloc(256, sizeof(char)); - gr_ftoa(s, a, &formatReference); - axis->tick_labels[k].tick = a; - axis->tick_labels[k].label = replace_minus_sign(s); - gr_inqtext(0, 0, axis->tick_labels[k].label, tbx, tby); - axis->tick_labels[k].width = tbx[2] - tbx[0]; - k++; + axis->ticks[j++].is_major = (i % axis->major_count == 0); + gr_getformat(&formatReference, axis->min, a, axis->max, axis->tick, axis->major_count); + if (i % axis->major_count == 0) + { + s = (char *)xcalloc(256, sizeof(char)); + gr_ftoa(s, a, &formatReference); + axis->tick_labels[k].tick = a; + axis->tick_labels[k].label = replace_minus_sign(s); + gr_inqtext(0, 0, axis->tick_labels[k].label, tbx, tby); + axis->tick_labels[k].width = tbx[2] - tbx[0]; + k++; + } + } + else + { + axis->ticks[j++].is_major = 1; } i++; a = i * axis->tick; @@ -5688,7 +5703,10 @@ void gr_freeaxis(axis_t *axis) { free(axis->tick_labels[i].label); } - free(axis->tick_labels); + if (axis->tick_labels != NULL) + { + free(axis->tick_labels); + } free(axis->ticks); } From ccff069f6464867b8ed900eaefed63cbcb3e0da5 Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Tue, 18 Jun 2024 12:05:22 +0200 Subject: [PATCH 24/24] GR: ensure that sufficient memory has been allocated --- lib/gr/gr.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 325975e58..eb460cf49 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -5434,7 +5434,7 @@ void gr_axis(char which, axis_t *axis) axis->ticks = (double *)xcalloc(axis->num_ticks, sizeof(tick_t)); if (axis->major_count > 0) { - axis->num_tick_labels = axis->num_ticks / axis->major_count; + axis->num_tick_labels = (int)(axis->num_ticks / axis->major_count + 0.5) + 1; axis->tick_labels = (tick_label_t *)xcalloc(axis->num_tick_labels, sizeof(tick_label_t)); } else @@ -5476,6 +5476,15 @@ void gr_axis(char which, axis_t *axis) axis->num_ticks = j; axis->num_tick_labels = k; } + + if (axis->num_ticks > 0) + { + axis->ticks = (tick_t *)xrealloc(axis->ticks, axis->num_ticks * sizeof(tick_t)); + } + if (axis->num_tick_labels > 0) + { + axis->tick_labels = (tick_label_t *)xrealloc(axis->tick_labels, axis->num_tick_labels * sizeof(tick_label_t)); + } } void gr_drawaxis(char which, axis_t *axis)