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' 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; 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/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 diff --git a/lib/gr/gr.c b/lib/gr/gr.c index fd9803942..eb460cf49 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -5307,6 +5307,418 @@ 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, 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 (is_nan(axis->tick_size)) axis->tick_size = 0.0075; + + if (which == 'X') + { + scale_option = GR_OPTION_X_LOG; + base = lx.basex; + 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 (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 (scale_option & lx.scale_options) + { + axis->num_tick_labels = igauss(blog(base, axis->max / axis->min)) + 2; + 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)); + + a0 = pow(base, gauss(blog(base, axis->min))); + i = ipred(axis->min / a0); + a = a0 + i * a0; + decade = igauss(blog(base, axis->min / axis->org)); + j = k = 0; + while (a <= 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_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++; + } + } + } + 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 + { + if (is_nan(axis->tick)) axis->tick = gr_tick(axis->min, axis->max); + 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)); + if (axis->major_count > 0) + { + 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 + { + axis->num_tick_labels = 0; + axis->tick_labels = NULL; + } + + epsilon = FEPS * (axis->max - axis->min); + + i = isucc(axis->min / axis->tick); + a = i * axis->tick; + j = k = 0; + while (a <= axis->max + epsilon) + { + axis->ticks[j].value = a; + if (axis->major_count > 0) + { + 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; + } + 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) +{ + int errind, tnr, ltype, clsw, halign, valign; + double wn[4], vp[4], clrt[4]; + double tick, minor_tick, major_tick; + 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 (which == 'X') + { + 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); + if (axis->draw_axis_line) + { + start_pline(axis->min, axis->position); + pline(axis->max, axis->position); + end_pline(); + } + } + 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); + if (axis->draw_axis_line) + { + start_pline(axis->position, axis->min); + pline(axis->position, axis->max); + end_pline(); + } + } + + 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->position); + pline(axis->ticks[i].value, tick); + end_pline(); + } + else + { + pline(axis->position, axis->ticks[i].value); + pline(tick, axis->ticks[i].value); + end_pline(); + } + } + + if (axis->major_count > 0) + { + if (axis->num_tick_labels > 0) + { + /* save text alignment */ + gks_inq_text_align(&errind, &halign, &valign); + + if (which == 'X') + { + 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 + { + 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); + } + } + + /* 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 + 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; + 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) + { + if (x_axis != NULL) draw_axis_grid('X', x_axis); + if (y_axis != NULL) draw_axis_grid('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; + + 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 */ + + gks_set_pline_linetype(ltype); + gks_set_clipping(clsw); +} + +void gr_freeaxis(axis_t *axis) +{ + int i; + for (i = 0; i < axis->num_tick_labels; i++) + { + free(axis->tick_labels[i].label); + } + if (axis->tick_labels != NULL) + { + free(axis->tick_labels); + } + 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 f423bf7e5..962ffa95e 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; @@ -172,6 +180,37 @@ 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 +{ + double min, max; + double tick, org; + double position; + int major_count; + int num_ticks; + tick_t *ticks; + double tick_size; + int num_tick_labels; + tick_label_t *tick_labels; + double label_position; + 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); @@ -257,6 +296,10 @@ 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_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); DLLEXPORT void gr_verrorbars(int, double *, double *, double *, double *); @@ -338,7 +381,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++) diff --git a/lib/grm/grplot/README.md b/lib/grm/grplot/README.md index 0e631680b..f920b2b55 100644 --- a/lib/grm/grplot/README.md +++ b/lib/grm/grplot/README.md @@ -21,12 +21,20 @@ 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. - `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` @@ -37,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. @@ -101,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. @@ -135,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. 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`}}` + `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. @@ -233,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. 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`}}` + `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 @@ -276,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 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`}}` + `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. @@ -372,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. 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`}}` + `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 140f87fe9..b78e14c20 100644 --- a/lib/grm/grplot/grplot_widget.cxx +++ b/lib/grm/grplot/grplot_widget.cxx @@ -148,18 +148,17 @@ 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", "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", @@ -183,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); @@ -371,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) { @@ -394,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); @@ -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(); } @@ -689,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) { @@ -699,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) { @@ -745,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))); @@ -1122,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") { @@ -1871,6 +1888,7 @@ void GRPlotWidget::resizeEvent(QResizeEvent *event) mouse_move_selection = nullptr; amount_scrolled = 0; clicked.clear(); + tooltips.clear(); reset_pixmap(); } @@ -2500,7 +2518,7 @@ void GRPlotWidget::show_bounding_boxes_slot() } } -void GRPlotWidget::open_file_slot() +void GRPlotWidget::load_file_slot() { if (enable_editor) { @@ -2903,6 +2921,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/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/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/include/grm/dom_render/render.hxx b/lib/grm/include/grm/dom_render/render.hxx index 02c3928c9..ba535e322 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,14 @@ 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); + + 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 53a5b531b..0ca4d9264 100644 --- a/lib/grm/src/grm/dom_render/graphics_tree/schema.xsd +++ b/lib/grm/src/grm/dom_render/graphics_tree/schema.xsd @@ -120,8 +120,8 @@ + - @@ -144,6 +144,7 @@ + @@ -156,8 +157,6 @@ - - @@ -179,7 +178,6 @@ - @@ -233,23 +231,42 @@ - - + + + + + + + + + + + + + + + + + + + + + + + - - - + @@ -261,6 +278,19 @@ + + + + + + + + + + + + + @@ -385,6 +415,7 @@ + @@ -424,7 +455,7 @@ - + @@ -514,13 +545,13 @@ - + - + @@ -1189,16 +1220,16 @@ - + + + - - - + @@ -1351,12 +1382,10 @@ - - diff --git a/lib/grm/src/grm/dom_render/render.cxx b/lib/grm/src/grm/dom_render/render.cxx index fd62e0385..800b319c1 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", }; @@ -213,6 +215,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 +462,19 @@ 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", "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); @@ -474,8 +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 (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", + "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 { @@ -496,7 +505,8 @@ 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", "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); @@ -504,7 +514,8 @@ 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", "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); @@ -585,12 +596,20 @@ 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(); + } + } } } } else if (*del == del_values::recreate_all_children) { - if (!(element->localName() == "marginal_heatmap_plot" && child->localName() != "central_region")) + if (!(element->localName() == "marginal_heatmap_plot" && + (child->localName() != "central_region" || child->localName() != "side_region"))) child->remove(); } } @@ -601,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" && @@ -614,11 +633,34 @@ static void clearOldChildren(del_values *del, const std::shared_ptrlocalName() != "central_region" && child->localName() != "text" && - element->localName() == "marginal_heatmap_plot") + if (element->localName() == "marginal_heatmap_plot") { - only_central_region_child = false; - break; + 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_region") + { + only_non_marginal_heatmap_children = false; + break; + } + } + } + else if (child->localName() == "central_region") + { + 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" && element->localName() != "coordinate_system" && element->localName() != "marginal_heatmap_plot") @@ -630,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; } } @@ -678,30 +722,77 @@ 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) + 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; + bool left_text_margin = false, right_text_margin = false, bottom_text_margin = false, top_text_margin = false; + bool top_text_is_title = false; 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 = 0.0, bottom_margin = 0.0, top_margin = 0.0; double viewport[4] = {0.0, 0.0, 0.0, 0.0}; - 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")); - - 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")); + only_quadratic_aspect_ratio = static_cast(plot_parent->getAttribute("only_quadratic_aspect_ratio")); + + 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()) { @@ -712,8 +803,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")) { @@ -733,26 +861,39 @@ 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) + top_margin = (top_text_margin ? top_text_is_title ? 0.075 : 0.05 : 0.0); + 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); @@ -799,20 +934,10 @@ static void calculateCentralRegionMarginOrDiagFactor(const std::shared_ptr(element->getAttribute("cols")); auto rows = static_cast(element->getAttribute("rows")); @@ -870,7 +995,7 @@ 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(); @@ -900,7 +1124,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 +1135,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 +1154,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]); @@ -976,13 +1178,228 @@ 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; + 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; + bool keep_aspect_ratio = false, uniform_data = true, only_quadratic_aspect_ratio = false; + + auto plot_parent = element; + getPlotParent(plot_parent); + + if (element->hasAttribute("location")) location = static_cast(element->getAttribute("location")); + + // 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 + 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")); + kind = static_cast(plot_parent->getAttribute("kind")); + + 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") && !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) + { + offset = 0.0; + 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 (kind != "imshow") + { + 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]); + } + } + + 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); + + 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 (location == "top") + { + 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")) + offset += static_cast(element->parentElement()->getAttribute("width")); + } + if (location == "left") + { + 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]); + } + } + + 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) + { + 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 + { + 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); + element->setAttribute("_viewport_y_min_org", vp_y_min); + element->setAttribute("_viewport_y_max_org", vp_y_max); + } + else if (element->localName() == "marginal_heatmap_plot") + { + 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); + 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()) { @@ -993,129 +1410,157 @@ static void calculateViewport(const std::shared_ptr &element) } } - auto plot_parent = central_region; - getPlotParent(plot_parent); - - if (element->hasAttribute("location")) location = static_cast(element->getAttribute("location")); - - // 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("_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 (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 (element->hasAttribute("offset")) offset = static_cast(element->getAttribute("offset")); - if (element->hasAttribute("width")) width = static_cast(element->getAttribute("width")); + 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")); - GRM::Render::getFigureSize(&pixel_width, &pixel_height, nullptr, nullptr); - auto aspect_ratio_ws = (double)pixel_width / pixel_height; + scale_factor = diag_factor * default_diag_factor; + } + element->setAttribute("_scale_factor", scale_factor); - if (location == "right") + if (kind != "pie") { - if (aspect_ratio_ws <= 1) + legendSize(labels, &w, &h); + if (element->hasAttribute("_start_w")) { - offset_rel = offset * aspect_ratio_ws; - width_rel = width * aspect_ratio_ws; + w = static_cast(element->getAttribute("_start_w")); } else { - offset_rel = offset / aspect_ratio_ws; - width_rel = width / aspect_ratio_ws; + element->setAttribute("_start_w", w); } - 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") - { - if (aspect_ratio_ws <= 1) + if (element->hasAttribute("_start_h")) { - offset_rel = offset * aspect_ratio_ws; - width_rel = width * aspect_ratio_ws; + h = static_cast(element->getAttribute("_start_h")); } else { - offset_rel = offset / aspect_ratio_ws; - width_rel = width / aspect_ratio_ws; + element->setAttribute("_start_h", h); } - 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); - 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) + + 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)) { - offset_rel = offset * aspect_ratio_ws; - width_rel = width * aspect_ratio_ws; + px = viewport[0] + 0.11 * scale_factor; } else { - offset_rel = offset / aspect_ratio_ws; - width_rel = width / aspect_ratio_ws; + 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; } - 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 == 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 if (location == "bottom") + else { - if (aspect_ratio_ws <= 1) + 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")) { - offset_rel = offset * aspect_ratio_ws; - width_rel = width * aspect_ratio_ws; + h = static_cast(element->getAttribute("_start_h")); } else { - offset_rel = offset / aspect_ratio_ws; - width_rel = width / aspect_ratio_ws; + element->setAttribute("_start_h", h); } - min_vp = getMinViewport(element, false); - render->setViewport(element, viewport[0], viewport[1], grm_max(viewport[2], min_vp), - viewport[2] + offset_rel + width_rel); - 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); + + 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; } - } - 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")); - 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); - } - else if (element->localName() == "marginal_heatmap_plot") - { - 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); @@ -1145,7 +1590,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 +1660,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 +1697,8 @@ static void applyMoveTransformation(const std::shared_ptr &element private_shift = true; } + 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); vp_border_x_max = getMaxViewport(element, true); @@ -1317,7 +1762,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 +2339,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) + { + 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) { - tick_size_rel = tick_size * aspect_ratio_ws; + 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 +3026,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 +3042,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()) { @@ -3192,6 +3697,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` */ } @@ -3429,11 +3938,12 @@ 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") { - 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 @@ -3610,7 +4120,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")); @@ -3804,7 +4314,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 { @@ -4007,38 +4518,34 @@ 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; - 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() : 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")); + 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 +4554,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 +4578,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); @@ -4307,10 +4848,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); } } } @@ -4367,52 +4910,6 @@ std::string tickOrientationIntToString(int tick_orientation) return "down"; } -static void processTitle(const std::shared_ptr &element) -{ - double viewport[4]; - del_values del = del_values::update_without_default; - - 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")); - - double x = 0.5 * (viewport[0] + viewport[1]); // prev viewport from central_region - double y = viewport[3]; - auto title = static_cast(element->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")); - 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\"]"); - 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); - if (static_cast(element->getAttribute("kind")) == "marginal_heatmap") - { - element->querySelectors("marginal_heatmap_plot")->append(title_elem); - } - else - { - element->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"))); @@ -4436,7 +4933,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 +4948,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 +4994,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 +5011,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 +5031,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 +5058,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,112 +5104,21 @@ void GRM::Render::calculateCharHeight(const std::shared_ptr &eleme } char_height *= diag_factor; - if (aspect_ratio_ws > 1) - { - char_height /= aspect_ratio_ws; - } - else - { - char_height *= aspect_ratio_ws; - } - - plot_parent->setAttribute("char_height", char_height); - processCharHeight(plot_parent); -} - -static void processXlabel(const std::shared_ptr &element) -{ - double viewport[4], plot_viewport[4], char_height; - del_values del = del_values::update_without_default; - std::shared_ptr plot_parent = element->parentElement(); - - getPlotParent(plot_parent); - auto coordinate_system = element->parentElement(); - 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")); - - double x = 0.5 * (viewport[0] + viewport[1]); - double y = plot_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())) + if ((keep_aspect_ratio && uniform_data && only_quadratic_aspect_ratio) || !keep_aspect_ratio) { - del = del_values(static_cast(element->getAttribute("_delete_children"))); - auto xlabel_elem = element->querySelectors("[name=\"x_label\"]"); - - if ((del != del_values::update_without_default && del != del_values::update_with_default) || - xlabel_elem == nullptr) + if (aspect_ratio_ws > 1) { - 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); + char_height /= aspect_ratio_ws; } - else if (xlabel_elem != nullptr) + else { - render->createText(x, y, x_label, CoordinateSpace::NDC, xlabel_elem); + char_height *= aspect_ratio_ws; } - if (xlabel_elem != nullptr) xlabel_elem->setAttribute("name", "x_label"); + if (!element->hasAttribute("_char_height_set_by_user")) char_height *= DEFAULT_ASPECT_RATIO_FOR_SCALING; } -} - -static void processYlabel(const std::shared_ptr &element) -{ - double viewport[4], plot_viewport[4], char_height; - bool keep_aspect_ratio; - del_values del = del_values::update_without_default; - std::shared_ptr plot_parent = element->parentElement(); - - getPlotParent(plot_parent); - auto coordinate_system = element->parentElement(); - 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")); - - double x = ((keep_aspect_ratio) ? 0.925 : 1) * plot_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\"]"); - if ((del != del_values::update_without_default && del != del_values::update_with_default) || - ylabel_elem == nullptr) - { - 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); - } - else if (ylabel_elem != nullptr) - { - render->createText(x, y, y_label, CoordinateSpace::NDC, ylabel_elem); - } - if (ylabel_elem != nullptr) ylabel_elem->setAttribute("name", "y_label"); - } + plot_parent->setAttribute("char_height", char_height); + processCharHeight(plot_parent); } static void processXTickLabels(const std::shared_ptr &element) @@ -4863,7 +5298,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 +5308,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(); @@ -4935,15 +5370,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}, }; @@ -6112,8 +6544,8 @@ 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; - int scale_options, color_upward_scap, color_downward_scap, color_error_bar; + unsigned int i; + 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; @@ -6159,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")); @@ -6209,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")); @@ -6282,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]); } } } @@ -6302,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, color_downward_scap, 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; @@ -6318,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); } @@ -6339,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); } @@ -6357,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); } } @@ -6476,8 +6903,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 +6932,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 +6991,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 +7006,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 +7027,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(); @@ -6844,42 +7232,37 @@ 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]; /* 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 +7270,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); @@ -6925,66 +7307,82 @@ 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(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; } } @@ -7843,7 +8241,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()) @@ -10558,8 +10956,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")); @@ -10598,7 +10996,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; @@ -10630,7 +11028,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 @@ -11423,24 +11821,25 @@ 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); - side_region->append(sub_group); + sub_group->setAttribute("_child_id", child_id++); + auto side_plot_region = global_render->createSidePlotRegion(); + side_region->append(side_plot_region); + side_plot_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,28 +11862,24 @@ 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]"); + 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", 0); - side_region->append(sub_group); + sub_group->setAttribute("_child_id", child_id++); + if (!side_plot_region) side_plot_region = global_render->createSidePlotRegion(); + side_region->append(side_plot_region); + side_plot_region->append(sub_group); } else { @@ -11522,20 +11917,20 @@ 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") { 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); @@ -12019,6 +12414,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")); @@ -12026,6 +12422,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) @@ -12038,8 +12436,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); @@ -12063,10 +12461,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) { /*! @@ -12364,7 +12841,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 +12858,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 +13568,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); + + del = del_values(static_cast(element->getAttribute("_delete_children"))); + clearOldChildren(&del, element); + + if (element->hasAttribute("text_content")) + { + 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")) + { + auto text_elem = global_render->createTextRegion(); + text_elem->setAttribute("_child_id", child_id++); + element->appendChild(text_elem); + } + else + { + 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(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 */ +} + +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, @@ -13153,7 +13664,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); @@ -13410,8 +13921,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); @@ -13648,6 +14157,30 @@ static void processPlot(const std::shared_ptr &element, const std: break; } } + + std::shared_ptr side_region; + auto kind = static_cast(element->getAttribute("kind")); + + if (!element->querySelectors("side_region[location=\"right\"]")) + { + side_region = global_render->createSideRegion("right"); + central_region_parent->append(side_region); + } + if (!element->querySelectors("side_region[location=\"top\"]")) + { + side_region = global_render->createSideRegion("top"); + central_region_parent->append(side_region); + } + if (!element->querySelectors("side_region[location=\"left\"]")) + { + side_region = global_render->createSideRegion("left"); + central_region_parent->append(side_region); + } + 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) @@ -13766,14 +14299,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", "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", "text_region", "side_plot_region")) { bool old_state = automatic_update; automatic_update = false; @@ -13809,6 +14344,9 @@ 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); @@ -13884,7 +14422,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; @@ -14153,6 +14691,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); @@ -14465,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"}}, @@ -14490,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"}}, @@ -14499,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"}}, @@ -14529,7 +15071,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 +15112,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"}}, @@ -14614,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"}}, @@ -14659,12 +15201,11 @@ 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"}}, {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"}}, @@ -14702,7 +15243,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"}}, @@ -14743,7 +15283,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"}}, @@ -15961,6 +16500,29 @@ 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; +} + +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~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -16664,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", @@ -16828,6 +17390,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 +17480,16 @@ 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->hasAttribute("marginal_heatmap_side_plot")) + child->removeAttribute("marginal_heatmap_side_plot"); + } if (child->localName() == "central_region") { for (const auto ¢ral_region_child : child->children()) @@ -16930,21 +17502,32 @@ 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")->parentElement()->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); + // 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 { @@ -16991,20 +17574,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); } @@ -17144,9 +17713,23 @@ 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")); + 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 { @@ -17160,13 +17743,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") { @@ -17210,40 +17840,46 @@ 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 { double offset; int colors; - std::shared_ptr side_region = plot->querySelectors("side_region"), - colorbar = plot->querySelectors("colorbar"); + std::shared_ptr side_region = plot->querySelectors("side_region[location=\"right\"]"), + colorbar = plot->querySelectors("colorbar"), + side_plot = side_region->querySelectors("side_plot_region"); 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); + 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); 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("colorbar")) side_region->append(colorbar); + if (!plot->querySelectors("side_region[location=\"right\"]")) plot->append(side_region); + if (!side_region->querySelectors("side_plot_region")) side_region->append(side_plot); + if (!plot->querySelectors("colorbar")) side_plot->append(colorbar); } } } @@ -17458,6 +18094,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/import.cxx b/lib/grm/src/grm/import.cxx index 005fbc4a4..56fed4a56 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"}, @@ -50,6 +51,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"}, @@ -74,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"}, @@ -110,18 +114,21 @@ 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"}}; -/* ~~~~~~~~~~~~~~~~~~~~~~~~~ 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 ============================================================================== */ @@ -148,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()) { @@ -204,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 */ @@ -439,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; } @@ -539,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; @@ -560,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; } @@ -593,8 +646,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,15 +660,44 @@ 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")) + if (!str_equals_any(kind, "contour", "contourf", "heatmap", "imshow", "marginal_heatmap", "surface", "wireframe")) { - std::vector xi(cols), yi(rows), zi(rows * cols); + // these parameters are only for surface and similar types + use_bins = 0; + xyz_file = 0; + } - if (cols <= 1) + if (str_equals_any(kind, "contour", "contourf", "heatmap", "imshow", "marginal_heatmap", "surface", "wireframe")) + { + 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 @@ -629,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]; } } @@ -652,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) * @@ -702,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; @@ -709,45 +842,129 @@ 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()) { - error_vec[col] = grm_args_new(); - for (i = 0; i < rows; i++) + err = floor(cols / col_group_elem); + error_vec.resize(err); + for (col = 0; col < err; col++) { - errors_up[i] = file_data[depth][col + 1 + col * 2][i]; - errors_down[i] = file_data[depth][col + 2 + col * 2][i]; + error_vec[col] = grm_args_new(); + for (i = 0; i < rows; i++) + { + errors_up[i] = file_data[depth][col + 1 + col * down_err_off][i]; + errors_down[i] = file_data[depth][col + down_err_off + col * down_err_off][i]; + } + grm_args_push(error_vec[col], "relative", "nDD", rows, errors_up.data(), errors_down.data()); + if (grm_args_values(error, "error_bar_color", "i", &color)) + grm_args_push(error_vec[col], "error_bar_color", "i", color); + if (grm_args_values(error, "downwards_cap_color", "i", &color_down)) + grm_args_push(error_vec[col], "downwards_cap_color", "i", color_down); + if (grm_args_values(error, "upwards_cap_color", "i", &color_up)) + grm_args_push(error_vec[col], "upwards_cap_color", "i", color_up); + } + err *= down_err_off; + } + else + { + int cnt = 0; + err = 0; + error_vec.resize(equal_up_and_down_error ? error_data.size() : error_data.size() / 2); + for (i = 0; i < error_vec.size(); i++) + { + error_vec[i] = grm_args_new(); + if (grm_args_values(error, "error_bar_color", "i", &color)) + grm_args_push(error_vec[i], "error_bar_color", "i", color); + if (grm_args_values(error, "downwards_cap_color", "i", &color_down)) + grm_args_push(error_vec[i], "downwards_cap_color", "i", color_down); + if (grm_args_values(error, "upwards_cap_color", "i", &color_up)) + grm_args_push(error_vec[i], "upwards_cap_color", "i", color_up); + } + for (int error_col : error_data) + { + for (i = 0; i < rows; i++) + { + if (equal_up_and_down_error) + { + errors_up[i] = file_data[depth][error_col][i]; + errors_down[i] = file_data[depth][error_col][i]; + } + else if (cnt % 2 == 0) + { + errors_up[i] = file_data[depth][error_col][i]; + } + else if (cnt % 2 != 0) + { + errors_down[i] = file_data[depth][error_col][i]; + } + } + if (cnt % 2 != 0 || equal_up_and_down_error) + { + grm_args_push(error_vec[floor(cnt / (equal_up_and_down_error ? 1 : 2))], "relative", "nDD", + rows, errors_up.data(), errors_down.data()); + } + else + { + filtered_error_columns.push_back(error_col); + } + cnt += 1; } - 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()) { @@ -827,7 +1044,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) { @@ -837,26 +1055,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[row] = ranges.xmin + (ranges.xmax - ranges.xmin) * ((double)row / ((double)rows - 1)); + 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()) + { + 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)) @@ -866,21 +1121,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"); } @@ -898,8 +1165,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()); } @@ -1082,6 +1357,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); @@ -1272,6 +1559,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 { @@ -1374,10 +1675,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/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/interaction.cxx b/lib/grm/src/grm/interaction.cxx index c5a22c8a8..627557c54 100644 --- a/lib/grm/src/grm/interaction.cxx +++ b/lib/grm/src/grm/interaction.cxx @@ -1,5 +1,6 @@ /* ######################### includes ############################################################################### */ +#include #include #include #include @@ -360,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"; @@ -376,7 +377,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); @@ -577,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])) { @@ -590,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])) { @@ -636,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; @@ -650,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; @@ -668,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]; @@ -681,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]; @@ -718,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()) { @@ -739,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"); @@ -990,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); @@ -1234,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; @@ -1283,12 +1279,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")), @@ -1300,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()) { @@ -1312,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(); } } @@ -1544,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])) { @@ -1557,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])) { @@ -1582,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; @@ -1596,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; @@ -1627,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]; @@ -1641,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]; @@ -1697,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 c540f6756..b7d879b0d 100644 --- a/lib/grm/src/grm/plot.cxx +++ b/lib/grm/src/grm/plot.cxx @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -280,6 +281,7 @@ const char *valid_subplot_keys[] = {"abs_height", "location", "major_h", "normalization", + "only_quadratic_aspect_ratio", "orientation", "panzoom", "phi_flip", @@ -417,6 +419,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"}, @@ -2617,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); @@ -3300,7 +3319,19 @@ err_t plot_pie(grm_args_t *subplot_args) } if (grm_args_values(subplot_args, "title", "s", &title)) { - group->parentElement()->setAttribute("title", title); + 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); } global_root->setAttribute("_id", id++); @@ -3592,23 +3623,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)) { @@ -3659,8 +3713,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; } @@ -3671,8 +3733,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 +3761,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 +3777,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 */ @@ -3743,8 +3803,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); @@ -3833,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() @@ -3912,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); } @@ -4108,12 +4170,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); @@ -4654,7 +4718,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; @@ -4672,22 +4736,14 @@ 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); } + 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 ============================================================================== */