From 45eb78138bab6b97a6cb5102a6c7f5d8cedd1057 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Fri, 8 Oct 2021 12:04:27 +0200 Subject: [PATCH 01/22] specfile: disable Qt4 dependency for any RHEL-derived distro >= 8 --- packaging/redhat/gr.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/redhat/gr.spec b/packaging/redhat/gr.spec index f979ea83a..ca3f63a3e 100644 --- a/packaging/redhat/gr.spec +++ b/packaging/redhat/gr.spec @@ -47,7 +47,7 @@ BuildRequires: qt5-local BuildRequires: gcc-local BuildRequires: cmake-local %else -%if 0%{?centos_version} < 800 +%if 0%{?rhel} < 8 BuildRequires: qt-devel %endif %endif From 410d9d63afbf1f88868f17554c30a70f2884d940 Mon Sep 17 00:00:00 2001 From: Malte Deckers Date: Thu, 14 Oct 2021 09:13:00 +0200 Subject: [PATCH 02/22] [gr.js] Fix adjusting canvas size to dpr --- js/jsterm.js | 18 +++++++++++------- js/module.js | 18 +++++------------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/js/jsterm.js b/js/jsterm.js index 20da6d196..5cdb1b145 100644 --- a/js/jsterm.js +++ b/js/jsterm.js @@ -85,7 +85,7 @@ JSTerm = function(ispluto=false) { window.addEventListener('resize', function() { // redraw plots if window zoom changed if (window.devicePixelRatio != Module.dpr) { - for (pid in widgets) { + for (let pid in widgets) { if (typeof(widgets[pid].canvas) !== 'undefined' && document.body.contains(widgets[pid].canvas)) { widgets[pid].draw(); } @@ -180,11 +180,13 @@ JSTerm = function(ispluto=false) { overlay.style = 'position:absolute; top: 0; right: 0; z-index: 2;'; overlay.width = widget.width; overlay.height = widget.height; + overlay.style.width = widget.width + "px"; + overlay.style.height = widget.height + "px"; let canvas = document.createElement('canvas'); canvas.id = 'jsterm-' + widget.id; canvas.style = 'position: absolute; top: 0; right: 0; z-index: 0'; - canvas.width = widget.width; - canvas.height = widget.height; + canvas.style.width = widget.width + "px"; + canvas.style.height = widget.height + "px"; div.appendChild(overlay); div.appendChild(canvas); disp.appendChild(div); @@ -421,10 +423,12 @@ JSTerm = function(ispluto=false) { this.width = width; this.height = height; if (this.canvas !== undefined) { - this.canvas.width = width; - this.canvas.height = height; this.overlayCanvas.width = width; this.overlayCanvas.height = height; + this.canvas.style.width = width + "px"; + this.canvas.style.height = height + "px"; + this.overlayCanvas.style.width = width + "px"; + this.overlayCanvas.style.height = height + "px"; this.div.style = "position: relative; width: " + width + "px; height: " + height + "px;"; } this.draw(); @@ -1116,8 +1120,8 @@ JSTerm = function(ispluto=false) { if (typeof grm === 'undefined') { let canvas = document.createElement('canvas'); canvas.id = 'jsterm-hidden-canvas'; - canvas.width = 640; - canvas.height = 480; + canvas.style.width = '640px'; + canvas.style.height = '480px'; canvas.style = 'display: none;'; document.body.appendChild(canvas); grm = new GRM('jsterm-hidden-canvas'); diff --git a/js/module.js b/js/module.js index 602b345b5..8d967a4a3 100644 --- a/js/module.js +++ b/js/module.js @@ -35,22 +35,14 @@ var Module = { } }, set_dpr: function() { - var _dpr = window.devicePixelRatio || 1 + var _dpr = window.devicePixelRatio || 1; if (!(this.canvas.id in this.dpr_per_canvas)) { - this.dpr_per_canvas[this.canvas.id] = 1; - } - if (this.dpr_per_canvas[this.canvas.id] != _dpr) { - var initial_width = this.canvas.width / this.dpr_per_canvas[this.canvas.id]; - var initial_height = this.canvas.height / this.dpr_per_canvas[this.canvas.id]; - - this.canvas.style.width = initial_width + 'px'; - this.canvas.style.height = initial_height + 'px'; - this.canvas.width = parseInt(initial_width * _dpr, 10); - this.canvas.height = parseInt(initial_height * _dpr, 10); - - this.context.setTransform(_dpr, 0, 0, _dpr, 0, 0); this.dpr_per_canvas[this.canvas.id] = _dpr; } + this.canvas.width = parseInt(this.canvas.clientWidth * _dpr, 10); + this.canvas.height = parseInt(this.canvas.clientHeight * _dpr, 10); + this.context.setTransform(_dpr, 0, 0, _dpr, 0, 0); + this.dpr_per_canvas[this.canvas.id] = _dpr; if (this.dpr != this.dpr_per_canvas[this.canvas.id]) { this.dpr = this.dpr_per_canvas[this.canvas.id]; } From 2d289347e29be6f15df5ebb25b4a97e3055f8f63 Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Fri, 15 Oct 2021 09:30:35 +0200 Subject: [PATCH 03/22] Ignore generated files when building `gr.js` --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 993160c76..c7e23880e 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ gr_version.h Makedefs demo gksm +js/fonts/ +js/gr.js +js/libGR.js From 2947454eaf0ba1d6a98b048d4164c72d872dd8dc Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Fri, 15 Oct 2021 09:36:35 +0200 Subject: [PATCH 04/22] Add the built `gksqt` executable to the `.gitignore` file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c7e23880e..1fd8e5157 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ gksm js/fonts/ js/gr.js js/libGR.js +lib/gks/qt/gksqt From 9c59406de6e38b0c60b76802c4223b0b0c8d2624 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Fri, 15 Oct 2021 10:09:17 +0200 Subject: [PATCH 05/22] packaging: consolidate more RHEL derivate version checks to %rhel --- packaging/redhat/gr.spec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packaging/redhat/gr.spec b/packaging/redhat/gr.spec index ca3f63a3e..67d30b3ca 100644 --- a/packaging/redhat/gr.spec +++ b/packaging/redhat/gr.spec @@ -77,7 +77,7 @@ BuildRequires: libjpeg-turbo-devel %endif # RHEL, CentOS and Scientific Linux 7 have too old cmake version (use prebuild) -%if 0%{?rhel_version} == 700 || 0%{?scientificlinux_version} == 700 || 0%{?centos_version} == 700 +%if 0%{?rhel} == 7 %else BuildRequires: cmake %endif @@ -95,7 +95,7 @@ BuildRequires: wxWidgets-devel %endif # Qt5 BuildRequires for Fedora -%if 0%{?fedora_version} >= 23 || 0%{?centos_version} >= 800 +%if 0%{?fedora_version} >= 23 || 0%{?rhel} >= 8 %define qmake_qt5 qmake-qt5 BuildRequires: qt5-qtbase-devel %endif @@ -123,7 +123,7 @@ mkdir -p %{THIRDPARTY_SRC} %build # RHEL, CentOS and Scientific Linux 7 have too old cmake version (use prebuild) -%if 0%{?rhel_version} == 700 || 0%{?scientificlinux_version} == 700 || 0%{?centos_version} == 700 +%if 0%{?rhel} == 7 export PATH=`pwd`/%{THIRDPARTY}/cmake-3.6.3-Linux-x86_64/bin:$PATH %endif make -C 3rdparty GRDIR=%{grdir} DIR=`pwd`/%{THIRDPARTY} From 7abc4a9606be6e7140d1d0fa8c10e731b84ce54d Mon Sep 17 00:00:00 2001 From: Malte Deckers Date: Tue, 19 Oct 2021 11:24:13 +0200 Subject: [PATCH 06/22] [gr.js] Fix updating dpr * do not use `Module` variables to allow multiple JSTerm objects running in the same module --- js/jsterm.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/js/jsterm.js b/js/jsterm.js index 5cdb1b145..c8545e2a7 100644 --- a/js/jsterm.js +++ b/js/jsterm.js @@ -82,9 +82,13 @@ JSTerm = function(ispluto=false) { var jsterm_ispluto = ispluto; + var dpr = window.devicePixelRatio || 1; + window.addEventListener('resize', function() { // redraw plots if window zoom changed - if (window.devicePixelRatio != Module.dpr) { + let _dpr = window.devicePixelRatio || 1; + if (_dpr != dpr) { + dpr = _dpr; for (let pid in widgets) { if (typeof(widgets[pid].canvas) !== 'undefined' && document.body.contains(widgets[pid].canvas)) { widgets[pid].draw(); From 6a472a509f93eab12663330ceda10d3ebdebc30b Mon Sep 17 00:00:00 2001 From: Malte Deckers Date: Tue, 19 Oct 2021 11:29:19 +0200 Subject: [PATCH 07/22] [gr.js] support dpr != 1 on overlay canvas --- js/jsterm.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/js/jsterm.js b/js/jsterm.js index c8545e2a7..fb42ca7ea 100644 --- a/js/jsterm.js +++ b/js/jsterm.js @@ -182,10 +182,12 @@ JSTerm = function(ispluto=false) { let overlay = document.createElement('canvas'); overlay.id = 'jsterm-overlay-' + widget.id; overlay.style = 'position:absolute; top: 0; right: 0; z-index: 2;'; - overlay.width = widget.width; - overlay.height = widget.height; overlay.style.width = widget.width + "px"; overlay.style.height = widget.height + "px"; + overlay.width = parseInt(widget.width * dpr, 10); + overlay.height = parseInt(widget.height * dpr, 10); + overlay.getContext('2d').setTransform(dpr, 0, 0, dpr, 0, 0); + let canvas = document.createElement('canvas'); canvas.id = 'jsterm-' + widget.id; canvas.style = 'position: absolute; top: 0; right: 0; z-index: 0'; @@ -427,12 +429,13 @@ JSTerm = function(ispluto=false) { this.width = width; this.height = height; if (this.canvas !== undefined) { - this.overlayCanvas.width = width; - this.overlayCanvas.height = height; this.canvas.style.width = width + "px"; this.canvas.style.height = height + "px"; this.overlayCanvas.style.width = width + "px"; this.overlayCanvas.style.height = height + "px"; + this.overlayCanvas.width = parseInt(width * dpr, 10); + this.overlayCanvas.height = parseInt(height * dpr, 10); + this.overlayCanvas.getContext('2d').setTransform(dpr, 0, 0, dpr, 0, 0); this.div.style = "position: relative; width: " + width + "px; height: " + height + "px;"; } this.draw(); From b06a99438837da309f19cf51e98e055eac55a70a Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Wed, 20 Oct 2021 09:39:50 +0200 Subject: [PATCH 08/22] [GRM] Do not check for `y` data when drawing `xticklabels` for bar plots Instead of `y` data, the first data series of a bar plot can also contain an inner series. Since a check for `y` data is not needed in the tick label code, remove it completely to allow tick labels for inner series. --- lib/grm/plot.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/grm/plot.c b/lib/grm/plot.c index 43cb751de..b2eb21909 100644 --- a/lib/grm/plot.c +++ b/lib/grm/plot.c @@ -5171,8 +5171,6 @@ error_t plot_draw_axes(grm_args_t *args, unsigned int pass) if (strcmp("barplot", kind) == 0 && pass == 2) { /* xticklabels */ - grm_args_t **current_series; - args_values(args, "series", "A", ¤t_series); char **xticklabels = NULL; unsigned int xticklabels_length; int i; @@ -5183,14 +5181,11 @@ error_t plot_draw_axes(grm_args_t *args, unsigned int pass) double x1, x2; double x_left = 0, x_right = 1, null; double available_width; - double *y; - unsigned int y_length; const double *window; /* calculate width available for xticknotations */ gr_wctondc(&x_left, &null); gr_wctondc(&x_right, &null); available_width = x_right - x_left; - return_error_if(!args_first_value(*current_series, "y", "D", &y, &y_length), ERROR_PLOT_MISSING_DATA); args_values(args, "window", "D", &window); gr_setcharheight(charheight); gr_settextalign(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_TOP); From bd55d9596b5dd7c4c986f13672697f9e443704b1 Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Wed, 20 Oct 2021 13:22:34 +0200 Subject: [PATCH 09/22] Add `*.pkg` files to the `.gitignore` list --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1fd8e5157..b7ca2f77d 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ dist/ *.dylib *.app *.pc +*.pkg gr*.deb gr*.rpm cmake-build-*/ From 0fd5996f265331538ca90afeb970ee0f51b2e586 Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Fri, 15 Oct 2021 09:38:33 +0200 Subject: [PATCH 10/22] Sort the `.gitignore` entries --- .gitignore | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index b7ca2f77d..6ed07ffb9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,29 +1,29 @@ .* -!.gitignore !.git-blame-ignore-rev !.githooks -build/ -dist/ -*.o +!.gitignore *.a -*.so -*.so.dSYM -*.dylib *.app +*.dylib +*.o *.pc *.pkg -gr*.deb -gr*.rpm -cmake-build-*/ -moc_*.h -moc_*.cpp -qrc_*.cpp -QMakefile -gr_version.h +*.so +*.so.dSYM Makedefs +QMakefile +build/ +cmake-build-*/ demo +dist/ gksm +gr*.deb +gr*.rpm +gr_version.h js/fonts/ js/gr.js js/libGR.js lib/gks/qt/gksqt +moc_*.cpp +moc_*.h +qrc_*.cpp From 3239320ae98afd89348914a2436b53995d3ece31 Mon Sep 17 00:00:00 2001 From: Torben Moll Date: Thu, 21 Oct 2021 09:47:08 +0200 Subject: [PATCH 11/22] Add transparency for NaN pixels in heatmap --- lib/grm/plot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/grm/plot.c b/lib/grm/plot.c index 43cb751de..6257dabaa 100644 --- a/lib/grm/plot.c +++ b/lib/grm/plot.c @@ -3240,7 +3240,7 @@ error_t plot_heatmap(grm_args_t *subplot_args) zv = z[i]; } - if (zv > z_max || zv < z_min) + if (zv > z_max || zv < z_min || isnan(zv)) { data[i] = -1; } From 8f9cf02e540eb093e245211602d2f127b497d473 Mon Sep 17 00:00:00 2001 From: Florian Rhiem Date: Fri, 22 Oct 2021 13:57:45 +0200 Subject: [PATCH 12/22] Change size and positioning of integration symbol in mathtex2 --- lib/gr/mathtex2.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/gr/mathtex2.c b/lib/gr/mathtex2.c index 44c79f487..38690336a 100644 --- a/lib/gr/mathtex2.c +++ b/lib/gr/mathtex2.c @@ -1101,6 +1101,11 @@ static size_t make_char(unsigned int codepoint) return 0; } BoxModelNode bm_node; + double size_factor = 1.16; + if (codepoint == 8747) + { + size_factor *= 1.25; + } bm_node.index = 0; bm_node.type = BT_CHAR; bm_node.u.character.codepoint = codepoint; @@ -1108,10 +1113,15 @@ static size_t make_char(unsigned int codepoint) bm_node.u.character.state = *get_current_state(); double width, height, depth, advance, bearing; if (gks_ft_get_metrics( - MATH_FONT, bm_node.u.character.state.fontsize * 1.16, + MATH_FONT, bm_node.u.character.state.fontsize * size_factor, get_codepoint_for_character_variant(bm_node.u.character.codepoint, bm_node.u.character.state.font), bm_node.u.character.state.dpi, &width, &height, &depth, &advance, &bearing, NULL, NULL, NULL, NULL)) { + if (codepoint == 8747) + { + height *= 0.75; + depth *= 1.25; + } if (codepoint == ' ') { bm_node.u.character.width = advance; @@ -3104,7 +3114,12 @@ static void render_character(BoxModelNode *node, double x, double y) } /* TODO: inquire current workstation window height? */ int window_height = 2400; - gks_set_text_height(node->u.character.state.fontsize / 15.0 * 12 / window_height); + double size_factor = 12 / 15.0 / window_height; + if (codepoint == 8747) + { + size_factor *= 1.25; + } + gks_set_text_height(node->u.character.state.fontsize * size_factor); gks_set_text_fontprec(MATH_FONT, 3); gks_set_text_align(GKS_K_TEXT_HALIGN_LEFT, GKS_K_TEXT_VALIGN_BASE); if (node->u.character.bearing < 0) From 5be07b372f3239eb32ff546abb92d2f4de078c21 Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Sun, 24 Oct 2021 07:36:24 +0200 Subject: [PATCH 13/22] Allow math expressions in GR text function --- lib/gr/gr.c | 365 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 259 insertions(+), 106 deletions(-) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 6da063e01..8a8ab5347 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -202,6 +202,17 @@ typedef struct char *candidate[3]; } font_alias_t; +typedef struct text_node +{ + struct text_node *next; + double x, y; + char *string; + int line_number; + double line_width; + int math; + double width, height; +} text_node_t; + static volume_t vt = {1, 0, 1.25, 1000, 1000, NULL, 1}; static norm_xform nx = {1, 0, 1, 0}; @@ -244,6 +255,8 @@ static double arrow_size = 1; static int flag_printing = 0, flag_graphics = 0; +static text_node_t *text, *head; + #define DEFAULT_FIRST_COLOR 8 #define DEFAULT_LAST_COLOR 79 @@ -1806,153 +1819,246 @@ void gr_polymarker(int n, double *x, double *y) if (flag_graphics) primitive("polymarker", n, x, y); } -/*! - * Draw a text at position `x`, `y` using the current text attributes. - * - * \param[in] x The X coordinate of the starting position of the text string - * \param[in] y The Y coordinate of the starting position of the text string - * \param[in] string The text to be drawn - * - * The values for `x` and `y` are in normalized device coordinates. - * The attributes that control the appearance of text are text font and - * precision, character expansion factor, character spacing, text color index, - * character height, character up vector, text path and text alignment. - */ -void gr_text(double x, double y, char *string) +static void append(double x, double y, char *string, int line_number, int math) { - int errind, tnr, halign, valign, n; - char *s, *t; - double ux, uy, angle, height; - double rx, ry, sx, sy; + text_node_t *prev = text; + int errInd, n, wkId; + double cpx, cpy, tbx[4], tby[4]; + char *src, *dest; - check_autoinit; + if (*string == '\0') return; - gks_inq_current_xformno(&errind, &tnr); - if (tnr != NDC) gks_select_xform(NDC); + text = (text_node_t *)calloc(1, sizeof(text_node_t)); + text->next = NULL; + if (head == NULL) + head = text; + else if (prev != NULL) + prev->next = text; - if (strchr(string, '\n') != NULL) + text->x = x; + text->y = y; + text->string = (char *)calloc(strlen(string) + 1, sizeof(char)); + src = string; + dest = text->string; + while (*src) { - gks_inq_text_align(&errind, &halign, &valign); - gks_inq_text_upvec(&errind, &ux, &uy); - angle = -atan2(ux, uy); - gks_inq_text_height(&errind, &height); - height *= 1.5; + if (*src == '$' && *(src + 1) == '$') src++; + *dest++ = *src++; + } + *dest = '\0'; + text->line_number = line_number; + text->line_width = 0; + text->math = math; - n = 0; - s = string; - while (*s) - if (*s++ == '\n') n++; + gks_inq_open_ws(1, &errInd, &n, &wkId); + if (math) + gr_inqmathtex(0, 0, text->string, tbx, tby); + else + gks_inq_text_extent(wkId, 0, 0, text->string, &errInd, &cpx, &cpy, tbx, tby); - rx = x; - ry = y; - switch (valign) + text->width = tbx[1] - tbx[0]; + text->height = tby[2] - tby[1]; +} + +static text_node_t *parse(double x, double y, char *string) +{ + char *s, *start, *end; + int line_number, math; + + head = text = NULL; + line_number = 1; + math = 0; + + s = (char *)calloc(strlen(string) + 1, sizeof(char)); + strcpy(s, string); + + start = end = s; + while (*end) + { + if (*end == '\n') { - case 3: - rx = x - sin(angle) * 0.5 * n * height; - ry = y + cos(angle) * 0.5 * n * height; - break; - case 4: - case 5: - rx = x - sin(angle) * n * height; - ry = y + cos(angle) * n * height; - break; + math = 0; + *end++ = '\0'; + append(x, y, start, line_number, math); + start = end; + line_number++; } - - t = gks_strdup(string); - n = 0; - s = strtok(t, "\n"); - while (s != NULL) + else if (*end == '$' && *(end + 1) == '$') { - sx = rx + sin(angle) * n * height; - sy = ry - cos(angle) * n * height; - gks_text(sx, sy, s); - s = strtok(NULL, "\n"); - n++; + end += 2; + } + else if (*end == '$') + { + *end++ = '\0'; + append(x, y, start, line_number, math); + math = !math; + start = end; } - free(t); + else + end++; } - else - gks_text(x, y, string); - - if (tnr != NDC) gks_select_xform(tnr); + append(x, y, start, line_number, math); + free(s); - if (flag_graphics) gr_writestream("\n", x, y, string); + return head; } -void gr_inqtext(double x, double y, char *string, double *tbx, double *tby) +static void text_impl(double x, double y, char *string, int inquire, double *tbx, double *tby) { - int errind, tnr, halign, valign; - char *s, *t; - double ux, uy, angle, width = 0.0, height, chh; + int errInd, hAlign, vAlign; + double chuX, chuY, angle, charHeight, xOff, yOff, lineWidth, lineHeight; + text_node_t *textP, *p; + int lineNumber = 1; + double totalWidth = 0, totalHeight = 0; + double xx, yy, sx, sy; int i; - int n, wkid = 0; - double rx, ry, xx, yy, cpx, cpy; - check_autoinit; + gks_inq_text_upvec(&errInd, &chuX, &chuY); + gks_set_text_upvec(0, 1); + angle = -atan2(chuX, chuY); - gks_inq_current_xformno(&errind, &tnr); - if (tnr != NDC) gks_select_xform(NDC); + gks_inq_text_height(&errInd, &charHeight); - gks_inq_open_ws(1, &errind, &n, &wkid); + gks_inq_text_align(&errInd, &hAlign, &vAlign); + gks_set_text_align(GKS_K_TEXT_HALIGN_LEFT, GKS_K_TEXT_VALIGN_HALF); - if (strchr(string, '\n') != NULL) + text = textP = parse(x, y, string); + yOff = 0; + while (textP != NULL) { - gks_inq_text_align(&errind, &halign, &valign); - gks_inq_text_upvec(&errind, &ux, &uy); + lineWidth = 0; + lineHeight = 0; + p = textP; + while (p != NULL) + { + if (p->line_number != lineNumber) break; + lineHeight = max(p->height, lineHeight); + lineWidth += p->width; + p = p->next; + } + xOff = 0; + yOff += 0.5 * lineHeight; - gks_set_text_upvec(0.0, 1.0); + totalWidth = max(totalWidth, lineWidth); + totalHeight += lineHeight; - t = gks_strdup(string); - n = 0; - s = strtok(t, "\n"); - while (s != NULL) + while (textP != NULL && textP->line_number == lineNumber) { - gks_inq_text_extent(wkid, x, y, s, &errind, &cpx, &cpy, tbx, tby); - s = strtok(NULL, "\n"); - width = max(tbx[1] - tbx[0], width); - n++; + textP->x += xOff; + textP->y -= yOff; + + xOff += textP->width; + totalWidth = max(totalWidth, xOff); + + textP->line_width = lineWidth; + textP = textP->next; } - free(t); + yOff += 0.5 * lineHeight; + lineNumber += 1; + } - gks_set_text_upvec(ux, uy); + gks_set_text_upvec(chuX, chuY); - angle = -atan2(ux, uy); - gks_inq_text_height(&errind, &chh); - height = chh * n * 1.5; + if (!inquire) + { + p = text; + while (p != NULL) + { + if (hAlign == 2) + p->x += 0.5 * (totalWidth - p->line_width); + else if (hAlign == 3) + p->x += totalWidth - p->line_width; + p = p->next; + } - rx = x; - switch (halign) + while (text != NULL) + { + text_node_t *next = text->next; + + xx = text->x - x; + switch (hAlign) + { + case 2: + xx -= 0.5 * totalWidth; + break; + case 3: + xx -= totalWidth; + break; + default: + break; + } + yy = text->y - y; + switch (vAlign) + { + case 1: + yy += charHeight * 0.04; + break; + case 3: + yy += 0.5 * totalHeight; + break; + case 4: + yy += totalHeight; + break; + case 5: + yy += totalHeight - charHeight * 0.04; + break; + default: + break; + } + + sx = x + cos(angle) * xx - sin(angle) * yy; + sy = y + sin(angle) * xx + cos(angle) * yy; + + if (text->math) + gr_mathtex(sx, sy, text->string); + else + gks_text(sx, sy, text->string); + + free(text->string); + free(text); + text = next; + } + } + else + { + xx = x; + switch (hAlign) { case 2: - rx -= 0.5 * width; + xx -= 0.5 * totalWidth; break; case 3: - rx -= width; + xx -= totalWidth; + break; + default: break; } - ry = y; - switch (valign) + yy = y; + switch (vAlign) { case 1: - ry -= height - chh * 0.04; - break; - case 2: - ry -= height; + yy += charHeight * 0.04; break; case 3: - ry -= 0.5 * height; + yy += 0.5 * totalHeight; + break; + case 4: + yy += totalHeight; break; case 5: - ry -= chh * 0.04; + yy += totalHeight - charHeight * 0.04; + break; + default: break; } - tbx[0] = rx; - tbx[1] = rx + width; + + tbx[0] = xx; + tby[0] = yy; + tbx[1] = tbx[0] + totalWidth; + tby[1] = tby[0]; tbx[2] = tbx[1]; + tby[2] = tby[1] - totalHeight; tbx[3] = tbx[0]; - tby[0] = ry; - tby[1] = tby[0]; - tby[2] = ry + height; tby[3] = tby[2]; for (i = 0; i < 4; i++) @@ -1962,12 +2068,59 @@ void gr_inqtext(double x, double y, char *string, double *tbx, double *tby) tbx[i] = x + cos(angle) * xx - sin(angle) * yy; tby[i] = y + sin(angle) * xx + cos(angle) * yy; } - - cpx = tbx[1]; - cpy = tby[1]; } + + gks_set_text_align(hAlign, vAlign); +} + +/*! + * Draw a text at position `x`, `y` using the current text attributes. + * + * \param[in] x The X coordinate of the starting position of the text string + * \param[in] y The Y coordinate of the starting position of the text string + * \param[in] string The text to be drawn + * + * The values for `x` and `y` are in normalized device coordinates. + * The attributes that control the appearance of text are text font and + * precision, character expansion factor, character spacing, text color index, + * character height, character up vector, text path and text alignment. + */ +void gr_text(double x, double y, char *string) +{ + int errind, tnr; + + check_autoinit; + + gks_inq_current_xformno(&errind, &tnr); + if (tnr != NDC) gks_select_xform(NDC); + + if (strchr(string, '\n') != NULL && strchr(string, '$')) + text_impl(x, y, string, 0, NULL, NULL); else - gks_inq_text_extent(wkid, x, y, string, &errind, &cpx, &cpy, tbx, tby); + gks_text(x, y, string); + + if (tnr != NDC) gks_select_xform(tnr); + + if (flag_graphics) gr_writestream("\n", x, y, string); +} + +void gr_inqtext(double x, double y, char *string, double *tbx, double *tby) +{ + int errind, tnr, n, wkid, i; + double cpx, cpy; + + check_autoinit; + + gks_inq_current_xformno(&errind, &tnr); + if (tnr != NDC) gks_select_xform(NDC); + + if (strchr(string, '\n') != NULL && strchr(string, '$')) + text_impl(x, y, string, 1, tbx, tby); + else + { + gks_inq_open_ws(1, &errind, &n, &wkid); + gks_inq_text_extent(wkid, x, y, string, &errind, &cpx, &cpy, tbx, tby); + } if (tnr != NDC) { From 7331d496963037984194fab49e6fe0512bf74f5a Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Mon, 25 Oct 2021 11:23:32 +0200 Subject: [PATCH 14/22] [GRM] Fix the calculation of bar plot data length for inner series If the first series of a bar plot is an inner series, the calculation of the `y` data length is trapped in an endless loop. This commit determines the maximum length of all inner series and uses the array length as the `y` data length. --- lib/grm/plot.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/grm/plot.c b/lib/grm/plot.c index 43cb751de..1c0d007b7 100644 --- a/lib/grm/plot.c +++ b/lib/grm/plot.c @@ -2279,7 +2279,7 @@ error_t plot_barplot(grm_args_t *subplot_args) const char *style = "default"; double *y; unsigned int y_length; - unsigned int fixed_y_length; + unsigned int fixed_y_length = 0; grm_args_t **ind_bar_color = NULL; double(*pos_ind_bar_color)[3] = NULL; grm_args_t **ind_edge_color = NULL; @@ -2318,19 +2318,19 @@ error_t plot_barplot(grm_args_t *subplot_args) /* ind_parameter */ /* determine the length of y */ - if (!args_first_value(*current_series, "y", "D", &y, &fixed_y_length)) + while (*current_series != NULL) { - unsigned int temp_y_length = 0; - cleanup_and_set_error_if( - !args_first_value(*current_series, "inner_series", "A", &inner_series, &inner_series_length), - ERROR_PLOT_MISSING_DATA); - while (inner_series != NULL) + if (!args_first_value(*current_series, "y", "D", &y, &y_length)) { - args_first_value(*inner_series, "y", "D", &y, &y_length); - temp_y_length += fixed_y_length; + cleanup_and_set_error_if( + !args_first_value(*current_series, "inner_series", "A", &inner_series, &inner_series_length), + ERROR_PLOT_MISSING_DATA); + y_length = inner_series_length; } - fixed_y_length = temp_y_length; + fixed_y_length = max(y_length, fixed_y_length); + ++current_series; } + /* ind_bar_color */ if (args_values(subplot_args, "ind_bar_color", "A", &ind_bar_color)) { @@ -2467,6 +2467,7 @@ error_t plot_barplot(grm_args_t *subplot_args) } } + args_values(subplot_args, "series", "A", ¤t_series); wfac = 0.9 * bar_width; while (*current_series != NULL) { From 91a9155d1633c7247d2e1916aaa5cb76f81b84aa Mon Sep 17 00:00:00 2001 From: Torben Moll Date: Thu, 21 Oct 2021 14:09:09 +0200 Subject: [PATCH 15/22] Add resample method to subplots --- lib/grm/plot.c | 31 ++++++++++++++ lib/grm/plot_int.h | 2 + lib/grm/test/public_api/CMakeLists.txt | 1 + lib/grm/test/public_api/heatmap.c | 59 ++++++++++++++++++++++++++ 4 files changed, 93 insertions(+) diff --git a/lib/grm/plot.c b/lib/grm/plot.c index 24c6d1eec..51e76044b 100644 --- a/lib/grm/plot.c +++ b/lib/grm/plot.c @@ -246,6 +246,7 @@ const char *valid_subplot_keys[] = {"adjust_xlim", "normalization", "panzoom", "phiflip", + "resample_method", "reset_ranges", "rings", "rotation", @@ -351,6 +352,7 @@ static string_map_entry_t key_to_formats[] = {{"a", "A"}, {"nbins", "i"}, {"panzoom", "D"}, {"raw", "s"}, + {"resample_method", "s|i"}, {"reset_ranges", "i"}, {"rotation", "i"}, {"size", "D|A"}, @@ -900,6 +902,7 @@ void plot_set_attribute_defaults(grm_args_t *plot_args) args_setdefault(*current_subplot, "xgrid", "i", PLOT_DEFAULT_XGRID); args_setdefault(*current_subplot, "ygrid", "i", PLOT_DEFAULT_YGRID); args_setdefault(*current_subplot, "zgrid", "i", PLOT_DEFAULT_ZGRID); + args_setdefault(*current_subplot, "resample_method", "i", PLOT_DEFAULT_RESAMPLE_METHOD); if (strcmp(kind, "heatmap") == 0) { args_setdefault(*current_subplot, "adjust_xlim", "i", 0); @@ -1036,6 +1039,7 @@ error_t plot_pre_subplot(grm_args_t *subplot_args) plot_process_colormap(subplot_args); plot_process_font(subplot_args); + plot_process_resample_method(subplot_args); if (str_equals_any(kind, 2, "polar", "polar_histogram")) { @@ -1082,6 +1086,33 @@ void plot_process_font(grm_args_t *subplot_args) /* TODO: Implement other datatypes for `font` and `font_precision` */ } +void plot_process_resample_method(grm_args_t *subplot_args) +{ + unsigned int resample_method_flag; + if (!args_values(subplot_args, "resample_method", "i", &resample_method_flag)) + { + const char *resample_method_str; + args_values(subplot_args, "resample_method", "s", &resample_method_str); + if (strcmp(resample_method_str, "nearest") == 0) + { + resample_method_flag = GKS_K_RESAMPLE_NEAREST; + } + else if (strcmp(resample_method_str, "linear") == 0) + { + resample_method_flag = GKS_K_RESAMPLE_LINEAR; + } + else if (strcmp(resample_method_str, "lanczos") == 0) + { + resample_method_flag = GKS_K_RESAMPLE_LANCZOS; + } + else + { + resample_method_flag = GKS_K_RESAMPLE_DEFAULT; + } + } + gr_setresamplemethod(resample_method_flag); +} + void plot_process_viewport(grm_args_t *subplot_args) { const char *kind; diff --git a/lib/grm/plot_int.h b/lib/grm/plot_int.h index 15f8337f3..f9942d71a 100644 --- a/lib/grm/plot_int.h +++ b/lib/grm/plot_int.h @@ -56,6 +56,7 @@ 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_RESAMPLE_METHOD GKS_K_RESAMPLE_DEFAULT #define PLOT_DEFAULT_ADJUST_XLIM 1 #define PLOT_DEFAULT_ADJUST_YLIM 1 #define PLOT_DEFAULT_ADJUST_ZLIM 1 @@ -132,6 +133,7 @@ void plot_process_wswindow_wsviewport(grm_args_t *plot_args); error_t plot_pre_subplot(grm_args_t *subplot_args); void plot_process_colormap(grm_args_t *subplot_args); void plot_process_font(grm_args_t *subplot_args); +void plot_process_resample_method(grm_args_t *subplot_args); void plot_process_viewport(grm_args_t *subplot_args); void plot_process_window(grm_args_t *subplot_args); error_t plot_store_coordinate_ranges(grm_args_t *subplot_args); diff --git a/lib/grm/test/public_api/CMakeLists.txt b/lib/grm/test/public_api/CMakeLists.txt index f2f83c60d..2df0b1c25 100644 --- a/lib/grm/test/public_api/CMakeLists.txt +++ b/lib/grm/test/public_api/CMakeLists.txt @@ -37,6 +37,7 @@ foreach(executable_source ${EXECUTABLE_SOURCES}) add_executable("${PROJECT_NAME}_${executable}" "${executable_source}") target_link_libraries("${PROJECT_NAME}_${executable}" PRIVATE grm_shared) target_link_libraries("${PROJECT_NAME}_${executable}" PRIVATE gr_shared) + target_link_libraries("${PROJECT_NAME}_${executable}" PRIVATE gks_shared) target_link_libraries("${PROJECT_NAME}_${executable}" PRIVATE m) target_compile_options("${PROJECT_NAME}_${executable}" PRIVATE ${COMPILER_OPTION_ERROR_IMPLICIT}) set_target_properties( diff --git a/lib/grm/test/public_api/heatmap.c b/lib/grm/test/public_api/heatmap.c index d7b1c6c84..e768814ba 100644 --- a/lib/grm/test/public_api/heatmap.c +++ b/lib/grm/test/public_api/heatmap.c @@ -6,6 +6,7 @@ #include #include "grm.h" +#include "gks.h" #ifndef M_PI #define M_PI 3.14159265358979323846 @@ -130,6 +131,64 @@ static void test_heatmap(void) grm_args_delete(args); + printf("plot a heatmap with x, y z and resample method (nearest)\n"); + args = grm_args_new(); + grm_args_push(args, "x", "nD", X_DIM, x); + grm_args_push(args, "y", "nD", Y_DIM, y); + grm_args_push(args, "z", "nD", X_DIM * Y_DIM, z); + grm_args_push(args, "kind", "s", "heatmap"); + grm_args_push(args, "resample_method", "s", "nearest"); + grm_plot(args); + + printf("Press any key to continue...\n"); + getchar(); + + grm_args_delete(args); + + printf("plot a heatmap with x, y z and resample method (linear)\n"); + args = grm_args_new(); + grm_args_push(args, "x", "nD", X_DIM, x); + grm_args_push(args, "y", "nD", Y_DIM, y); + grm_args_push(args, "z", "nD", X_DIM * Y_DIM, z); + grm_args_push(args, "kind", "s", "heatmap"); + grm_args_push(args, "resample_method", "s", "linear"); + grm_plot(args); + + printf("Press any key to continue...\n"); + getchar(); + + grm_args_delete(args); + + printf("plot a heatmap with x, y z and resample method (lanczos)\n"); + args = grm_args_new(); + grm_args_push(args, "x", "nD", X_DIM, x); + grm_args_push(args, "y", "nD", Y_DIM, y); + grm_args_push(args, "z", "nD", X_DIM * Y_DIM, z); + grm_args_push(args, "kind", "s", "heatmap"); + grm_args_push(args, "resample_method", "s", "lanczos"); + grm_plot(args); + + printf("Press any key to continue...\n"); + getchar(); + + grm_args_delete(args); + + printf("plot a heatmap with x, y z and resample method (custom)\n"); + args = grm_args_new(); + grm_args_push(args, "x", "nD", X_DIM, x); + grm_args_push(args, "y", "nD", Y_DIM, y); + grm_args_push(args, "z", "nD", X_DIM * Y_DIM, z); + grm_args_push(args, "kind", "s", "heatmap"); + grm_args_push(args, "resample_method", "i", + GKS_K_DOWNSAMPLE_HORIZONTAL_NEAREST | GKS_K_DOWNSAMPLE_VERTICAL_LINEAR | + GKS_K_UPSAMPLE_HORIZONTAL_LANCZOS | GKS_K_UPSAMPLE_VERTICAL_NEAREST); + grm_plot(args); + + printf("Press any key to continue...\n"); + getchar(); + + grm_args_delete(args); + printf("plot two non-uniform heatmaps\n"); args = grm_args_new(); x[0] = -3.25; From a415b307aed53855c8ef6afd495aa8728fb9c27b Mon Sep 17 00:00:00 2001 From: Florian Rhiem Date: Mon, 25 Oct 2021 14:24:51 +0200 Subject: [PATCH 16/22] Fixed problem in text parser --- lib/gr/gr.c | 3655 +++++++++++++++++++++++---------------------- lib/gr/mathtex2.c | 11 +- 2 files changed, 1855 insertions(+), 1811 deletions(-) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 8a8ab5347..77b594e4e 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -211,6 +211,7 @@ typedef struct text_node double line_width; int math; double width, height; + double baseline[2]; } text_node_t; static volume_t vt = {1, 0, 1.25, 1000, 1000, NULL, 1}; @@ -1819,710 +1820,681 @@ void gr_polymarker(int n, double *x, double *y) if (flag_graphics) primitive("polymarker", n, x, y); } -static void append(double x, double y, char *string, int line_number, int math) +/*! + * Allows you to specify a polygonal shape of an area to be filled. + * + * \param[in] n The number of points + * \param[in] x A pointer to the X coordinates + * \param[in] y A pointer to the Y coordinates + * + * The attributes that control the appearance of fill areas are fill area + * interior style, fill area style index and fill area color index. + */ +void gr_fillarea(int n, double *x, double *y) { - text_node_t *prev = text; - int errInd, n, wkId; - double cpx, cpy, tbx[4], tby[4]; - char *src, *dest; - - if (*string == '\0') return; - - text = (text_node_t *)calloc(1, sizeof(text_node_t)); - text->next = NULL; - if (head == NULL) - head = text; - else if (prev != NULL) - prev->next = text; - - text->x = x; - text->y = y; - text->string = (char *)calloc(strlen(string) + 1, sizeof(char)); - src = string; - dest = text->string; - while (*src) - { - if (*src == '$' && *(src + 1) == '$') src++; - *dest++ = *src++; - } - *dest = '\0'; - text->line_number = line_number; - text->line_width = 0; - text->math = math; - - gks_inq_open_ws(1, &errInd, &n, &wkId); - if (math) - gr_inqmathtex(0, 0, text->string, tbx, tby); - else - gks_inq_text_extent(wkId, 0, 0, text->string, &errInd, &cpx, &cpy, tbx, tby); + fillarea(n, x, y); - text->width = tbx[1] - tbx[0]; - text->height = tby[2] - tby[1]; + if (flag_graphics) primitive("fillarea", n, x, y); } -static text_node_t *parse(double x, double y, char *string) +/*! + * Display rasterlike images in a device-independent manner. The cell array + * function partitions a rectangle given by two corner points into DIMX X DIMY + * cells, each of them colored individually by the corresponding color index + * of the given cell array. + * + * \param[in] xmin X coordinate of the lower left point of the rectangle + * \param[in] xmax X coordinate of the upper right point of the rectangle + * \param[in] ymin Y coordinate of the lower left point of the rectangle + * \param[in] ymax Y coordinate of the upper right point of the rectangle + * \param[in] dimx X dimension of the color index array + * \param[in] dimy Y dimension of the color index array + * \param[in] scol number of leading columns in the color index array + * \param[in] srow number of leading rows in the color index array + * \param[in] ncol total number of columns in the color index array + * \param[in] nrow total number of rows in the color index array + * \param[in] color color index array + * + * The values for `xmin`, `xmax`, `ymin` and `ymax` are in world coordinates. + */ +void gr_cellarray(double xmin, double xmax, double ymin, double ymax, int dimx, int dimy, int scol, int srow, int ncol, + int nrow, int *color) { - char *s, *start, *end; - int line_number, math; - - head = text = NULL; - line_number = 1; - math = 0; + check_autoinit; - s = (char *)calloc(strlen(string) + 1, sizeof(char)); - strcpy(s, string); + gks_cellarray(x_lin(xmin), y_lin(ymax), x_lin(xmax), y_lin(ymin), dimx, dimy, scol, srow, ncol, nrow, color); - start = end = s; - while (*end) + if (flag_graphics) { - if (*end == '\n') - { - math = 0; - *end++ = '\0'; - append(x, y, start, line_number, math); - start = end; - line_number++; - } - else if (*end == '$' && *(end + 1) == '$') - { - end += 2; - } - else if (*end == '$') - { - *end++ = '\0'; - append(x, y, start, line_number, math); - math = !math; - start = end; - } - else - end++; + gr_writestream("\n"); } - append(x, y, start, line_number, math); - free(s); - - return head; } -static void text_impl(double x, double y, char *string, int inquire, double *tbx, double *tby) +/*! + * Display a two dimensional color index array with nonuniform cell sizes. + * + * \param[in] x X coordinates of the cell edges + * \param[in] y Y coordinates of the cell edges + * \param[in] dimx actual X dimension of the color index array in the memory + * \param[in] dimy actual Y dimension of the color index array in the memory + * \param[in] scol 1-based starting column of color index and x array + * \param[in] srow 1-based starting row of color index and y array + * \param[in] ncol number of columns displayed + * \param[in] nrow number of rows displayed + * \param[in] color color index array + * + * The values for `x` and `y` are in world coordinates. + * + * If `dimx` and `dimy` are positive `x` must contain `dimx`+1 elements and `y` must contain `dimy`+1 elements. The + * elements i and i+1 are respectively the edges of the i-th cell in X and Y direction. + * + * If `dimx` and `dimy` are negative `x` must contain `-dimx` elements and `y` must contain `-dimy` elements. The i-th + * element is the center of the i-th cell, and the first and last elements will also be the edges of the outer cells. + * + * To draw all cells of the color index array use: + * + * gr_nonuniformcellarray(x, y, dimx, dimy, 1, 1, dimx, dimy, color) + * + * `scol` and `srow` can be used to specify a (1-based) starting column and row + * in the `color`, `x` and `y` array. `dimx` and `dimy` specify the actual dimension of the + * arrays in the memory whereof `ncol` and `nrow` values are displayed. + */ +void gr_nonuniformcellarray(double *x, double *y, int dimx, int dimy, int scol, int srow, int ncol, int nrow, + int *color) { - int errInd, hAlign, vAlign; - double chuX, chuY, angle, charHeight, xOff, yOff, lineWidth, lineHeight; - text_node_t *textP, *p; - int lineNumber = 1; - double totalWidth = 0, totalHeight = 0; - double xx, yy, sx, sy; - int i; + int img_data_x, img_data_y, color_x_ind, color_y_ind, color_ind, edges_x = 1, edges_y = 1, size = 2000; + int *img_data; + double x_pos, y_pos, x_size, y_size, *x_orig = NULL, *y_orig = NULL; - gks_inq_text_upvec(&errInd, &chuX, &chuY); - gks_set_text_upvec(0, 1); - angle = -atan2(chuX, chuY); + if (dimx < 0) + { + dimx = -dimx; + edges_x = 0; + x_orig = x; + } + if (dimy < 0) + { + dimy = -dimy; + edges_y = 0; + y_orig = y; + } - gks_inq_text_height(&errInd, &charHeight); + if (scol < 1 || srow < 1 || scol + ncol - 1 > dimx || srow + nrow - 1 > dimy || (!edges_x && ncol < 2) || + (!edges_y && nrow < 2)) + { + fprintf(stderr, "Dimensions of color index array are invalid.\n"); + return; + } - gks_inq_text_align(&errInd, &hAlign, &vAlign); - gks_set_text_align(GKS_K_TEXT_HALIGN_LEFT, GKS_K_TEXT_VALIGN_HALF); + check_autoinit; - text = textP = parse(x, y, string); - yOff = 0; - while (textP != NULL) + scol--; + srow--; + nrow += srow; + ncol += scol; + + if (!edges_x) { - lineWidth = 0; - lineHeight = 0; - p = textP; - while (p != NULL) + x = (double *)gks_malloc(sizeof(double) * (ncol + 1)); + x[scol] = x_orig[scol]; + for (color_x_ind = scol + 1; color_x_ind < ncol; color_x_ind++) { - if (p->line_number != lineNumber) break; - lineHeight = max(p->height, lineHeight); - lineWidth += p->width; - p = p->next; + x[color_x_ind] = 0.5 * (x_orig[color_x_ind] + x_orig[color_x_ind - 1]); } - xOff = 0; - yOff += 0.5 * lineHeight; - - totalWidth = max(totalWidth, lineWidth); - totalHeight += lineHeight; + x[ncol] = x_orig[ncol - 1]; + } - while (textP != NULL && textP->line_number == lineNumber) + if (!edges_y) + { + y = (double *)gks_malloc(sizeof(double) * (nrow + 1)); + y[srow] = y_orig[srow]; + for (color_y_ind = srow + 1; color_y_ind < nrow; color_y_ind++) { - textP->x += xOff; - textP->y -= yOff; - - xOff += textP->width; - totalWidth = max(totalWidth, xOff); - - textP->line_width = lineWidth; - textP = textP->next; + y[color_y_ind] = 0.5 * (y_orig[color_y_ind] + y_orig[color_y_ind - 1]); } - yOff += 0.5 * lineHeight; - lineNumber += 1; + y[nrow] = y_orig[nrow - 1]; } - gks_set_text_upvec(chuX, chuY); - - if (!inquire) + for (color_x_ind = scol; color_x_ind < ncol; color_x_ind++) { - p = text; - while (p != NULL) + if (x[color_x_ind] > x[color_x_ind + 1]) { - if (hAlign == 2) - p->x += 0.5 * (totalWidth - p->line_width); - else if (hAlign == 3) - p->x += totalWidth - p->line_width; - p = p->next; + if (!edges_x) + { + gks_free(x); + } + if (!edges_y) + { + gks_free(y); + } + fprintf(stderr, "x points not sorted in ascending order\n"); + return; } + } - while (text != NULL) + for (color_y_ind = srow; color_y_ind < nrow; color_y_ind++) + { + if (y[color_y_ind] > y[color_y_ind + 1]) { - text_node_t *next = text->next; - - xx = text->x - x; - switch (hAlign) + if (!edges_x) { - case 2: - xx -= 0.5 * totalWidth; - break; - case 3: - xx -= totalWidth; - break; - default: - break; + gks_free(x); } - yy = text->y - y; - switch (vAlign) + if (!edges_y) { - case 1: - yy += charHeight * 0.04; - break; - case 3: - yy += 0.5 * totalHeight; - break; - case 4: - yy += totalHeight; - break; - case 5: - yy += totalHeight - charHeight * 0.04; - break; - default: - break; + gks_free(y); } - - sx = x + cos(angle) * xx - sin(angle) * yy; - sy = y + sin(angle) * xx + cos(angle) * yy; - - if (text->math) - gr_mathtex(sx, sy, text->string); - else - gks_text(sx, sy, text->string); - - free(text->string); - free(text); - text = next; + fprintf(stderr, "y points not sorted in ascending order\n"); + return; } } - else + + x_size = x[ncol] - x[scol]; + y_size = y[nrow] - y[srow]; + img_data = (int *)xmalloc(size * size * sizeof(int)); + + color_y_ind = srow; + for (img_data_y = 0; img_data_y < size; img_data_y++) { - xx = x; - switch (hAlign) - { - case 2: - xx -= 0.5 * totalWidth; - break; - case 3: - xx -= totalWidth; - break; - default: - break; - } - yy = y; - switch (vAlign) + y_pos = y[srow] + img_data_y * y_size / size; + while (color_y_ind < nrow && y[color_y_ind + 1] <= y_pos) { - case 1: - yy += charHeight * 0.04; - break; - case 3: - yy += 0.5 * totalHeight; - break; - case 4: - yy += totalHeight; - break; - case 5: - yy += totalHeight - charHeight * 0.04; - break; - default: - break; + color_y_ind++; } - - tbx[0] = xx; - tby[0] = yy; - tbx[1] = tbx[0] + totalWidth; - tby[1] = tby[0]; - tbx[2] = tbx[1]; - tby[2] = tby[1] - totalHeight; - tbx[3] = tbx[0]; - tby[3] = tby[2]; - - for (i = 0; i < 4; i++) + color_x_ind = scol; + for (img_data_x = 0; img_data_x < size; img_data_x++) { - xx = tbx[i] - x; - yy = tby[i] - y; - tbx[i] = x + cos(angle) * xx - sin(angle) * yy; - tby[i] = y + sin(angle) * xx + cos(angle) * yy; + x_pos = x[scol] + img_data_x * x_size / size; + while (color_x_ind < ncol && x[color_x_ind + 1] <= x_pos) + { + color_x_ind++; + } + color_ind = color[color_y_ind * dimx + color_x_ind]; + if (color_ind >= 0 && color_ind < MAX_COLOR) + { + img_data[img_data_y * size + img_data_x] = (255 << 24) + rgb[color_ind]; + } + else + { + /* invalid color indices in input data result in transparent pixel */ + img_data[img_data_y * size + img_data_x] = 0; + } } } - gks_set_text_align(hAlign, vAlign); + gr_drawimage(x[scol], x[ncol], y[nrow], y[srow], size, size, img_data, 0); + free(img_data); + if (!edges_x) + { + gks_free(x); + } + if (!edges_y) + { + gks_free(y); + } } /*! - * Draw a text at position `x`, `y` using the current text attributes. + * Display a two dimensional color index array mapped to a disk using polar + * coordinates. * - * \param[in] x The X coordinate of the starting position of the text string - * \param[in] y The Y coordinate of the starting position of the text string - * \param[in] string The text to be drawn + * \param[in] x_org X coordinate of the disk center in world coordinates + * \param[in] y_org Y coordinate of the disk center in world coordinates + * \param[in] phimin start angle of the disk sector in degrees + * \param[in] phimax end angle of the disk sector in degrees + * \param[in] rmin inner radius of the punctured disk in world coordinates + * \param[in] rmax outer radius of the disk in world coordinates + * \param[in] dimphi Phi (X) dimension of the color index array + * \param[in] dimr R (Y) dimension of the color index array + * \param[in] scol number of leading columns in the color index array + * \param[in] srow number of leading rows in the color index array + * \param[in] ncol total number of columns in the color index array + * \param[in] nrow total number of rows in the color index array + * \param[in] color color index array + * + * The two dimensional color index array is mapped to the resulting image by + * interpreting the X-axis of the array as the angle and the Y-axis as the radius. + * The center point of the resulting disk is located at `x_org`, `y_org` and the + * radius of the disk is `rmax`. + * + * To draw a contiguous array as a complete disk use: + * + * gr_polarcellarray(x_org, y_org, 0, 360, 0, rmax, dimphi, dimr, 1, 1, dimphi, dimr, color) + * + * The additional parameters to the function can be used to further control the + * mapping from polar to cartesian coordinates. + * + * If `rmin` is greater than 0 the input data is mapped to a punctured disk (or + * annulus) with an inner radius of `rmin` and an outer radius `rmax`. If `rmin` + * is greater than `rmax` the Y-axis of the array is reversed. + * + * The parameter `phimin` and `phimax` can be used to map the data to a sector + * of the (punctured) disk starting at `phimin` and ending at `phimax`. If + * `phimin` is greater than `phimax` the X-axis is reversed. The visible sector + * is the one starting in mathematically positive direction (counterclockwise) + * at the smaller angle and ending at the larger angle. An example of the four + * possible options can be found below: + * + * \verbatim embed:rst:leading-asterisk + * + * +-----------+-----------+---------------------------------------------------+ + * |**phimin** |**phimax** |**Result** | + * +-----------+-----------+---------------------------------------------------+ + * |90 |270 |Left half visible, mapped counterclockwise | + * +-----------+-----------+---------------------------------------------------+ + * |270 |90 |Left half visible, mapped clockwise | + * +-----------+-----------+---------------------------------------------------+ + * |-90 |90 |Right half visible, mapped counterclockwise | + * +-----------+-----------+---------------------------------------------------+ + * |90 |-90 |Right half visible, mapped clockwise | + * +-----------+-----------+---------------------------------------------------+ + * + * \endverbatim + * + * `scol` and `srow` can be used to specify a (1-based) starting column and row + * in the `color` array. `ncol` and `nrow` specify the actual dimension of the + * array in the memory whereof `dimphi` and `dimr` values are mapped to the disk. * - * The values for `x` and `y` are in normalized device coordinates. - * The attributes that control the appearance of text are text font and - * precision, character expansion factor, character spacing, text color index, - * character height, character up vector, text path and text alignment. */ -void gr_text(double x, double y, char *string) +void gr_polarcellarray(double x_org, double y_org, double phimin, double phimax, double rmin, double rmax, int dimphi, + int dimr, int scol, int srow, int ncol, int nrow, int *color) { - int errind, tnr; - - check_autoinit; - - gks_inq_current_xformno(&errind, &tnr); - if (tnr != NDC) gks_select_xform(NDC); + int x, y, color_ind, r_ind, phi_ind, phi_reverse, phi_wrapped_reverse, r_reverse, size = 2000; + int *img_data; + double r, phi, tmp, center = size / 2.; - if (strchr(string, '\n') != NULL && strchr(string, '$')) - text_impl(x, y, string, 0, NULL, NULL); - else - gks_text(x, y, string); + phimin = arc(phimin); + phimax = arc(phimax); - if (tnr != NDC) gks_select_xform(tnr); + if (!(scol >= 1 && srow >= 1 && scol + ncol - 1 <= dimphi && srow + nrow - 1 <= dimr)) + { + fprintf(stderr, "Dimensions of color index array are invalid.\n"); + return; + } - if (flag_graphics) gr_writestream("\n", x, y, string); -} + if (phimin == phimax) + { + fprintf(stderr, "Invalid angles specified.\n"); + return; + } -void gr_inqtext(double x, double y, char *string, double *tbx, double *tby) -{ - int errind, tnr, n, wkid, i; - double cpx, cpy; + if (rmin == rmax || rmin < 0 || rmax < 0) + { + fprintf(stderr, "Invalid radii specified.\n"); + return; + } check_autoinit; - gks_inq_current_xformno(&errind, &tnr); - if (tnr != NDC) gks_select_xform(NDC); - - if (strchr(string, '\n') != NULL && strchr(string, '$')) - text_impl(x, y, string, 1, tbx, tby); - else + if ((r_reverse = rmin > rmax)) { - gks_inq_open_ws(1, &errind, &n, &wkid); - gks_inq_text_extent(wkid, x, y, string, &errind, &cpx, &cpy, tbx, tby); + tmp = rmin; + rmin = rmax; + rmax = tmp; } - if (tnr != NDC) - { - gks_select_xform(tnr); + phi_reverse = phimin > phimax; - for (i = 0; i < 4; i++) + /* Wrap phimin and phimax to [0, 2Pi] */ + phimin -= floor(phimin / M_PI / 2) * 2 * M_PI; + phimax -= floor(phimax / M_PI / 2) * 2 * M_PI; + + if (fabs(phimin - phimax) < 1e-8) /* Avoid empty images when drawing a full circle */ + { + if (!phi_reverse) { - tbx[i] = (tbx[i] - nx.b) / nx.a; - tby[i] = (tby[i] - nx.d) / nx.c; - if (lx.scale_options) - { - tbx[i] = x_log(tbx[i]); - tby[i] = y_log(tby[i]); - } + phimax += 2 * M_PI; + } + else + { + phimin += 2 * M_PI; } } -} -/*! - * Allows you to specify a polygonal shape of an area to be filled. - * - * \param[in] n The number of points - * \param[in] x A pointer to the X coordinates - * \param[in] y A pointer to the Y coordinates - * - * The attributes that control the appearance of fill areas are fill area - * interior style, fill area style index and fill area color index. - */ -void gr_fillarea(int n, double *x, double *y) -{ - fillarea(n, x, y); - - if (flag_graphics) primitive("fillarea", n, x, y); -} + if ((phi_wrapped_reverse = phimin > phimax)) + { + tmp = phimin; + phimin = phimax; + phimax = tmp; + } -/*! - * Display rasterlike images in a device-independent manner. The cell array - * function partitions a rectangle given by two corner points into DIMX X DIMY - * cells, each of them colored individually by the corresponding color index - * of the given cell array. - * - * \param[in] xmin X coordinate of the lower left point of the rectangle - * \param[in] xmax X coordinate of the upper right point of the rectangle - * \param[in] ymin Y coordinate of the lower left point of the rectangle - * \param[in] ymax Y coordinate of the upper right point of the rectangle - * \param[in] dimx X dimension of the color index array - * \param[in] dimy Y dimension of the color index array - * \param[in] scol number of leading columns in the color index array - * \param[in] srow number of leading rows in the color index array - * \param[in] ncol total number of columns in the color index array - * \param[in] nrow total number of rows in the color index array - * \param[in] color color index array - * - * The values for `xmin`, `xmax`, `ymin` and `ymax` are in world coordinates. - */ -void gr_cellarray(double xmin, double xmax, double ymin, double ymax, int dimx, int dimy, int scol, int srow, int ncol, - int nrow, int *color) -{ - check_autoinit; + if (phi_reverse != phi_wrapped_reverse) /* Drawing in negative direction */ + { + phimin += 2 * M_PI; + } - gks_cellarray(x_lin(xmin), y_lin(ymax), x_lin(xmax), y_lin(ymin), dimx, dimy, scol, srow, ncol, nrow, color); + img_data = (int *)xmalloc(size * size * sizeof(int)); - if (flag_graphics) + for (y = 0; y < size; y++) { - gr_writestream("\n"); + for (x = 0; x < size; x++) + { + double px = (x - center) / center; + double py = (y - center) / center; + r = sqrt(px * px + py * py); + phi = atan2(py, px); + if (phi < min(phimin, phimax)) + { + phi += 2 * M_PI; + } + + /* map phi from [phimin, phimax] to [0, 1] */ + phi = (phi - phimin) / (phimax - phimin); + + if (r * rmax < rmin || r >= 1 || phi < 0 || phi > 1) + { + img_data[y * size + x] = 0; + continue; + } + r = (r * rmax - rmin) / (rmax - rmin); + r_ind = (int)(r * dimr); + phi_ind = (int)(phi * dimphi) % dimphi; + if (r_reverse) + { + r_ind = dimr - r_ind - 1; + } + if (phi_wrapped_reverse) + { + phi_ind = dimphi - phi_ind - 1; + } + color_ind = color[(r_ind + srow - 1) * ncol + phi_ind + scol - 1]; + if (color_ind >= 0 && color_ind < MAX_COLOR) + { + img_data[y * size + x] = (255 << 24) + rgb[color_ind]; + } + else + { + /* invalid color indices in input data result in transparent pixel */ + img_data[y * size + x] = 0; + } + } } + + gr_drawimage(x_org - rmax, x_org + rmax, y_org + rmax, y_org - rmax, size, size, img_data, 0); + free(img_data); } /*! - * Display a two dimensional color index array with nonuniform cell sizes. + * Display a two dimensional color index array mapped to a disk using polar + * coordinates with nonuniform cell sizes. * - * \param[in] x X coordinates of the cell edges - * \param[in] y Y coordinates of the cell edges - * \param[in] dimx actual X dimension of the color index array in the memory - * \param[in] dimy actual Y dimension of the color index array in the memory - * \param[in] scol 1-based starting column of color index and x array - * \param[in] srow 1-based starting row of color index and y array - * \param[in] ncol number of columns displayed - * \param[in] nrow number of rows displayed + * \param[in] x_org X coordinate of the disk center in world coordinates + * \param[in] y_org Y coordinate of the disk center in world coordinates + * \param[in] phi array with the angles of the disk sector in degrees + * \param[in] r array with the radii of the disk in world coordinates + * \param[in] dimphi Phi (X) dimension of the color index array + * \param[in] dimr R (Y) dimension of the color index array + * \param[in] scol number of leading columns in the color index array and the angle array + * \param[in] srow number of leading rows in the color index array and the radii array + * \param[in] ncol total number of columns in the color index array and the angle array + * \param[in] nrow total number of rows in the color index array and the radii array * \param[in] color color index array * - * The values for `x` and `y` are in world coordinates. - * - * If `dimx` and `dimy` are positive `x` must contain `dimx`+1 elements and `y` must contain `dimy`+1 elements. The - * elements i and i+1 are respectively the edges of the i-th cell in X and Y direction. - * - * If `dimx` and `dimy` are negative `x` must contain `-dimx` elements and `y` must contain `-dimy` elements. The i-th - * element is the center of the i-th cell, and the first and last elements will also be the edges of the outer cells. + * The mapping of the polar coordinates and the drawing is performed simialr to `gr_polarcellarray` + * with the difference that the individual cell sizes are specified allowing nonuniform sized cells. * - * To draw all cells of the color index array use: + * If `dimphi` and `dimr` are positive `phi` must contain `dimphi`+1 elements and `r` must contain `dimr`+1 elements and + * the elements i and i+1 are respectively the edges of the i-th cell. * - * gr_nonuniformcellarray(x, y, dimx, dimy, 1, 1, dimx, dimy, color) + * If `dimphi` and `dimr` are negative `phi` must contain `-dimphi` elements and `r` must contain `-dimr` elements and + * the i-th element is the center of the i-th cell, and the first and last elements will also be the edges of the outer + * cells. * * `scol` and `srow` can be used to specify a (1-based) starting column and row - * in the `color`, `x` and `y` array. `dimx` and `dimy` specify the actual dimension of the + * in the `color`, `phi` and `r` array. `dimr` and `dimphi` specify the actual dimension of the * arrays in the memory whereof `ncol` and `nrow` values are displayed. + * */ -void gr_nonuniformcellarray(double *x, double *y, int dimx, int dimy, int scol, int srow, int ncol, int nrow, - int *color) +void gr_nonuniformpolarcellarray(double x_org, double y_org, double *phi, double *r, int dimphi, int dimr, int scol, + int srow, int ncol, int nrow, int *color) { - int img_data_x, img_data_y, color_x_ind, color_y_ind, color_ind, edges_x = 1, edges_y = 1, size = 2000; + int x, y, color_ind, r_ind, phi_ind, phi_reverse, r_reverse, start, end, edges_phi = 1, edges_r = 1, size = 2000; int *img_data; - double x_pos, y_pos, x_size, y_size, *x_orig = NULL, *y_orig = NULL; - - if (dimx < 0) + double cur_r, cur_phi, tmp, phimin, phimax, rmin, rmax, center = size / 2.; + double *r_sorted, *phi_sorted; + if (dimphi < 0) { - dimx = -dimx; - edges_x = 0; - x_orig = x; + edges_phi = 0; + dimphi = -dimphi; + ncol--; } - if (dimy < 0) + if (dimr < 0) { - dimy = -dimy; - edges_y = 0; - y_orig = y; + edges_r = 0; + dimr = -dimr; + nrow--; } - if (scol < 1 || srow < 1 || scol + ncol - 1 > dimx || srow + nrow - 1 > dimy || (!edges_x && ncol < 2) || - (!edges_y && nrow < 2)) + if (scol < 1 || srow < 1 || scol + ncol - 1 > dimphi || srow + nrow - 1 > dimr || (!edges_phi && ncol < 1) || + (!edges_r && nrow < 1)) { fprintf(stderr, "Dimensions of color index array are invalid.\n"); return; } - check_autoinit; + phimin = phi[scol - 1]; + phimax = phi[ncol]; - scol--; - srow--; - nrow += srow; - ncol += scol; + rmin = r[srow - 1]; + rmax = r[nrow]; - if (!edges_x) + if (phimin == phimax) { - x = (double *)gks_malloc(sizeof(double) * (ncol + 1)); - x[scol] = x_orig[scol]; - for (color_x_ind = scol + 1; color_x_ind < ncol; color_x_ind++) - { - x[color_x_ind] = 0.5 * (x_orig[color_x_ind] + x_orig[color_x_ind - 1]); - } - x[ncol] = x_orig[ncol - 1]; + fprintf(stderr, "Invalid angles specified.\n"); + return; } - if (!edges_y) + if (rmin == rmax || rmin < 0 || rmax < 0) { - y = (double *)gks_malloc(sizeof(double) * (nrow + 1)); - y[srow] = y_orig[srow]; - for (color_y_ind = srow + 1; color_y_ind < nrow; color_y_ind++) - { - y[color_y_ind] = 0.5 * (y_orig[color_y_ind] + y_orig[color_y_ind - 1]); - } - y[nrow] = y_orig[nrow - 1]; + fprintf(stderr, "Invalid radii specified.\n"); + return; } - for (color_x_ind = scol; color_x_ind < ncol; color_x_ind++) + check_autoinit; + + if ((r_reverse = rmin > rmax)) { - if (x[color_x_ind] > x[color_x_ind + 1]) + tmp = rmin; + rmin = rmax; + rmax = tmp; + } + if (!edges_r) + { + r_sorted = (double *)gks_malloc(sizeof(double) * (nrow - srow + 3)); + } + else + { + r_sorted = (double *)gks_malloc(sizeof(double) * (nrow - srow + 2)); + } + for (y = 0; y < nrow - srow + 2; y++) + { + if (r_reverse) { - if (!edges_x) + if (!edges_r && y) { - gks_free(x); + r_sorted[y] = 0.5 * (r[nrow - y] + r[nrow - y + 1]); } - if (!edges_y) + else { - gks_free(y); + r_sorted[y] = r[nrow - y]; } - fprintf(stderr, "x points not sorted in ascending order\n"); - return; } - } - - for (color_y_ind = srow; color_y_ind < nrow; color_y_ind++) - { - if (y[color_y_ind] > y[color_y_ind + 1]) + else { - if (!edges_x) + if (!edges_r && y) { - gks_free(x); + r_sorted[y] = 0.5 * (r[srow - 1 + y] + r[srow - 2 + y]); } - if (!edges_y) + else { - gks_free(y); + r_sorted[y] = r[srow - 1 + y]; } - fprintf(stderr, "y points not sorted in ascending order\n"); + } + if (y && r_sorted[y] < r_sorted[y - 1]) + { + fprintf(stderr, "radii not sorted in ascending order.\n"); + gks_free(r_sorted); return; } } - - x_size = x[ncol] - x[scol]; - y_size = y[nrow] - y[srow]; - img_data = (int *)xmalloc(size * size * sizeof(int)); - - color_y_ind = srow; - for (img_data_y = 0; img_data_y < size; img_data_y++) + if (!edges_r) { - y_pos = y[srow] + img_data_y * y_size / size; - while (color_y_ind < nrow && y[color_y_ind + 1] <= y_pos) + if (r_reverse) { - color_y_ind++; + r_sorted[nrow - srow + 2] = r[srow - 1]; } - color_x_ind = scol; - for (img_data_x = 0; img_data_x < size; img_data_x++) + else { - x_pos = x[scol] + img_data_x * x_size / size; - while (color_x_ind < ncol && x[color_x_ind + 1] <= x_pos) - { - color_x_ind++; - } - color_ind = color[color_y_ind * dimx + color_x_ind]; - if (color_ind >= 0 && color_ind < MAX_COLOR) - { - img_data[img_data_y * size + img_data_x] = (255 << 24) + rgb[color_ind]; - } - else - { - /* invalid color indices in input data result in transparent pixel */ - img_data[img_data_y * size + img_data_x] = 0; - } + r_sorted[nrow - srow + 2] = r[nrow]; } + nrow++; } - gr_drawimage(x[scol], x[ncol], y[nrow], y[srow], size, size, img_data, 0); - free(img_data); - if (!edges_x) - { - gks_free(x); - } - if (!edges_y) - { - gks_free(y); - } -} - -/*! - * Display a two dimensional color index array mapped to a disk using polar - * coordinates. - * - * \param[in] x_org X coordinate of the disk center in world coordinates - * \param[in] y_org Y coordinate of the disk center in world coordinates - * \param[in] phimin start angle of the disk sector in degrees - * \param[in] phimax end angle of the disk sector in degrees - * \param[in] rmin inner radius of the punctured disk in world coordinates - * \param[in] rmax outer radius of the disk in world coordinates - * \param[in] dimphi Phi (X) dimension of the color index array - * \param[in] dimr R (Y) dimension of the color index array - * \param[in] scol number of leading columns in the color index array - * \param[in] srow number of leading rows in the color index array - * \param[in] ncol total number of columns in the color index array - * \param[in] nrow total number of rows in the color index array - * \param[in] color color index array - * - * The two dimensional color index array is mapped to the resulting image by - * interpreting the X-axis of the array as the angle and the Y-axis as the radius. - * The center point of the resulting disk is located at `x_org`, `y_org` and the - * radius of the disk is `rmax`. - * - * To draw a contiguous array as a complete disk use: - * - * gr_polarcellarray(x_org, y_org, 0, 360, 0, rmax, dimphi, dimr, 1, 1, dimphi, dimr, color) - * - * The additional parameters to the function can be used to further control the - * mapping from polar to cartesian coordinates. - * - * If `rmin` is greater than 0 the input data is mapped to a punctured disk (or - * annulus) with an inner radius of `rmin` and an outer radius `rmax`. If `rmin` - * is greater than `rmax` the Y-axis of the array is reversed. - * - * The parameter `phimin` and `phimax` can be used to map the data to a sector - * of the (punctured) disk starting at `phimin` and ending at `phimax`. If - * `phimin` is greater than `phimax` the X-axis is reversed. The visible sector - * is the one starting in mathematically positive direction (counterclockwise) - * at the smaller angle and ending at the larger angle. An example of the four - * possible options can be found below: - * - * \verbatim embed:rst:leading-asterisk - * - * +-----------+-----------+---------------------------------------------------+ - * |**phimin** |**phimax** |**Result** | - * +-----------+-----------+---------------------------------------------------+ - * |90 |270 |Left half visible, mapped counterclockwise | - * +-----------+-----------+---------------------------------------------------+ - * |270 |90 |Left half visible, mapped clockwise | - * +-----------+-----------+---------------------------------------------------+ - * |-90 |90 |Right half visible, mapped counterclockwise | - * +-----------+-----------+---------------------------------------------------+ - * |90 |-90 |Right half visible, mapped clockwise | - * +-----------+-----------+---------------------------------------------------+ - * - * \endverbatim - * - * `scol` and `srow` can be used to specify a (1-based) starting column and row - * in the `color` array. `ncol` and `nrow` specify the actual dimension of the - * array in the memory whereof `dimphi` and `dimr` values are mapped to the disk. - * - */ -void gr_polarcellarray(double x_org, double y_org, double phimin, double phimax, double rmin, double rmax, int dimphi, - int dimr, int scol, int srow, int ncol, int nrow, int *color) -{ - int x, y, color_ind, r_ind, phi_ind, phi_reverse, phi_wrapped_reverse, r_reverse, size = 2000; - int *img_data; - double r, phi, tmp, center = size / 2.; - - phimin = arc(phimin); - phimax = arc(phimax); - - if (!(scol >= 1 && srow >= 1 && scol + ncol - 1 <= dimphi && srow + nrow - 1 <= dimr)) - { - fprintf(stderr, "Dimensions of color index array are invalid.\n"); - return; - } - - if (phimin == phimax) + if ((phi_reverse = phimin > phimax)) { - fprintf(stderr, "Invalid angles specified.\n"); - return; + tmp = phimin; + phimin = phimax; + phimax = tmp; } - - if (rmin == rmax || rmin < 0 || rmax < 0) + if (!edges_phi) { - fprintf(stderr, "Invalid radii specified.\n"); - return; + phi_sorted = (double *)gks_malloc(sizeof(double) * (ncol - scol + 3)); } - - check_autoinit; - - if ((r_reverse = rmin > rmax)) + else { - tmp = rmin; - rmin = rmax; - rmax = tmp; + phi_sorted = (double *)gks_malloc(sizeof(double) * (ncol - scol + 2)); } - - phi_reverse = phimin > phimax; - - /* Wrap phimin and phimax to [0, 2Pi] */ - phimin -= floor(phimin / M_PI / 2) * 2 * M_PI; - phimax -= floor(phimax / M_PI / 2) * 2 * M_PI; - - if (fabs(phimin - phimax) < 1e-8) /* Avoid empty images when drawing a full circle */ + for (x = 0; x < ncol - scol + 2; x++) { - if (!phi_reverse) + if (phi_reverse) { - phimax += 2 * M_PI; + if (!edges_phi && x) + { + phi_sorted[x] = 0.5 * (phi[ncol - x] + phi[ncol - x + 1]); + } + else + { + phi_sorted[x] = phi[ncol - x]; + } } else { - phimin += 2 * M_PI; + if (!edges_phi && x) + { + phi_sorted[x] = 0.5 * (phi[scol - 1 + x] + phi[scol - 2 + x]); + } + else + { + phi_sorted[x] = phi[scol - 1 + x]; + } + } + phi_sorted[x] = phi_sorted[x] - phimax + 360; + if (x && phi_sorted[x] < phi_sorted[x - 1]) + { + fprintf(stderr, "angles not sorted in ascending order.\n"); + gks_free(r_sorted); + gks_free(phi_sorted); + return; } } - - if ((phi_wrapped_reverse = phimin > phimax)) + if (!edges_phi) { - tmp = phimin; - phimin = phimax; - phimax = tmp; + if (phi_reverse) + { + phi_sorted[ncol - scol + 2] = phi[scol - 1] - phimax + 360; + } + else + { + phi_sorted[ncol - scol + 2] = phi[ncol] - phimax + 360; + } + ncol++; } - if (phi_reverse != phi_wrapped_reverse) /* Drawing in negative direction */ - { - phimin += 2 * M_PI; - } + phimin = fmod(phimin, 360); + phimax = fmod(phimax, 360); - img_data = (int *)xmalloc(size * size * sizeof(int)); + img_data = (int *)gks_malloc(size * size * sizeof(int)); for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { - double px = (x - center) / center; - double py = (y - center) / center; - r = sqrt(px * px + py * py); - phi = atan2(py, px); - if (phi < min(phimin, phimax)) + double px = (x - center) / center * rmax; + double py = (y - center) / center * rmax; + + cur_r = sqrt(px * px + py * py); + if (r_reverse) { - phi += 2 * M_PI; + cur_r = rmax - cur_r + rmin; } - /* map phi from [phimin, phimax] to [0, 1] */ - phi = (phi - phimin) / (phimax - phimin); + if (phi_reverse) + { + cur_phi = fmod(-fmod(atan2(py, px) * 180 / M_PI + 360, 360) + phimin + 2 * 360, 360); + } + else + { + cur_phi = fmod(fmod(atan2(py, px) * 180 / M_PI + 360, 360) + 2 * 360 - phimax, 360); + } - if (r * rmax < rmin || r >= 1 || phi < 0 || phi > 1) + start = 0; + end = nrow - srow + 1; + if (cur_r < r_sorted[start] || cur_r >= r_sorted[end]) { img_data[y * size + x] = 0; continue; } - r = (r * rmax - rmin) / (rmax - rmin); - r_ind = (int)(r * dimr); - phi_ind = (int)(phi * dimphi) % dimphi; - if (r_reverse) + + while (start != end) { - r_ind = dimr - r_ind - 1; + int m = ((start + end) / 2); + if (cur_r >= r_sorted[m + 1]) + { + start = m + 1; + } + else if (r_sorted[m] > cur_r) + { + end = m; + } + else + { + start = end = m; + } } - if (phi_wrapped_reverse) + r_ind = start; + + start = 0; + end = ncol - scol + 1; + if (cur_phi < phi_sorted[start] || cur_phi >= phi_sorted[end]) { - phi_ind = dimphi - phi_ind - 1; + img_data[y * size + x] = 0; + continue; } - color_ind = color[(r_ind + srow - 1) * ncol + phi_ind + scol - 1]; - if (color_ind >= 0 && color_ind < MAX_COLOR) + while (start != end) + { + int m = ((start + end) / 2); + if (cur_phi >= phi_sorted[m + 1]) + { + start = m + 1; + } + else if (phi_sorted[m] > cur_phi) + { + end = m; + } + else + { + start = end = m; + } + } + phi_ind = start; + + color_ind = color[(r_ind + srow - 1) * ncol + phi_ind + scol - 1]; + if (color_ind >= 0 && color_ind < MAX_COLOR) { img_data[y * size + x] = (255 << 24) + rgb[color_ind]; } @@ -2534,453 +2506,162 @@ void gr_polarcellarray(double x_org, double y_org, double phimin, double phimax, } } + gks_free(r_sorted); + gks_free(phi_sorted); gr_drawimage(x_org - rmax, x_org + rmax, y_org + rmax, y_org - rmax, size, size, img_data, 0); - free(img_data); + gks_free(img_data); } -/*! - * Display a two dimensional color index array mapped to a disk using polar - * coordinates with nonuniform cell sizes. - * - * \param[in] x_org X coordinate of the disk center in world coordinates - * \param[in] y_org Y coordinate of the disk center in world coordinates - * \param[in] phi array with the angles of the disk sector in degrees - * \param[in] r array with the radii of the disk in world coordinates - * \param[in] dimphi Phi (X) dimension of the color index array - * \param[in] dimr R (Y) dimension of the color index array - * \param[in] scol number of leading columns in the color index array and the angle array - * \param[in] srow number of leading rows in the color index array and the radii array - * \param[in] ncol total number of columns in the color index array and the angle array - * \param[in] nrow total number of rows in the color index array and the radii array - * \param[in] color color index array - * - * The mapping of the polar coordinates and the drawing is performed simialr to `gr_polarcellarray` - * with the difference that the individual cell sizes are specified allowing nonuniform sized cells. - * - * If `dimphi` and `dimr` are positive `phi` must contain `dimphi`+1 elements and `r` must contain `dimr`+1 elements and - * the elements i and i+1 are respectively the edges of the i-th cell. - * - * If `dimphi` and `dimr` are negative `phi` must contain `-dimphi` elements and `r` must contain `-dimr` elements and - * the i-th element is the center of the i-th cell, and the first and last elements will also be the edges of the outer - * cells. - * - * `scol` and `srow` can be used to specify a (1-based) starting column and row - * in the `color`, `phi` and `r` array. `dimr` and `dimphi` specify the actual dimension of the - * arrays in the memory whereof `ncol` and `nrow` values are displayed. - * - */ -void gr_nonuniformpolarcellarray(double x_org, double y_org, double *phi, double *r, int dimphi, int dimr, int scol, - int srow, int ncol, int nrow, int *color) +void gr_gdp(int n, double *x, double *y, int primid, int ldr, int *datrec) { - int x, y, color_ind, r_ind, phi_ind, phi_reverse, r_reverse, start, end, edges_phi = 1, edges_r = 1, size = 2000; - int *img_data; - double cur_r, cur_phi, tmp, phimin, phimax, rmin, rmax, center = size / 2.; - double *r_sorted, *phi_sorted; - if (dimphi < 0) - { - edges_phi = 0; - dimphi = -dimphi; - ncol--; - } - if (dimr < 0) + int npoints = n; + double *px = x, *py = y; + int i; + + check_autoinit; + + if (lx.scale_options) { - edges_r = 0; - dimr = -dimr; - nrow--; + if (npoints >= maxpath) reallocate(npoints); + + px = xpoint; + py = ypoint; + for (i = 0; i < npoints; i++) + { + px[i] = x_lin(x[i]); + py[i] = y_lin(y[i]); + } } - if (scol < 1 || srow < 1 || scol + ncol - 1 > dimphi || srow + nrow - 1 > dimr || (!edges_phi && ncol < 1) || - (!edges_r && nrow < 1)) + gks_gdp(npoints, px, py, primid, ldr, datrec); + + if (flag_graphics) { - fprintf(stderr, "Dimensions of color index array are invalid.\n"); - return; + gr_writestream("\n"); } +} - phimin = phi[scol - 1]; - phimax = phi[ncol]; - - rmin = r[srow - 1]; - rmax = r[nrow]; +/*! + * Generate a cubic spline-fit, starting from the first data point and + * ending at the last data point. + * + * \param[in] n The number of points + * \param[in] px A pointer to the X coordinates + * \param[in] py A pointer to the Y coordinates + * \param[in] m The number of points in the polygon to be drawn (m > n) + * \param[in] method The smoothing method + * + * The values for `x` and `y` are in world coordinates. The attributes that + * control the appearance of a spline-fit are linetype, linewidth and color + * index. + * + * If `method` is > 0, then a generalized cross-validated smoothing spline is calculated. + * If `method` is 0, then an interpolating natural cubic spline is calculated. + * If `method` is < -1, then a cubic B-spline is calculated. + */ +void gr_spline(int n, double *px, double *py, int m, int method) +{ + int err = 0, i, j; + double *t, *s; + double *sx, *sy, *x, *f, *df, *y, *c, *wk, *se, var, d; + int ic, job, ier; - if (phimin == phimax) + if (n <= 2) { - fprintf(stderr, "Invalid angles specified.\n"); + fprintf(stderr, "invalid number of points\n"); return; } - - if (rmin == rmax || rmin < 0 || rmax < 0) + else if (n >= m) { - fprintf(stderr, "Invalid radii specified.\n"); + fprintf(stderr, "invalid number of domain values\n"); return; } check_autoinit; - if ((r_reverse = rmin > rmax)) - { - tmp = rmin; - rmin = rmax; - rmax = tmp; - } - if (!edges_r) - { - r_sorted = (double *)gks_malloc(sizeof(double) * (nrow - srow + 3)); - } - else + t = (double *)xmalloc(sizeof(double) * m); + s = (double *)xmalloc(sizeof(double) * m); + sx = (double *)xmalloc(sizeof(double) * m); + sy = (double *)xmalloc(sizeof(double) * m); + x = (double *)xmalloc(sizeof(double) * n); + f = (double *)xmalloc(sizeof(double) * n); + df = (double *)xmalloc(sizeof(double) * n); + y = (double *)xmalloc(sizeof(double) * n); + c = (double *)xmalloc(sizeof(double) * 3 * (n - 1)); + se = (double *)xmalloc(sizeof(double) * n); + wk = (double *)xmalloc(sizeof(double) * 7 * (n + 2)); + + for (i = 0; i < n; i++) { - r_sorted = (double *)gks_malloc(sizeof(double) * (nrow - srow + 2)); + x[i] = (double)((x_lin(px[i]) - lx.xmin) / (lx.xmax - lx.xmin)); + f[i] = (double)((y_lin(py[i]) - lx.ymin) / (lx.ymax - lx.ymin)); + df[i] = 1; } - for (y = 0; y < nrow - srow + 2; y++) + + if (method >= -1) { - if (r_reverse) - { - if (!edges_r && y) - { - r_sorted[y] = 0.5 * (r[nrow - y] + r[nrow - y + 1]); - } - else - { - r_sorted[y] = r[nrow - y]; - } - } - else + for (i = 1; i < n; i++) + if (px[i - 1] >= px[i]) + { + fprintf(stderr, "points not sorted in ascending order\n"); + err = 1; + } + + if (!err) { - if (!edges_r && y) + sx[0] = x[0]; + for (j = 1; j < m - 1; j++) sx[j] = x[0] + j * (x[n - 1] - x[0]) / (m - 1); + sx[m - 1] = x[n - 1]; + + job = 0; + ic = n - 1; + var = (double)method; + + cubgcv(x, f, df, &n, y, c, &ic, &var, &job, se, wk, &ier); + + if (ier == 0) { - r_sorted[y] = 0.5 * (r[srow - 1 + y] + r[srow - 2 + y]); + for (j = 0; j < m; j++) + { + i = 0; + while ((i < ic) && (x[i] <= sx[j])) i++; + if (x[i] > sx[j]) i--; + if (i < 0) + i = 0; + else if (i >= ic) + i = ic - 1; + d = sx[j] - x[i]; + + s[j] = (double)(((c[i + 2 * ic] * d + c[i + ic]) * d + c[i]) * d + y[i]); + } } else { - r_sorted[y] = r[srow - 1 + y]; + fprintf(stderr, "invalid argument to math library\n"); + err = 1; } } - if (y && r_sorted[y] < r_sorted[y - 1]) - { - fprintf(stderr, "radii not sorted in ascending order.\n"); - gks_free(r_sorted); - return; - } } - if (!edges_r) + else { - if (r_reverse) - { - r_sorted[nrow - srow + 2] = r[srow - 1]; - } - else - { - r_sorted[nrow - srow + 2] = r[nrow]; - } - nrow++; - } + b_spline(n, x, f, m, sx, sy); - if ((phi_reverse = phimin > phimax)) - { - tmp = phimin; - phimin = phimax; - phimax = tmp; + for (j = 0; j < m; j++) s[j] = (double)sy[j]; } - if (!edges_phi) + + if (!err) { - phi_sorted = (double *)gks_malloc(sizeof(double) * (ncol - scol + 3)); - } - else - { - phi_sorted = (double *)gks_malloc(sizeof(double) * (ncol - scol + 2)); - } - for (x = 0; x < ncol - scol + 2; x++) - { - if (phi_reverse) - { - if (!edges_phi && x) - { - phi_sorted[x] = 0.5 * (phi[ncol - x] + phi[ncol - x + 1]); - } - else - { - phi_sorted[x] = phi[ncol - x]; - } - } - else - { - if (!edges_phi && x) - { - phi_sorted[x] = 0.5 * (phi[scol - 1 + x] + phi[scol - 2 + x]); - } - else - { - phi_sorted[x] = phi[scol - 1 + x]; - } - } - phi_sorted[x] = phi_sorted[x] - phimax + 360; - if (x && phi_sorted[x] < phi_sorted[x - 1]) - { - fprintf(stderr, "angles not sorted in ascending order.\n"); - gks_free(r_sorted); - gks_free(phi_sorted); - return; - } - } - if (!edges_phi) - { - if (phi_reverse) - { - phi_sorted[ncol - scol + 2] = phi[scol - 1] - phimax + 360; - } - else - { - phi_sorted[ncol - scol + 2] = phi[ncol] - phimax + 360; - } - ncol++; - } - - phimin = fmod(phimin, 360); - phimax = fmod(phimax, 360); - - img_data = (int *)gks_malloc(size * size * sizeof(int)); - - for (y = 0; y < size; y++) - { - for (x = 0; x < size; x++) - { - double px = (x - center) / center * rmax; - double py = (y - center) / center * rmax; - - cur_r = sqrt(px * px + py * py); - if (r_reverse) - { - cur_r = rmax - cur_r + rmin; - } - - if (phi_reverse) - { - cur_phi = fmod(-fmod(atan2(py, px) * 180 / M_PI + 360, 360) + phimin + 2 * 360, 360); - } - else - { - cur_phi = fmod(fmod(atan2(py, px) * 180 / M_PI + 360, 360) + 2 * 360 - phimax, 360); - } - - start = 0; - end = nrow - srow + 1; - if (cur_r < r_sorted[start] || cur_r >= r_sorted[end]) - { - img_data[y * size + x] = 0; - continue; - } - - while (start != end) - { - int m = ((start + end) / 2); - if (cur_r >= r_sorted[m + 1]) - { - start = m + 1; - } - else if (r_sorted[m] > cur_r) - { - end = m; - } - else - { - start = end = m; - } - } - r_ind = start; - - start = 0; - end = ncol - scol + 1; - if (cur_phi < phi_sorted[start] || cur_phi >= phi_sorted[end]) - { - img_data[y * size + x] = 0; - continue; - } - while (start != end) - { - int m = ((start + end) / 2); - if (cur_phi >= phi_sorted[m + 1]) - { - start = m + 1; - } - else if (phi_sorted[m] > cur_phi) - { - end = m; - } - else - { - start = end = m; - } - } - phi_ind = start; - - color_ind = color[(r_ind + srow - 1) * ncol + phi_ind + scol - 1]; - if (color_ind >= 0 && color_ind < MAX_COLOR) - { - img_data[y * size + x] = (255 << 24) + rgb[color_ind]; - } - else - { - /* invalid color indices in input data result in transparent pixel */ - img_data[y * size + x] = 0; - } - } - } - - gks_free(r_sorted); - gks_free(phi_sorted); - gr_drawimage(x_org - rmax, x_org + rmax, y_org + rmax, y_org - rmax, size, size, img_data, 0); - gks_free(img_data); -} - -void gr_gdp(int n, double *x, double *y, int primid, int ldr, int *datrec) -{ - int npoints = n; - double *px = x, *py = y; - int i; - - check_autoinit; - - if (lx.scale_options) - { - if (npoints >= maxpath) reallocate(npoints); - - px = xpoint; - py = ypoint; - for (i = 0; i < npoints; i++) - { - px[i] = x_lin(x[i]); - py[i] = y_lin(y[i]); - } - } - - gks_gdp(npoints, px, py, primid, ldr, datrec); - - if (flag_graphics) - { - gr_writestream("\n"); - } -} - -/*! - * Generate a cubic spline-fit, starting from the first data point and - * ending at the last data point. - * - * \param[in] n The number of points - * \param[in] px A pointer to the X coordinates - * \param[in] py A pointer to the Y coordinates - * \param[in] m The number of points in the polygon to be drawn (m > n) - * \param[in] method The smoothing method - * - * The values for `x` and `y` are in world coordinates. The attributes that - * control the appearance of a spline-fit are linetype, linewidth and color - * index. - * - * If `method` is > 0, then a generalized cross-validated smoothing spline is calculated. - * If `method` is 0, then an interpolating natural cubic spline is calculated. - * If `method` is < -1, then a cubic B-spline is calculated. - */ -void gr_spline(int n, double *px, double *py, int m, int method) -{ - int err = 0, i, j; - double *t, *s; - double *sx, *sy, *x, *f, *df, *y, *c, *wk, *se, var, d; - int ic, job, ier; - - if (n <= 2) - { - fprintf(stderr, "invalid number of points\n"); - return; - } - else if (n >= m) - { - fprintf(stderr, "invalid number of domain values\n"); - return; - } - - check_autoinit; - - t = (double *)xmalloc(sizeof(double) * m); - s = (double *)xmalloc(sizeof(double) * m); - sx = (double *)xmalloc(sizeof(double) * m); - sy = (double *)xmalloc(sizeof(double) * m); - x = (double *)xmalloc(sizeof(double) * n); - f = (double *)xmalloc(sizeof(double) * n); - df = (double *)xmalloc(sizeof(double) * n); - y = (double *)xmalloc(sizeof(double) * n); - c = (double *)xmalloc(sizeof(double) * 3 * (n - 1)); - se = (double *)xmalloc(sizeof(double) * n); - wk = (double *)xmalloc(sizeof(double) * 7 * (n + 2)); - - for (i = 0; i < n; i++) - { - x[i] = (double)((x_lin(px[i]) - lx.xmin) / (lx.xmax - lx.xmin)); - f[i] = (double)((y_lin(py[i]) - lx.ymin) / (lx.ymax - lx.ymin)); - df[i] = 1; - } - - if (method >= -1) - { - for (i = 1; i < n; i++) - if (px[i - 1] >= px[i]) - { - fprintf(stderr, "points not sorted in ascending order\n"); - err = 1; - } - - if (!err) - { - sx[0] = x[0]; - for (j = 1; j < m - 1; j++) sx[j] = x[0] + j * (x[n - 1] - x[0]) / (m - 1); - sx[m - 1] = x[n - 1]; - - job = 0; - ic = n - 1; - var = (double)method; - - cubgcv(x, f, df, &n, y, c, &ic, &var, &job, se, wk, &ier); - - if (ier == 0) - { - for (j = 0; j < m; j++) - { - i = 0; - while ((i < ic) && (x[i] <= sx[j])) i++; - if (x[i] > sx[j]) i--; - if (i < 0) - i = 0; - else if (i >= ic) - i = ic - 1; - d = sx[j] - x[i]; - - s[j] = (double)(((c[i + 2 * ic] * d + c[i + ic]) * d + c[i]) * d + y[i]); - } - } - else - { - fprintf(stderr, "invalid argument to math library\n"); - err = 1; - } - } - } - else - { - b_spline(n, x, f, m, sx, sy); - - for (j = 0; j < m; j++) s[j] = (double)sy[j]; - } - - if (!err) - { - for (j = 0; j < m; j++) - { - t[j] = x_log((double)(lx.xmin + sx[j] * (lx.xmax - lx.xmin))); - s[j] = y_log((double)(lx.ymin + s[j] * (lx.ymax - lx.ymin))); - } - polyline(m, t, s); + for (j = 0; j < m; j++) + { + t[j] = x_log((double)(lx.xmin + sx[j] * (lx.xmax - lx.xmin))); + s[j] = y_log((double)(lx.ymin + s[j] * (lx.ymax - lx.ymin))); + } + polyline(m, t, s); } free(wk); @@ -9935,1096 +9616,1452 @@ void gr_fillrect(double xmin, double xmax, double ymin, double ymax) fillarea(4, x, y); } - else - { - x[0] = min(x_lin(xmin), x_lin(xmax)); - y[0] = min(y_lin(ymin), y_lin(ymax)); - x[1] = max(x_lin(xmin), x_lin(xmax)); - y[1] = max(y_lin(ymin), y_lin(ymax)); + else + { + x[0] = min(x_lin(xmin), x_lin(xmax)); + y[0] = min(y_lin(ymin), y_lin(ymax)); + x[1] = max(x_lin(xmin), x_lin(xmax)); + y[1] = max(y_lin(ymin), y_lin(ymax)); + + x[1] = x[2] = x_lin(max(xmin, xmax)); + x[0] = x[3] = x_lin(min(xmin, xmax)); + y[2] = y[3] = y_lin(max(ymin, ymax)); + y[0] = y[1] = y_lin(min(ymin, ymax)); + + gr_inqborderwidth(&bwidth); + if (bwidth != 0) codes[4] = 'F'; + + gks_gdp(4, x, y, GKS_K_GDP_DRAW_PATH, 5, codes); + } + + if (flag_graphics) + gr_writestream("\n", xmin, xmax, ymin, ymax); +} + +/*! + * Draw a circular or elliptical arc covering the specified rectangle. + * + * \param[in] xmin Left edge of the rectangle + * \param[in] xmax Right edge of the rectangle + * \param[in] ymin Bottom edge of the rectangle + * \param[in] ymax Upper edge of the rectangle + * \param[in] a1 The start angle + * \param[in] a2 The end angle + * + * The resulting arc begins at a1 and ends at a2 degrees. Angles are + * interpreted such that 0 degrees is at the 3 o'clock position. The center of + * the arc is the center of the given rectangle. + */ +void gr_drawarc(double xmin, double xmax, double ymin, double ymax, double a1, double a2) +{ + int errind, style; + double xcenter, ycenter, width, height, start, end, a; + int n; + double x[361], y[361]; + int codes[3] = {'M', 'A', 'S'}; + + check_autoinit; + + gks_inq_fill_int_style(&errind, &style); + + xcenter = (x_lin(xmin) + x_lin(xmax)) / 2.0; + ycenter = (y_lin(ymin) + y_lin(ymax)) / 2.0; + width = fabs(x_lin(xmax) - x_lin(xmin)) / 2.0; + height = fabs(y_lin(ymax) - y_lin(ymin)) / 2.0; + + if (style != GKS_K_INTSTYLE_SOLID_WITH_BORDER) + { + start = min(a1, a2); + end = max(a1, a2); + start += ((int)(end - start)) / 360 * 360; + /* Ensure that two equivalent but unequal angles result in a full arc. */ + if (fabs(end - start) < FEPS && fabs(a1 - a2) > FEPS) + { + end += 360; + } + + n = 0; + for (a = start; a <= end; a++) + { + x[n] = x_log(xcenter + width * cos(a * M_PI / 180)); + y[n] = y_log(ycenter + height * sin(a * M_PI / 180)); + n++; + } + if (fabs((a - 1) - end) > FEPS) + { + x[n] = x_log(xcenter + width * cos(end * M_PI / 180)); + y[n] = y_log(ycenter + height * sin(end * M_PI / 180)); + n++; + } + + if (n > 1) + { + polyline(n, x, y); + } + } + else + { + x[0] = xcenter + width * cos(a1); + y[0] = ycenter + height * sin(a1); + x[1] = width; + y[1] = height; + x[2] = a1 * M_PI / 180; + y[2] = a2 * M_PI / 180; + x[3] = y[3] = 0; + + gks_gdp(4, x, y, GKS_K_GDP_DRAW_PATH, 3, codes); + } + + if (flag_graphics) + { + gr_writestream("\n", + xmin, xmax, ymin, ymax, a1, a2); + } +} + +/*! + * Fill a circular or elliptical arc covering the specified rectangle. + * + * \param[in] xmin Left edge of the rectangle + * \param[in] xmax Right edge of the rectangle + * \param[in] ymin Bottom edge of the rectangle + * \param[in] ymax Upper edge of the rectangle + * \param[in] a1 The start angle + * \param[in] a2 The end angle + * + * The resulting arc begins at a1 and ends at a2 degrees. Angles are + * interpreted such that 0 degrees is at the 3 o'clock position. The center of + * the arc is the center of the given rectangle. + */ +void gr_fillarc(double xmin, double xmax, double ymin, double ymax, double a1, double a2) +{ + int errind, style; + double xcenter, ycenter, width, height, start, end, a; + int n; + double bwidth, x[362], y[362]; + int codes[3] = {'M', 'A', 'f'}; + + check_autoinit; + + gks_inq_fill_int_style(&errind, &style); + + xcenter = (x_lin(xmin) + x_lin(xmax)) / 2.0; + ycenter = (y_lin(ymin) + y_lin(ymax)) / 2.0; + width = fabs(x_lin(xmax) - x_lin(xmin)) / 2.0; + height = fabs(y_lin(ymax) - y_lin(ymin)) / 2.0; + + if (style != GKS_K_INTSTYLE_SOLID_WITH_BORDER) + { + start = min(a1, a2); + end = max(a1, a2); + start += ((int)(end - start)) / 360 * 360; + /* Ensure that two equivalent but unequal angles result in a full arc. */ + if (fabs(end - start) < FEPS && fabs(a1 - a2) > FEPS) + { + end += 360; + } + + x[0] = x_log(xcenter); + y[0] = x_log(ycenter); + n = 1; + for (a = start; a <= end; a++) + { + x[n] = x_log(xcenter + width * cos(a * M_PI / 180)); + y[n] = y_log(ycenter + height * sin(a * M_PI / 180)); + n++; + } + if (fabs((a - 1) - end) > FEPS) + { + x[n] = x_log(xcenter + width * cos(end * M_PI / 180)); + y[n] = y_log(ycenter + height * sin(end * M_PI / 180)); + n++; + } + + if (n > 2) + { + fillarea(n, x, y); + } + } + else + { + x[0] = xcenter + width * cos(a1); + y[0] = ycenter + height * sin(a1); + x[1] = width; + y[1] = height; + x[2] = a1 * M_PI / 180; + y[2] = a2 * M_PI / 180; + x[3] = y[3] = 0; + + gr_inqborderwidth(&bwidth); + if (bwidth != 0) codes[2] = 'F'; + + gks_gdp(4, x, y, GKS_K_GDP_DRAW_PATH, 3, codes); + } + + if (flag_graphics) + { + gr_writestream("\n", + xmin, xmax, ymin, ymax, a1, a2); + } +} + +static void addpath(double x, double y) +{ + xpath[npath] = x; + ypath[npath] = y; + npath += 1; +} + +static void closepath(int fill) +{ + if (fill) + { + if (npath > 2) gks_fillarea(npath, xpath, ypath); + } + else if (npath > 1) + gks_polyline(npath, xpath, ypath); + npath = 0; +} + +static void quad_bezier(double x[3], double y[3], int n) +{ + int i; + double t, a, b, c; + + if (npath + n >= maxpath) reallocate(npath + n); + + for (i = 0; i < n; i++) + { + t = (double)i / (n - 1); + a = pow((1.0 - t), 2.0); + b = 2.0 * t * (1.0 - t); + c = pow(t, 2.0); + addpath(a * x[0] + b * x[1] + c * x[2], a * y[0] + b * y[1] + c * y[2]); + } +} - x[1] = x[2] = x_lin(max(xmin, xmax)); - x[0] = x[3] = x_lin(min(xmin, xmax)); - y[2] = y[3] = y_lin(max(ymin, ymax)); - y[0] = y[1] = y_lin(min(ymin, ymax)); +static void cubic_bezier(double x[4], double y[4], int n) +{ + int i; + double t, a, b, c, d; - gr_inqborderwidth(&bwidth); - if (bwidth != 0) codes[4] = 'F'; + if (npath + n >= maxpath) reallocate(npath + n); - gks_gdp(4, x, y, GKS_K_GDP_DRAW_PATH, 5, codes); + for (i = 0; i < n; i++) + { + t = (double)i / (n - 1); + a = pow((1.0 - t), 3.0); + b = 3.0 * t * pow((1.0 - t), 2.0); + c = 3.0 * pow(t, 2.0) * (1.0 - t); + d = pow(t, 3.0); + addpath(a * x[0] + b * x[1] + c * x[2] + d * x[3], a * y[0] + b * y[1] + c * y[2] + d * y[3]); } - - if (flag_graphics) - gr_writestream("\n", xmin, xmax, ymin, ymax); } /*! - * Draw a circular or elliptical arc covering the specified rectangle. + * Draw simple and compound outlines consisting of line segments and bezier + * curves. * - * \param[in] xmin Left edge of the rectangle - * \param[in] xmax Right edge of the rectangle - * \param[in] ymin Bottom edge of the rectangle - * \param[in] ymax Upper edge of the rectangle - * \param[in] a1 The start angle - * \param[in] a2 The end angle + * \param[in] n The number of vertices + * \param[in] vertices A pointer to the vertices + * \param[in] codes A pointer to the path codes + * \param[in] fill A flag indication whether resulting path is to be filled or + * not * - * The resulting arc begins at a1 and ends at a2 degrees. Angles are - * interpreted such that 0 degrees is at the 3 o'clock position. The center of - * the arc is the center of the given rectangle. + * \verbatim embed:rst:leading-asterisk + * + * The following path codes are recognized: + * + * +----------+-----------------------------------------------------------+ + * | STOP|end the entire path | + * +----------+-----------------------------------------------------------+ + * | MOVETO|move to the given vertex | + * +----------+-----------------------------------------------------------+ + * | LINETO|draw a line from the current position to the given vertex | + * +----------+-----------------------------------------------------------+ + * | CURVE3|draw a quadratic Bezier curve | + * +----------+-----------------------------------------------------------+ + * | CURVE4|draw a cubic Bezier curve | + * +----------+-----------------------------------------------------------+ + * | CLOSEPOLY|draw a line segment to the start point of the current path | + * +----------+-----------------------------------------------------------+ + * + * \endverbatim */ -void gr_drawarc(double xmin, double xmax, double ymin, double ymax, double a1, double a2) +void gr_drawpath(int n, vertex_t *vertices, unsigned char *codes, int fill) { - int errind, style; - double xcenter, ycenter, width, height, start, end, a; - int n; - double x[361], y[361]; - int codes[3] = {'M', 'A', 'S'}; + int i, j = 0, code, nan = 0; check_autoinit; - gks_inq_fill_int_style(&errind, &style); + if (n >= maxpath) reallocate(n); - xcenter = (x_lin(xmin) + x_lin(xmax)) / 2.0; - ycenter = (y_lin(ymin) + y_lin(ymax)) / 2.0; - width = fabs(x_lin(xmax) - x_lin(xmin)) / 2.0; - height = fabs(y_lin(ymax) - y_lin(ymin)) / 2.0; + if (codes == NULL) + { + memset(opcode, LINETO, n); + opcode[0] = MOVETO; + } + else + memmove(opcode, codes, n); - if (style != GKS_K_INTSTYLE_SOLID_WITH_BORDER) + for (i = 0; i < n; i++) { - start = min(a1, a2); - end = max(a1, a2); - start += ((int)(end - start)) / 360 * 360; - /* Ensure that two equivalent but unequal angles result in a full arc. */ - if (fabs(end - start) < FEPS && fabs(a1 - a2) > FEPS) + if (is_nan(vertices[i].x) || is_nan(vertices[i].y)) { - end += 360; + nan = 1; + continue; + } + else + { + opcode[j] = nan ? MOVETO : opcode[i]; + nan = 0; } + xpoint[j] = vertices[i].x; + ypoint[j] = vertices[i].y; + j++; + } - n = 0; - for (a = start; a <= end; a++) + for (i = 0; i < j; i++) + { + code = opcode[i]; + if (code == STOP) + break; + else if (code == MOVETO) { - x[n] = x_log(xcenter + width * cos(a * M_PI / 180)); - y[n] = y_log(ycenter + height * sin(a * M_PI / 180)); - n++; + closepath(fill); + addpath(xpoint[i], ypoint[i]); } - if (fabs((a - 1) - end) > FEPS) + else if (code == LINETO) + addpath(xpoint[i], ypoint[i]); + else if (code == CURVE3) { - x[n] = x_log(xcenter + width * cos(end * M_PI / 180)); - y[n] = y_log(ycenter + height * sin(end * M_PI / 180)); - n++; + quad_bezier(xpoint + i - 1, ypoint + i - 1, 20); + i += 1; } - - if (n > 1) + else if (code == CURVE4) { - polyline(n, x, y); + cubic_bezier(xpoint + i - 1, ypoint + i - 1, 20); + i += 2; + } + else if (code == CLOSEPOLY) + { + addpath(xpoint[i], ypoint[i]); + closepath(fill); } } - else - { - x[0] = xcenter + width * cos(a1); - y[0] = ycenter + height * sin(a1); - x[1] = width; - y[1] = height; - x[2] = a1 * M_PI / 180; - y[2] = a2 * M_PI / 180; - x[3] = y[3] = 0; - - gks_gdp(4, x, y, GKS_K_GDP_DRAW_PATH, 3, codes); - } + closepath(fill); if (flag_graphics) { - gr_writestream("\n", - xmin, xmax, ymin, ymax, a1, a2); + gr_writestream("\n", fill); } } /*! - * Fill a circular or elliptical arc covering the specified rectangle. + * Set the arrow style to be used for subsequent arrow commands. * - * \param[in] xmin Left edge of the rectangle - * \param[in] xmax Right edge of the rectangle - * \param[in] ymin Bottom edge of the rectangle - * \param[in] ymax Upper edge of the rectangle - * \param[in] a1 The start angle - * \param[in] a2 The end angle + * \param[in] style The arrow style to be used * - * The resulting arc begins at a1 and ends at a2 degrees. Angles are - * interpreted such that 0 degrees is at the 3 o'clock position. The center of - * the arc is the center of the given rectangle. + * This function defines the arrow style for subsequent arrow primitives. + * The default arrow style is 1. + * + * \verbatim embed:rst:leading-asterisk + * + * +---+----------------------------------+ + * | 1|simple, single-ended | + * +---+----------------------------------+ + * | 2|simple, single-ended, acute head | + * +---+----------------------------------+ + * | 3|hollow, single-ended | + * +---+----------------------------------+ + * | 4|filled, single-ended | + * +---+----------------------------------+ + * | 5|triangle, single-ended | + * +---+----------------------------------+ + * | 6|filled triangle, single-ended | + * +---+----------------------------------+ + * | 7|kite, single-ended | + * +---+----------------------------------+ + * | 8|filled kite, single-ended | + * +---+----------------------------------+ + * | 9|simple, double-ended | + * +---+----------------------------------+ + * | 10|simple, double-ended, acute head | + * +---+----------------------------------+ + * | 11|hollow, double-ended | + * +---+----------------------------------+ + * | 12|filled, double-ended | + * +---+----------------------------------+ + * | 13|triangle, double-ended | + * +---+----------------------------------+ + * | 14|filled triangle, double-ended | + * +---+----------------------------------+ + * | 15|kite, double-ended | + * +---+----------------------------------+ + * | 16|filled kite, double-ended | + * +---+----------------------------------+ + * | 17|double line, single-ended | + * +---+----------------------------------+ + * | 18|double line, double-ended | + * +---+----------------------------------+ + * + * \endverbatim */ -void gr_fillarc(double xmin, double xmax, double ymin, double ymax, double a1, double a2) +void gr_setarrowstyle(int style) +{ + check_autoinit; + + if (style >= 1 && style <= 18) arrow_style = style - 1; + + if (flag_graphics) gr_writestream("\n", style); +} + +/*! + * Set the arrow size to be used for subsequent arrow commands. + * + * \param[in] size The arrow size to be used + * + * This function defines the arrow size for subsequent arrow primitives. + * The default arrow size is 1. + */ +void gr_setarrowsize(double size) +{ + check_autoinit; + + if (arrow_size > 0) arrow_size = size; + + if (flag_graphics) gr_writestream("\n", size); +} + +/*! + * Draw an arrow between two points. + * + * \param[in] x1 The X coordinate of the arrow start point (tail) + * \param[in] y1 The Y coordinate of the arrow start point (tail) + * \param[in] x2 The X coordinate of the arrow end point (head) + * \param[in] y2 The Y coordinate of the arrow end point (head) + * + * Different arrow styles (angles between arrow tail and wing, optionally filled + * heads, double headed arrows) are available and can be set with the + * gr_setarrowstyle function. + */ +void gr_drawarrow(double x1, double y1, double x2, double y2) { - int errind, style; - double xcenter, ycenter, width, height, start, end, a; - int n; - double bwidth, x[362], y[362]; - int codes[3] = {'M', 'A', 'f'}; + double xs, ys, xe, ye; + int errind, ltype, intstyle, tnr; + double a, c, xc, yc, f, fh; + int fill, i, j, n; + double xi, yi, x[10], y[10]; check_autoinit; - gks_inq_fill_int_style(&errind, &style); - - xcenter = (x_lin(xmin) + x_lin(xmax)) / 2.0; - ycenter = (y_lin(ymin) + y_lin(ymax)) / 2.0; - width = fabs(x_lin(xmax) - x_lin(xmin)) / 2.0; - height = fabs(y_lin(ymax) - y_lin(ymin)) / 2.0; + gks_inq_pline_linetype(&errind, <ype); + gks_inq_fill_int_style(&errind, &intstyle); + gks_inq_current_xformno(&errind, &tnr); - if (style != GKS_K_INTSTYLE_SOLID_WITH_BORDER) + if (tnr != NDC) { - start = min(a1, a2); - end = max(a1, a2); - start += ((int)(end - start)) / 360 * 360; - /* Ensure that two equivalent but unequal angles result in a full arc. */ - if (fabs(end - start) < FEPS && fabs(a1 - a2) > FEPS) - { - end += 360; - } - - x[0] = x_log(xcenter); - y[0] = x_log(ycenter); - n = 1; - for (a = start; a <= end; a++) - { - x[n] = x_log(xcenter + width * cos(a * M_PI / 180)); - y[n] = y_log(ycenter + height * sin(a * M_PI / 180)); - n++; - } - if (fabs((a - 1) - end) > FEPS) - { - x[n] = x_log(xcenter + width * cos(end * M_PI / 180)); - y[n] = y_log(ycenter + height * sin(end * M_PI / 180)); - n++; - } - - if (n > 2) - { - fillarea(n, x, y); - } + xs = nx.a * x_lin(x1) + nx.b; + ys = nx.c * y_lin(y1) + nx.d; + xe = nx.a * x_lin(x2) + nx.b; + ye = nx.c * y_lin(y2) + nx.d; } else { - x[0] = xcenter + width * cos(a1); - y[0] = ycenter + height * sin(a1); - x[1] = width; - y[1] = height; - x[2] = a1 * M_PI / 180; - y[2] = a2 * M_PI / 180; - x[3] = y[3] = 0; + xs = x1; + ys = y1; + xe = x2; + ye = y2; + } - gr_inqborderwidth(&bwidth); - if (bwidth != 0) codes[2] = 'F'; + gks_set_fill_int_style(GKS_K_INTSTYLE_SOLID); - gks_gdp(4, x, y, GKS_K_GDP_DRAW_PATH, 3, codes); - } + c = sqrt((xe - xs) * (xe - xs) + (ye - ys) * (ye - ys)); + if (ys != ye) + a = acos(fabs(xe - xs) / c); + else + a = 0; + if (ye < ys) a = 2 * M_PI - a; + if (xe < xs) a = M_PI - a; + a -= M_PI / 2; - if (flag_graphics) + xc = (xs + xe) / 2; + yc = (ys + ye) / 2; + f = 0.01 * c / 2; + fh = 0.15 / c * arrow_size; + + j = 0; + while ((n = vertex_list[arrow_style][j++]) != 0) { - gr_writestream("\n", - xmin, xmax, ymin, ymax, a1, a2); + fill = n < 0; + n = abs(n); + gks_set_pline_linetype(n > 2 ? GKS_K_LINETYPE_SOLID : ltype); + for (i = 0; i < n; i++) + { + xi = vertex_list[arrow_style][j++]; + yi = vertex_list[arrow_style][j++]; + xi *= fh; + if (yi < 0) + yi = (yi + 100) * fh - 100; + else + yi = (yi - 100) * fh + 100; + xi *= f; + yi *= f; + x[i] = xc + cos(a) * xi - sin(a) * yi; + y[i] = yc + sin(a) * xi + cos(a) * yi; + if (tnr != NDC) + { + x[i] = (x[i] - nx.b) / nx.a; + y[i] = (y[i] - nx.d) / nx.c; + if (lx.scale_options) + { + x[i] = x_log(x[i]); + y[i] = y_log(y[i]); + } + } + } + if (fill) + gks_fillarea(n, x, y); + else + gks_polyline(n, x, y); } -} -static void addpath(double x, double y) -{ - xpath[npath] = x; - ypath[npath] = y; - npath += 1; -} + gks_set_fill_int_style(intstyle); + gks_set_pline_linetype(ltype); -static void closepath(int fill) -{ - if (fill) - { - if (npath > 2) gks_fillarea(npath, xpath, ypath); - } - else if (npath > 1) - gks_polyline(npath, xpath, ypath); - npath = 0; + if (flag_graphics) gr_writestream("\n", x1, y1, x2, y2); } -static void quad_bezier(double x[3], double y[3], int n) +static void drawimage_calculation(double xmin, double xmax, double ymin, double ymax, int width, int height, int *data, + int model) { - int i; - double t, a, b, c; - - if (npath + n >= maxpath) reallocate(npath + n); + int *img = data, *imgT; + int n, i, j, w, h; + double hue, saturation, value, red, green, blue, x, y; - for (i = 0; i < n; i++) + if (model == MODEL_HSV) { - t = (double)i / (n - 1); - a = pow((1.0 - t), 2.0); - b = 2.0 * t * (1.0 - t); - c = pow(t, 2.0); - addpath(a * x[0] + b * x[1] + c * x[2], a * y[0] + b * y[1] + c * y[2]); + n = width * height; + img = (int *)xmalloc(n * sizeof(int)); + for (i = 0; i < n; i++) + { + hue = (data[i] & 0xff) / 255.0; + saturation = ((data[i] & 0xff00) >> 8) / 255.0; + value = ((data[i] & 0xff0000) >> 16) / 255.0; + gr_hsvtorgb(hue, saturation, value, &red, &green, &blue); + img[i] = (data[i] & 0xff000000) | ((int)(red * 255) << 16) | ((int)(green * 255) << 8) | ((int)(blue * 255)); + } } -} -static void cubic_bezier(double x[4], double y[4], int n) -{ - int i; - double t, a, b, c, d; - - if (npath + n >= maxpath) reallocate(npath + n); - - for (i = 0; i < n; i++) + if (lx.scale_options != 0) { - t = (double)i / (n - 1); - a = pow((1.0 - t), 3.0); - b = 3.0 * t * pow((1.0 - t), 2.0); - c = 3.0 * pow(t, 2.0) * (1.0 - t); - d = pow(t, 3.0); - addpath(a * x[0] + b * x[1] + c * x[2] + d * x[3], a * y[0] + b * y[1] + c * y[2] + d * y[3]); + w = max(width, 500); + h = max(height, 500); + linear_xform lx_original = lx; + lx.xmin = xmin; + lx.xmax = xmax; + lx.a = (xmax - xmin) / log10(xmax / xmin); + lx.b = xmin - lx.a * log10(xmin); + lx.ymin = ymin; + lx.ymax = ymax; + lx.c = (ymax - ymin) / log10(ymax / ymin); + lx.d = ymin - lx.c * log10(ymin); + imgT = (int *)xmalloc(w * h * sizeof(int)); + for (i = 0; i < w; i++) + { + if (w > 1) + { + x = (x_log(xmin + i * (xmax - xmin) / (w - 1)) - xmin) / (xmax - xmin); + if (x < 0) + x = 0; + else if (x > 1) + x = 1; + } + else + x = 0; + for (j = 0; j < h; j++) + { + if (h > 1) + { + y = (y_log(ymin + (h - 1 - j) * (ymax - ymin) / (h - 1)) - ymin) / (ymax - ymin); + if (y < 0) + y = 0; + else if (y > 1) + y = 1; + } + else + y = 0; + imgT[i + j * w] = img[(int)min(x * width, width - 1) + (int)min((1 - y) * height, height - 1) * width]; + } + } + lx = lx_original; + if (lx.scale_options & OPTION_FLIP_X) + { + double t = xmin; + xmin = xmax; + xmax = t; + } + if (lx.scale_options & OPTION_FLIP_Y) + { + double t = ymin; + ymin = ymax; + ymax = t; + } + gks_draw_image(x_lin(xmin), y_lin(ymax), x_lin(xmax), y_lin(ymin), w, h, imgT); + free(imgT); } + else + gks_draw_image(xmin, ymax, xmax, ymin, width, height, img); } /*! - * Draw simple and compound outlines consisting of line segments and bezier - * curves. + * Draw an image into a given rectangular area. * - * \param[in] n The number of vertices - * \param[in] vertices A pointer to the vertices - * \param[in] codes A pointer to the path codes - * \param[in] fill A flag indication whether resulting path is to be filled or - * not + * \param[in] xmin X coordinate of the lower left point of the rectangle + * \param[in] ymin Y coordinate of the lower left point of the rectangle + * \param[in] xmax X coordinate of the upper right point of the rectangle + * \param[in] ymax Y coordinate of the upper right point of the rectangle + * \param[in] width X dimension of the color index array + * \param[in] height Y dimension of the color index array + * \param[in] data color array + * \param[in] model color model + * + * The points (xmin, ymin) and (xmax, ymax) are world coordinates defining + * diagonally opposite corner points of a rectangle. This rectangle is divided + * into width by height cells. The two-dimensional array data specifies colors + * for each cell. * * \verbatim embed:rst:leading-asterisk * - * The following path codes are recognized: + * The available color models are: * - * +----------+-----------------------------------------------------------+ - * | STOP|end the entire path | - * +----------+-----------------------------------------------------------+ - * | MOVETO|move to the given vertex | - * +----------+-----------------------------------------------------------+ - * | LINETO|draw a line from the current position to the given vertex | - * +----------+-----------------------------------------------------------+ - * | CURVE3|draw a quadratic Bezier curve | - * +----------+-----------------------------------------------------------+ - * | CURVE4|draw a cubic Bezier curve | - * +----------+-----------------------------------------------------------+ - * | CLOSEPOLY|draw a line segment to the start point of the current path | - * +----------+-----------------------------------------------------------+ + * +-----------------------+---+-----------+ + * |MODEL_RGB | 0| AABBGGRR| + * +-----------------------+---+-----------+ + * |MODEL_HSV | 1| AAVVSSHH| + * +-----------------------+---+-----------+ * * \endverbatim */ -void gr_drawpath(int n, vertex_t *vertices, unsigned char *codes, int fill) +void gr_drawimage(double xmin, double xmax, double ymin, double ymax, int width, int height, int *data, int model) { - int i, j = 0, code, nan = 0; + int n; check_autoinit; - if (n >= maxpath) reallocate(n); + drawimage_calculation(xmin, xmax, ymin, ymax, width, height, data, model); - if (codes == NULL) + if (flag_graphics) { - memset(opcode, LINETO, n); - opcode[0] = MOVETO; + n = width * height; + gr_writestream("\n", model); } - else - memmove(opcode, codes, n); +} - for (i = 0; i < n; i++) +/*! + * Allows drawing of shadows, realized by images painted underneath, + * and offset from, graphics objects such that the shadow mimics the effect of + * a light source cast on the graphics objects. + * + * \param[in] offsetx An x-offset, which specifies how far in the horizontal + * direction the shadow is offset from the object + * \param[in] offsety A y-offset, which specifies how far in the vertical + * direction the shadow is offset from the object + * \param[in] blur A blur value, which specifies whether the object has a + * hard or a diffuse edge + */ +void gr_setshadow(double offsetx, double offsety, double blur) +{ + check_autoinit; + + gks_set_shadow(offsetx, offsety, blur); +} + +/*! + * Set the value of the alpha component associated with GR colors. + * + * \param[in] alpha An alpha value (0.0 - 1.0) + */ +void gr_settransparency(double alpha) +{ + check_autoinit; + + gks_set_transparency(alpha); +} + +/*! + * Change the coordinate transformation according to the given matrix. + * + * \param[in] mat 2D transformation matrix + */ +void gr_setcoordxform(double mat[3][2]) +{ + check_autoinit; + + gks_set_coord_xform(mat); +} + +/*! + * Open a file for graphics output. + * + * \param[in] path Filename for the graphics file. + * + * gr_begingraphics allows to write all graphics output into a XML-formatted + * file until the gr_endgraphics functions is called. The resulting file may + * later be imported with the gr_importgraphics function. + */ +void gr_begingraphics(char *path) +{ + if (!flag_graphics) { - if (is_nan(vertices[i].x) || is_nan(vertices[i].y)) + if (gr_openstream(path) == 0) { - nan = 1; - continue; + gr_writestream(XML_HEADER); + gr_writestream(GR_HEADER); + flag_graphics = 1; } else - { - opcode[j] = nan ? MOVETO : opcode[i]; - nan = 0; - } - xpoint[j] = vertices[i].x; - ypoint[j] = vertices[i].y; - j++; + fprintf(stderr, "%s: open failed\n", path); } +} - for (i = 0; i < j; i++) +void gr_endgraphics(void) +{ + if (flag_graphics) { - code = opcode[i]; - if (code == STOP) - break; - else if (code == MOVETO) + gr_writestream(GR_TRAILER); + gr_closestream(); + flag_graphics = 0; + } +} + +static void latex2image(char *string, int pointSize, double *rgb, int *width, int *height, int **data) +{ + int color; + char s[FILENAME_MAX], path[FILENAME_MAX], cache[33]; + char *tmp, *temp, *null, cmd[1024]; + static char *preamble = NULL; + char tex[FILENAME_MAX], dvi[FILENAME_MAX], png[FILENAME_MAX]; + FILE *stream; + int math, ret; +#ifdef _WIN32 + wchar_t w_path[MAX_PATH]; +#endif + + color = ((int)(rgb[0] * 255)) + ((int)(rgb[1] * 255) << 8) + ((int)(rgb[2] * 255) << 16) + (255 << 24); + sprintf(s, "%d%x%s", pointSize, color, string); + md5(s, cache); +#ifdef _WIN32 + temp = (char *)gks_getenv("TEMP"); +#else + temp = TMPDIR; +#endif + sprintf(path, "%s%sgr-cache-%s.png", temp, DIRDELIM, cache); + +#ifdef _WIN32 + MultiByteToWideChar(CP_UTF8, 0, path, strlen(path) + 1, w_path, MAX_PATH); + if (_waccess(w_path, R_OK) != 0) +#else + if (access(path, R_OK) != 0) +#endif + { + math = strstr(string, "\\(") == NULL; +#ifdef _WIN32 + tmp = cache; + temp = "."; +#else +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + tmp = tempnam(temp, NULL); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#endif + sprintf(tex, "%s.tex", tmp); + sprintf(dvi, "%s.dvi", tmp); + sprintf(png, "%s.png", tmp); +#ifdef _WIN32 + null = "NUL"; + MultiByteToWideChar(CP_UTF8, 0, tex, strlen(tex) + 1, w_path, MAX_PATH); + stream = _wfopen(w_path, L"w"); +#else + null = "/dev/null"; + stream = fopen(tex, "w"); +#endif + if (preamble == NULL) { - closepath(fill); - addpath(xpoint[i], ypoint[i]); + preamble = (char *)gks_getenv("GR_LATEX_PREAMBLE"); } - else if (code == LINETO) - addpath(xpoint[i], ypoint[i]); - else if (code == CURVE3) + if (preamble != NULL) { - quad_bezier(xpoint + i - 1, ypoint + i - 1, 20); - i += 1; + if (strcmp(preamble, "AMS") == 0) + { + preamble = "\ +\\documentclass{article}\n\ +\\pagestyle{empty}\n\ +\\usepackage{amssymb}\n\ +\\usepackage{amsmath}\n\ +\\usepackage[dvips]{color}\n\ +\\begin{document}\n"; + } } - else if (code == CURVE4) + else { - cubic_bezier(xpoint + i - 1, ypoint + i - 1, 20); - i += 2; + preamble = "\ +\\documentclass{article}\n\ +\\pagestyle{empty}\n\ +\\usepackage[dvips]{color}\n\ +\\begin{document}\n"; } - else if (code == CLOSEPOLY) + fprintf(stream, "%s", preamble); + if (math) fprintf(stream, "\\[\n"); + fprintf(stream, "\\color[rgb]{%.3f,%.3f,%.3f} {\n", rgb[0], rgb[1], rgb[2]); + fwrite(string, strlen(string), 1, stream); + fprintf(stream, "}\n"); + if (math) fprintf(stream, "\\]\n"); + fprintf(stream, "\\end{document}"); + fclose(stream); + + sprintf(cmd, "latex -interaction=batchmode -halt-on-error -output-directory=%s %s >%s", temp, tex, null); + ret = system(cmd); + +#ifdef _WIN32 + MultiByteToWideChar(CP_UTF8, 0, dvi, strlen(dvi) + 1, w_path, MAX_PATH); + if (ret == 0 && _waccess(w_path, R_OK) == 0) +#else + if (ret == 0 && access(dvi, R_OK) == 0) +#endif { - addpath(xpoint[i], ypoint[i]); - closepath(fill); + sprintf(cmd, "dvipng -bg transparent -q -T tight -x %d %s -o %s >%s", pointSize * 100, dvi, png, null); + ret = system(cmd); + if (ret == 0) + { + rename(png, path); +#ifdef _WIN32 + sprintf(cmd, "DEL %s.*", tmp); +#else + sprintf(cmd, "rm -f %s.*", tmp); +#endif + ret = system(cmd); + if (ret != 0) fprintf(stderr, "error deleting temprorary files\n"); + } + else + fprintf(stderr, "dvipng: PNG conversion failed\n"); } + else + fprintf(stderr, "latex: failed to create a dvi file\n"); } - closepath(fill); - if (flag_graphics) +#ifdef _WIN32 + MultiByteToWideChar(CP_UTF8, 0, path, strlen(path) + 1, w_path, MAX_PATH); + if (_waccess(w_path, R_OK) == 0) +#else + if (access(path, R_OK) == 0) +#endif { - gr_writestream("\n", fill); + gr_readimage(path, width, height, data); } } -/*! - * Set the arrow style to be used for subsequent arrow commands. - * - * \param[in] style The arrow style to be used - * - * This function defines the arrow style for subsequent arrow primitives. - * The default arrow style is 1. - * - * \verbatim embed:rst:leading-asterisk - * - * +---+----------------------------------+ - * | 1|simple, single-ended | - * +---+----------------------------------+ - * | 2|simple, single-ended, acute head | - * +---+----------------------------------+ - * | 3|hollow, single-ended | - * +---+----------------------------------+ - * | 4|filled, single-ended | - * +---+----------------------------------+ - * | 5|triangle, single-ended | - * +---+----------------------------------+ - * | 6|filled triangle, single-ended | - * +---+----------------------------------+ - * | 7|kite, single-ended | - * +---+----------------------------------+ - * | 8|filled kite, single-ended | - * +---+----------------------------------+ - * | 9|simple, double-ended | - * +---+----------------------------------+ - * | 10|simple, double-ended, acute head | - * +---+----------------------------------+ - * | 11|hollow, double-ended | - * +---+----------------------------------+ - * | 12|filled, double-ended | - * +---+----------------------------------+ - * | 13|triangle, double-ended | - * +---+----------------------------------+ - * | 14|filled triangle, double-ended | - * +---+----------------------------------+ - * | 15|kite, double-ended | - * +---+----------------------------------+ - * | 16|filled kite, double-ended | - * +---+----------------------------------+ - * | 17|double line, single-ended | - * +---+----------------------------------+ - * | 18|double line, double-ended | - * +---+----------------------------------+ - * - * \endverbatim - */ -void gr_setarrowstyle(int style) +int *rotl90(int m, int n, int *mat) { - check_autoinit; + int *trans = (int *)xcalloc(m * n, sizeof(int)); + int i, j; - if (style >= 1 && style <= 18) arrow_style = style - 1; + for (j = 0; j < n; j++) + for (i = 0; i < m; i++) trans[(m - 1 - i) * n + j] = mat[j * m + i]; - if (flag_graphics) gr_writestream("\n", style); + return trans; } -/*! - * Set the arrow size to be used for subsequent arrow commands. - * - * \param[in] size The arrow size to be used - * - * This function defines the arrow size for subsequent arrow primitives. - * The default arrow size is 1. - */ -void gr_setarrowsize(double size) +static int *rot180(int m, int n, int *mat) { - check_autoinit; + int *trans = (int *)xcalloc(m * n, sizeof(int)); + int i, j; - if (arrow_size > 0) arrow_size = size; + for (j = 0; j < n; j++) + for (i = 0; i < m; i++) trans[(n - 1 - j) * m + m - 1 - i] = mat[j * m + i]; - if (flag_graphics) gr_writestream("\n", size); + return trans; } -/*! - * Draw an arrow between two points. - * - * \param[in] x1 The X coordinate of the arrow start point (tail) - * \param[in] y1 The Y coordinate of the arrow start point (tail) - * \param[in] x2 The X coordinate of the arrow end point (head) - * \param[in] y2 The Y coordinate of the arrow end point (head) - * - * Different arrow styles (angles between arrow tail and wing, optionally filled - * heads, double headed arrows) are available and can be set with the - * gr_setarrowstyle function. - */ -void gr_drawarrow(double x1, double y1, double x2, double y2) +static int *rotr90(int m, int n, int *mat) { - double xs, ys, xe, ye; - int errind, ltype, intstyle, tnr; - double a, c, xc, yc, f, fh; - int fill, i, j, n; - double xi, yi, x[10], y[10]; - - check_autoinit; + int *trans = (int *)xcalloc(m * n, sizeof(int)); + int i, j; - gks_inq_pline_linetype(&errind, <ype); - gks_inq_fill_int_style(&errind, &intstyle); - gks_inq_current_xformno(&errind, &tnr); + for (j = 0; j < n; j++) + for (i = 0; i < m; i++) trans[i * n + n - 1 - j] = mat[j * m + i]; - if (tnr != NDC) - { - xs = nx.a * x_lin(x1) + nx.b; - ys = nx.c * y_lin(y1) + nx.d; - xe = nx.a * x_lin(x2) + nx.b; - ye = nx.c * y_lin(y2) + nx.d; - } - else - { - xs = x1; - ys = y1; - xe = x2; - ye = y2; - } + return trans; +} - gks_set_fill_int_style(GKS_K_INTSTYLE_SOLID); +static void mathtex(double x, double y, char *string, int inquire, double *tbx, double *tby) +{ + int wkid = 1, errind, conid, wtype, dcunit; + int pointSize, pixels, color; + double chh, rgb[3], ux, uy; + int width, height, *data = NULL, w, h, *trans = NULL; + double rad, rw, rh, rx, ry, xx, yy, bbx[4], bby[4]; + double x1, x2, y1, y2, midx, midy, sinf, cosf; + int i, j, ii, jj, angle, path, halign, valign, tnr; - c = sqrt((xe - xs) * (xe - xs) + (ye - ys) * (ye - ys)); - if (ys != ye) - a = acos(fabs(xe - xs) / c); + gks_inq_ws_conntype(wkid, &errind, &conid, &wtype); + gks_inq_max_ds_size(wtype, &errind, &dcunit, &rw, &rh, &width, &height); + if (sizex > 0) + pixels = sizex / rh * height; else - a = 0; - if (ye < ys) a = 2 * M_PI - a; - if (xe < xs) a = M_PI - a; - a -= M_PI / 2; + pixels = 500; + if (wtype == 101 || wtype == 102 || wtype == 120 || wtype == 382) pixels *= 8; - xc = (xs + xe) / 2; - yc = (ys + ye) / 2; - f = 0.01 * c / 2; - fh = 0.15 / c * arrow_size; + gks_inq_text_height(&errind, &chh); + gks_inq_text_color_index(&errind, &color); + gks_inq_color_rep(wkid, color, GKS_K_VALUE_SET, &errind, &rgb[0], &rgb[1], &rgb[2]); - j = 0; - while ((n = vertex_list[arrow_style][j++]) != 0) - { - fill = n < 0; - n = abs(n); - gks_set_pline_linetype(n > 2 ? GKS_K_LINETYPE_SOLID : ltype); - for (i = 0; i < n; i++) - { - xi = vertex_list[arrow_style][j++]; - yi = vertex_list[arrow_style][j++]; - xi *= fh; - if (yi < 0) - yi = (yi + 100) * fh - 100; - else - yi = (yi - 100) * fh + 100; - xi *= f; - yi *= f; - x[i] = xc + cos(a) * xi - sin(a) * yi; - y[i] = yc + sin(a) * xi + cos(a) * yi; - if (tnr != NDC) - { - x[i] = (x[i] - nx.b) / nx.a; - y[i] = (y[i] - nx.d) / nx.c; - if (lx.scale_options) - { - x[i] = x_log(x[i]); - y[i] = y_log(y[i]); - } - } - } - if (fill) - gks_fillarea(n, x, y); - else - gks_polyline(n, x, y); - } + pointSize = chh * pixels; + latex2image(string, pointSize, rgb, &width, &height, &data); - gks_set_fill_int_style(intstyle); - gks_set_pline_linetype(ltype); + gks_inq_text_upvec(&errind, &ux, &uy); + rad = -atan2(ux, uy); + angle = (int)(rad * 180 / M_PI + 0.5); + if (angle < 0) angle += 360; + path = ((angle + 45) / 90) % 4; - if (flag_graphics) gr_writestream("\n", x1, y1, x2, y2); -} + if (data != NULL) + { + rw = width / (double)pixels; + rh = height / (double)pixels; -static void drawimage_calculation(double xmin, double xmax, double ymin, double ymax, int width, int height, int *data, - int model) -{ - int *img = data, *imgT; - int n, i, j, w, h; - double hue, saturation, value, red, green, blue, x, y; + gks_inq_text_align(&errind, &halign, &valign); - if (model == MODEL_HSV) - { - n = width * height; - img = (int *)xmalloc(n * sizeof(int)); - for (i = 0; i < n; i++) + rx = x; + switch (halign) + { + case 2: + rx -= 0.5 * rw; + break; + case 3: + rx -= rw; + break; + } + ry = y; + switch (valign) + { + case 1: + ry -= rh - chh * 0.04; + break; + case 2: + ry -= rh; + break; + case 3: + ry -= 0.5 * rh; + break; + case 5: + ry -= chh * 0.04; + break; + } + bbx[0] = rx; + bbx[1] = rx + rw; + bbx[2] = bbx[1]; + bbx[3] = bbx[0]; + bby[0] = ry; + bby[1] = bby[0]; + bby[2] = ry + rh; + bby[3] = bby[2]; + + x1 = y1 = FLT_MAX; + x2 = y2 = -FLT_MAX; + + for (i = 0; i < 4; i++) { - hue = (data[i] & 0xff) / 255.0; - saturation = ((data[i] & 0xff00) >> 8) / 255.0; - value = ((data[i] & 0xff0000) >> 16) / 255.0; - gr_hsvtorgb(hue, saturation, value, &red, &green, &blue); - img[i] = (data[i] & 0xff000000) | ((int)(red * 255) << 16) | ((int)(green * 255) << 8) | ((int)(blue * 255)); + xx = bbx[i] - x; + yy = bby[i] - y; + bbx[i] = x + cos(rad) * xx - sin(rad) * yy; + bby[i] = y + sin(rad) * xx + cos(rad) * yy; + + x1 = min(x1, bbx[i]); + x2 = max(x2, bbx[i]); + y1 = min(y1, bby[i]); + y2 = max(y2, bby[i]); } - } - if (lx.scale_options != 0) - { - w = max(width, 500); - h = max(height, 500); - linear_xform lx_original = lx; - lx.xmin = xmin; - lx.xmax = xmax; - lx.a = (xmax - xmin) / log10(xmax / xmin); - lx.b = xmin - lx.a * log10(xmin); - lx.ymin = ymin; - lx.ymax = ymax; - lx.c = (ymax - ymin) / log10(ymax / ymin); - lx.d = ymin - lx.c * log10(ymin); - imgT = (int *)xmalloc(w * h * sizeof(int)); - for (i = 0; i < w; i++) + if (inquire) { - if (w > 1) + for (i = 0; i < 4; i++) { - x = (x_log(xmin + i * (xmax - xmin) / (w - 1)) - xmin) / (xmax - xmin); - if (x < 0) - x = 0; - else if (x > 1) - x = 1; + tbx[i] = bbx[i]; + tby[i] = bby[i]; } - else - x = 0; - for (j = 0; j < h; j++) + } + else + { + gks_inq_current_xformno(&errind, &tnr); + if (tnr != NDC) gks_select_xform(NDC); + + if (angle % 90 == 0) { - if (h > 1) + switch (path) { - y = (y_log(ymin + (h - 1 - j) * (ymax - ymin) / (h - 1)) - ymin) / (ymax - ymin); - if (y < 0) - y = 0; - else if (y > 1) - y = 1; + case 0: + gks_draw_image(x1, y2, x2, y1, width, height, data); + break; + case 1: + trans = rotl90(width, height, data); + gks_draw_image(x1, y2, x2, y1, height, width, trans); + free(trans); + break; + case 2: + trans = rot180(width, height, data); + gks_draw_image(x1, y2, x2, y1, width, height, trans); + free(trans); + break; + case 3: + trans = rotr90(width, height, data); + gks_draw_image(x1, y2, x2, y1, height, width, trans); + free(trans); + break; } - else - y = 0; - imgT[i + j * w] = img[(int)min(x * width, width - 1) + (int)min((1 - y) * height, height - 1) * width]; } + else + { + w = (int)((x2 - x1) * pixels + 0.5), h = (int)((y2 - y1) * pixels + 0.5); + trans = (int *)xcalloc(w * h, sizeof(int)); + midx = ceil(0.5 * w); + midy = ceil(0.5 * h); + sinf = sin(rad); + cosf = cos(rad); + for (j = 0; j < h; j++) + for (i = 0; i < w; i++) + { + xx = (i - midx) * cosf - (j - midy) * sinf; + yy = (i - midx) * sinf + (j - midy) * cosf; + ii = round(xx) + ceil(0.5 * width); + jj = round(yy) + ceil(0.5 * height); + if (ii >= 0 && jj >= 0 && ii < width && jj < height) trans[j * w + i] = data[jj * width + ii]; + } + gks_draw_image(x1, y2, x2, y1, w, h, trans); + free(trans); + } + + if (tnr != NDC) gks_select_xform(tnr); } - lx = lx_original; - if (lx.scale_options & OPTION_FLIP_X) - { - double t = xmin; - xmin = xmax; - xmax = t; - } - if (lx.scale_options & OPTION_FLIP_Y) - { - double t = ymin; - ymin = ymax; - ymax = t; - } - gks_draw_image(x_lin(xmin), y_lin(ymax), x_lin(xmax), y_lin(ymin), w, h, imgT); - free(imgT); + + free(data); } - else - gks_draw_image(xmin, ymax, xmax, ymin, width, height, img); } +void mathtex2(double x, double y, const char *formula, int inquire, double *tbx, double *tby, double *baseline); + /*! - * Draw an image into a given rectangular area. - * - * \param[in] xmin X coordinate of the lower left point of the rectangle - * \param[in] ymin Y coordinate of the lower left point of the rectangle - * \param[in] xmax X coordinate of the upper right point of the rectangle - * \param[in] ymax Y coordinate of the upper right point of the rectangle - * \param[in] width X dimension of the color index array - * \param[in] height Y dimension of the color index array - * \param[in] data color array - * \param[in] model color model - * - * The points (xmin, ymin) and (xmax, ymax) are world coordinates defining - * diagonally opposite corner points of a rectangle. This rectangle is divided - * into width by height cells. The two-dimensional array data specifies colors - * for each cell. - * - * \verbatim embed:rst:leading-asterisk - * - * The available color models are: - * - * +-----------------------+---+-----------+ - * |MODEL_RGB | 0| AABBGGRR| - * +-----------------------+---+-----------+ - * |MODEL_HSV | 1| AAVVSSHH| - * +-----------------------+---+-----------+ + * Generate a character string starting at the given location. Strings can be + * defined to create mathematical symbols and Greek letters using LaTeX syntax. * - * \endverbatim + * \param[in] x The X coordinate of the starting position of the text string + * \param[in] y The Y coordinate of the starting position of the text string + * \param[in] string The text string to be drawn */ -void gr_drawimage(double xmin, double xmax, double ymin, double ymax, int width, int height, int *data, int model) +void gr_mathtex(double x, double y, char *string) { - int n; + int unused; + int prec; check_autoinit; - drawimage_calculation(xmin, xmax, ymin, ymax, width, height, data, model); - - if (flag_graphics) + gks_inq_text_fontprec(&unused, &unused, &prec); + if (prec == 3) { - n = width * height; - gr_writestream("\n", model); + mathtex2(x, y, string, 0, NULL, NULL, NULL); + } + else + { + mathtex(x, y, string, 0, NULL, NULL); } -} - -/*! - * Allows drawing of shadows, realized by images painted underneath, - * and offset from, graphics objects such that the shadow mimics the effect of - * a light source cast on the graphics objects. - * - * \param[in] offsetx An x-offset, which specifies how far in the horizontal - * direction the shadow is offset from the object - * \param[in] offsety A y-offset, which specifies how far in the vertical - * direction the shadow is offset from the object - * \param[in] blur A blur value, which specifies whether the object has a - * hard or a diffuse edge - */ -void gr_setshadow(double offsetx, double offsety, double blur) -{ - check_autoinit; - gks_set_shadow(offsetx, offsety, blur); + if (flag_graphics) gr_writestream("\n", x, y, string); } -/*! - * Set the value of the alpha component associated with GR colors. - * - * \param[in] alpha An alpha value (0.0 - 1.0) - */ -void gr_settransparency(double alpha) +void gr_inqmathtex(double x, double y, char *string, double *tbx, double *tby) { + int unused; + int prec; + check_autoinit; - gks_set_transparency(alpha); + gks_inq_text_fontprec(&unused, &unused, &prec); + if (prec == 3) + { + mathtex2(x, y, string, 1, tbx, tby, NULL); + } + else + { + mathtex(x, y, string, 1, tbx, tby); + } } -/*! - * Change the coordinate transformation according to the given matrix. - * - * \param[in] mat 2D transformation matrix - */ -void gr_setcoordxform(double mat[3][2]) +static void append(double x, double y, char *string, int line_number, int math) { - check_autoinit; + text_node_t *prev = text; + int errInd, n, wkId, font, prec; + double cpx, cpy, tbx[4], tby[4]; + char *src, *dest; - gks_set_coord_xform(mat); -} + if (*string == '\0') return; -/*! - * Open a file for graphics output. - * - * \param[in] path Filename for the graphics file. - * - * gr_begingraphics allows to write all graphics output into a XML-formatted - * file until the gr_endgraphics functions is called. The resulting file may - * later be imported with the gr_importgraphics function. - */ -void gr_begingraphics(char *path) -{ - if (!flag_graphics) + text = (text_node_t *)calloc(1, sizeof(text_node_t)); + text->next = NULL; + if (head == NULL) + head = text; + else if (prev != NULL) + prev->next = text; + + text->x = x; + text->y = y; + text->string = (char *)calloc(strlen(string) + 1, sizeof(char)); + src = string; + dest = text->string; + while (*src) + { + if (*src == '$' && *(src + 1) == '$') src++; + *dest++ = *src++; + } + *dest = '\0'; + text->line_number = line_number; + text->line_width = 0; + text->math = math; + + gks_inq_open_ws(1, &errInd, &n, &wkId); + if (math) { - if (gr_openstream(path) == 0) + gks_inq_text_fontprec(&errInd, &font, &prec); + if (prec == 3) { - gr_writestream(XML_HEADER); - gr_writestream(GR_HEADER); - flag_graphics = 1; + mathtex2(0, 0, text->string, 1, tbx, tby, text->baseline); } else - fprintf(stderr, "%s: open failed\n", path); + { + mathtex(0, 0, text->string, 1, tbx, tby); + } } -} - -void gr_endgraphics(void) -{ - if (flag_graphics) + else { - gr_writestream(GR_TRAILER); - gr_closestream(); - flag_graphics = 0; + gks_inq_text_extent(wkId, 0, 0, text->string, &errInd, &cpx, &cpy, tbx, tby); } + + text->width = tbx[1] - tbx[0]; + text->height = tby[2] - tby[1]; } -static void latex2image(char *string, int pointSize, double *rgb, int *width, int *height, int **data) +static text_node_t *parse(double x, double y, char *string) { - int color; - char s[FILENAME_MAX], path[FILENAME_MAX], cache[33]; - char *tmp, *temp, *null, cmd[1024]; - static char *preamble = NULL; - char tex[FILENAME_MAX], dvi[FILENAME_MAX], png[FILENAME_MAX]; - FILE *stream; - int math, ret; -#ifdef _WIN32 - wchar_t w_path[MAX_PATH]; -#endif + char *s, *start, *end; + int line_number, math; - color = ((int)(rgb[0] * 255)) + ((int)(rgb[1] * 255) << 8) + ((int)(rgb[2] * 255) << 16) + (255 << 24); - sprintf(s, "%d%x%s", pointSize, color, string); - md5(s, cache); -#ifdef _WIN32 - temp = (char *)gks_getenv("TEMP"); -#else - temp = TMPDIR; -#endif - sprintf(path, "%s%sgr-cache-%s.png", temp, DIRDELIM, cache); + head = text = NULL; + line_number = 1; + math = 0; -#ifdef _WIN32 - MultiByteToWideChar(CP_UTF8, 0, path, strlen(path) + 1, w_path, MAX_PATH); - if (_waccess(w_path, R_OK) != 0) -#else - if (access(path, R_OK) != 0) -#endif + s = (char *)calloc(strlen(string) + 1, sizeof(char)); + strcpy(s, string); + + start = end = s; + while (*end) { - math = strstr(string, "\\(") == NULL; -#ifdef _WIN32 - tmp = cache; - temp = "."; -#else -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -#endif - tmp = tempnam(temp, NULL); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif -#endif - sprintf(tex, "%s.tex", tmp); - sprintf(dvi, "%s.dvi", tmp); - sprintf(png, "%s.png", tmp); -#ifdef _WIN32 - null = "NUL"; - MultiByteToWideChar(CP_UTF8, 0, tex, strlen(tex) + 1, w_path, MAX_PATH); - stream = _wfopen(w_path, L"w"); -#else - null = "/dev/null"; - stream = fopen(tex, "w"); -#endif - if (preamble == NULL) - { - preamble = (char *)gks_getenv("GR_LATEX_PREAMBLE"); - } - if (preamble != NULL) + if (*end == '\n') { - if (strcmp(preamble, "AMS") == 0) - { - preamble = "\ -\\documentclass{article}\n\ -\\pagestyle{empty}\n\ -\\usepackage{amssymb}\n\ -\\usepackage{amsmath}\n\ -\\usepackage[dvips]{color}\n\ -\\begin{document}\n"; - } + math = 0; + *end++ = '\0'; + append(x, y, start, line_number, math); + start = end; + line_number++; } - else + else if (!math && *end == '$' && *(end + 1) == '$') { - preamble = "\ -\\documentclass{article}\n\ -\\pagestyle{empty}\n\ -\\usepackage[dvips]{color}\n\ -\\begin{document}\n"; + end += 2; } - fprintf(stream, "%s", preamble); - if (math) fprintf(stream, "\\[\n"); - fprintf(stream, "\\color[rgb]{%.3f,%.3f,%.3f} {\n", rgb[0], rgb[1], rgb[2]); - fwrite(string, strlen(string), 1, stream); - fprintf(stream, "}\n"); - if (math) fprintf(stream, "\\]\n"); - fprintf(stream, "\\end{document}"); - fclose(stream); - - sprintf(cmd, "latex -interaction=batchmode -halt-on-error -output-directory=%s %s >%s", temp, tex, null); - ret = system(cmd); - -#ifdef _WIN32 - MultiByteToWideChar(CP_UTF8, 0, dvi, strlen(dvi) + 1, w_path, MAX_PATH); - if (ret == 0 && _waccess(w_path, R_OK) == 0) -#else - if (ret == 0 && access(dvi, R_OK) == 0) -#endif + else if (*end == '$') { - sprintf(cmd, "dvipng -bg transparent -q -T tight -x %d %s -o %s >%s", pointSize * 100, dvi, png, null); - ret = system(cmd); - if (ret == 0) - { - rename(png, path); -#ifdef _WIN32 - sprintf(cmd, "DEL %s.*", tmp); -#else - sprintf(cmd, "rm -f %s.*", tmp); -#endif - ret = system(cmd); - if (ret != 0) fprintf(stderr, "error deleting temprorary files\n"); - } - else - fprintf(stderr, "dvipng: PNG conversion failed\n"); + *end++ = '\0'; + append(x, y, start, line_number, math); + math = !math; + start = end; } else - fprintf(stderr, "latex: failed to create a dvi file\n"); + end++; } + append(x, y, start, line_number, math); + free(s); -#ifdef _WIN32 - MultiByteToWideChar(CP_UTF8, 0, path, strlen(path) + 1, w_path, MAX_PATH); - if (_waccess(w_path, R_OK) == 0) -#else - if (access(path, R_OK) == 0) -#endif - { - gr_readimage(path, width, height, data); - } + return head; } -int *rotl90(int m, int n, int *mat) +static void text_impl(double x, double y, char *string, int inquire, double *tbx, double *tby) { - int *trans = (int *)xcalloc(m * n, sizeof(int)); - int i, j; + int errInd, hAlign, vAlign; + double chuX, chuY, angle, charHeight, xOff, yOff, lineWidth, lineHeight; + text_node_t *textP, *p; + int lineNumber = 1; + double totalWidth = 0, totalHeight = 0, *baseLine; + double xx, yy, sx, sy; + int i; - for (j = 0; j < n; j++) - for (i = 0; i < m; i++) trans[(m - 1 - i) * n + j] = mat[j * m + i]; + gks_inq_text_upvec(&errInd, &chuX, &chuY); + gks_set_text_upvec(0, 1); + angle = -atan2(chuX, chuY); - return trans; -} + gks_inq_text_height(&errInd, &charHeight); -static int *rot180(int m, int n, int *mat) -{ - int *trans = (int *)xcalloc(m * n, sizeof(int)); - int i, j; + gks_inq_text_align(&errInd, &hAlign, &vAlign); + gks_set_text_align(GKS_K_TEXT_HALIGN_LEFT, GKS_K_TEXT_VALIGN_HALF); - for (j = 0; j < n; j++) - for (i = 0; i < m; i++) trans[(n - 1 - j) * m + m - 1 - i] = mat[j * m + i]; + text = textP = parse(x, y, string); + yOff = 0; + while (textP != NULL) + { + lineWidth = 0; + lineHeight = 0; + p = textP; + while (p != NULL) + { + if (p->line_number != lineNumber) break; + lineHeight = max(p->height, lineHeight); + lineWidth += p->width; + p = p->next; + } + xOff = 0; + yOff += 0.5 * lineHeight; - return trans; -} + totalWidth = max(totalWidth, lineWidth); + totalHeight += lineHeight; -static int *rotr90(int m, int n, int *mat) -{ - int *trans = (int *)xcalloc(m * n, sizeof(int)); - int i, j; + while (textP != NULL && textP->line_number == lineNumber) + { + textP->x += xOff; + textP->y -= yOff; - for (j = 0; j < n; j++) - for (i = 0; i < m; i++) trans[i * n + n - 1 - j] = mat[j * m + i]; + xOff += textP->width; + totalWidth = max(totalWidth, xOff); - return trans; -} + textP->line_width = lineWidth; + textP = textP->next; + } + yOff += 0.5 * lineHeight; + lineNumber += 1; + } -static void mathtex(double x, double y, char *string, int inquire, double *tbx, double *tby) -{ - int wkid = 1, errind, conid, wtype, dcunit; - int pointSize, pixels, color; - double chh, rgb[3], ux, uy; - int width, height, *data = NULL, w, h, *trans = NULL; - double rad, rw, rh, rx, ry, xx, yy, bbx[4], bby[4]; - double x1, x2, y1, y2, midx, midy, sinf, cosf; - int i, j, ii, jj, angle, path, halign, valign, tnr; + gks_set_text_upvec(chuX, chuY); - gks_inq_ws_conntype(wkid, &errind, &conid, &wtype); - gks_inq_max_ds_size(wtype, &errind, &dcunit, &rw, &rh, &width, &height); - if (sizex > 0) - pixels = sizex / rh * height; - else - pixels = 500; - if (wtype == 101 || wtype == 102 || wtype == 120 || wtype == 382) pixels *= 8; + if (!inquire) + { + p = text; + while (p != NULL) + { + if (hAlign == 2) + p->x += 0.5 * (totalWidth - p->line_width); + else if (hAlign == 3) + p->x += totalWidth - p->line_width; + p = p->next; + } - gks_inq_text_height(&errind, &chh); - gks_inq_text_color_index(&errind, &color); - gks_inq_color_rep(wkid, color, GKS_K_VALUE_SET, &errind, &rgb[0], &rgb[1], &rgb[2]); + textP = text; + while (textP != NULL) + { + baseLine = NULL; + p = text; + while (p != NULL && p->line_number != textP->line_number) + { + p = p->next; + } + while (p != NULL && p->line_number == textP->line_number) + { + if (p->math) + { + baseLine = p->baseline + 1; + break; + } + else + p = p->next; + } - pointSize = chh * pixels; - latex2image(string, pointSize, rgb, &width, &height, &data); + xx = textP->x - x; + switch (hAlign) + { + case 2: + xx -= 0.5 * totalWidth; + break; + case 3: + xx -= totalWidth; + break; + default: + break; + } - gks_inq_text_upvec(&errind, &ux, &uy); - rad = -atan2(ux, uy); - angle = (int)(rad * 180 / M_PI + 0.5); - if (angle < 0) angle += 360; - path = ((angle + 45) / 90) % 4; + yy = textP->y - y - charHeight * 0.2; + if (!textP->math && baseLine != NULL) + { + yy += *baseLine + 0.5 * charHeight; + } + switch (vAlign) + { + case 2: + yy += charHeight * 0.2; + break; + case 3: + yy += 0.5 * totalHeight; + break; + case 4: + yy += totalHeight - charHeight * 0.2; + break; + case 5: + yy += totalHeight; + break; + default: + break; + } - if (data != NULL) - { - rw = width / (double)pixels; - rh = height / (double)pixels; + sx = x + cos(angle) * xx - sin(angle) * yy; + sy = y + sin(angle) * xx + cos(angle) * yy; - gks_inq_text_align(&errind, &halign, &valign); + if (textP->math) + gr_mathtex(sx, sy, textP->string); + else + gks_text(sx, sy, textP->string); - rx = x; - switch (halign) + textP = textP->next; + } + } + else + { + xx = x; + switch (hAlign) { case 2: - rx -= 0.5 * rw; + xx -= 0.5 * totalWidth; break; case 3: - rx -= rw; + xx -= totalWidth; + break; + default: break; } - ry = y; - switch (valign) + yy = y; + switch (vAlign) { - case 1: - ry -= rh - chh * 0.04; - break; - case 2: - ry -= rh; - break; case 3: - ry -= 0.5 * rh; + yy += 0.5 * totalHeight; + break; + case 4: + yy += totalHeight; break; case 5: - ry -= chh * 0.04; + yy += totalHeight; + break; + default: break; } - bbx[0] = rx; - bbx[1] = rx + rw; - bbx[2] = bbx[1]; - bbx[3] = bbx[0]; - bby[0] = ry; - bby[1] = bby[0]; - bby[2] = ry + rh; - bby[3] = bby[2]; - x1 = y1 = FLT_MAX; - x2 = y2 = -FLT_MAX; + tbx[0] = xx; + tby[0] = yy; + tbx[1] = tbx[0] + totalWidth; + tby[1] = tby[0]; + tbx[2] = tbx[1]; + tby[2] = tby[1] - totalHeight; + tbx[3] = tbx[0]; + tby[3] = tby[2]; for (i = 0; i < 4; i++) { - xx = bbx[i] - x; - yy = bby[i] - y; - bbx[i] = x + cos(rad) * xx - sin(rad) * yy; - bby[i] = y + sin(rad) * xx + cos(rad) * yy; - - x1 = min(x1, bbx[i]); - x2 = max(x2, bbx[i]); - y1 = min(y1, bby[i]); - y2 = max(y2, bby[i]); - } - - if (inquire) - { - for (i = 0; i < 4; i++) - { - tbx[i] = bbx[i]; - tby[i] = bby[i]; - } - } - else - { - gks_inq_current_xformno(&errind, &tnr); - if (tnr != NDC) gks_select_xform(NDC); - - if (angle % 90 == 0) - { - switch (path) - { - case 0: - gks_draw_image(x1, y2, x2, y1, width, height, data); - break; - case 1: - trans = rotl90(width, height, data); - gks_draw_image(x1, y2, x2, y1, height, width, trans); - free(trans); - break; - case 2: - trans = rot180(width, height, data); - gks_draw_image(x1, y2, x2, y1, width, height, trans); - free(trans); - break; - case 3: - trans = rotr90(width, height, data); - gks_draw_image(x1, y2, x2, y1, height, width, trans); - free(trans); - break; - } - } - else - { - w = (int)((x2 - x1) * pixels + 0.5), h = (int)((y2 - y1) * pixels + 0.5); - trans = (int *)xcalloc(w * h, sizeof(int)); - midx = ceil(0.5 * w); - midy = ceil(0.5 * h); - sinf = sin(rad); - cosf = cos(rad); - for (j = 0; j < h; j++) - for (i = 0; i < w; i++) - { - xx = (i - midx) * cosf - (j - midy) * sinf; - yy = (i - midx) * sinf + (j - midy) * cosf; - ii = round(xx) + ceil(0.5 * width); - jj = round(yy) + ceil(0.5 * height); - if (ii >= 0 && jj >= 0 && ii < width && jj < height) trans[j * w + i] = data[jj * width + ii]; - } - gks_draw_image(x1, y2, x2, y1, w, h, trans); - free(trans); - } - - if (tnr != NDC) gks_select_xform(tnr); + xx = tbx[i] - x; + yy = tby[i] - y; + tbx[i] = x + cos(angle) * xx - sin(angle) * yy; + tby[i] = y + sin(angle) * xx + cos(angle) * yy; } + } - free(data); + while (text != NULL) + { + text_node_t *next = text->next; + free(text->string); + free(text); + text = next; } -} -void mathtex2(double x, double y, const char *formula, int inquire, double *tbx, double *tby); + gks_set_text_align(hAlign, vAlign); +} /*! - * Generate a character string starting at the given location. Strings can be - * defined to create mathematical symbols and Greek letters using LaTeX syntax. + * Draw a text at position `x`, `y` using the current text attributes. * * \param[in] x The X coordinate of the starting position of the text string * \param[in] y The Y coordinate of the starting position of the text string - * \param[in] string The text string to be drawn + * \param[in] string The text to be drawn + * + * The values for `x` and `y` are in normalized device coordinates. + * The attributes that control the appearance of text are text font and + * precision, character expansion factor, character spacing, text color index, + * character height, character up vector, text path and text alignment. */ -void gr_mathtex(double x, double y, char *string) +void gr_text(double x, double y, char *string) { - int unused; - int prec; + int errind, tnr; check_autoinit; - gks_inq_text_fontprec(&unused, &unused, &prec); - if (prec == 3) - { - mathtex2(x, y, string, 0, NULL, NULL); - } + gks_inq_current_xformno(&errind, &tnr); + if (tnr != NDC) gks_select_xform(NDC); + + if (strchr(string, '\n') != NULL || strchr(string, '$')) + text_impl(x, y, string, 0, NULL, NULL); else - { - mathtex(x, y, string, 0, NULL, NULL); - } + gks_text(x, y, string); - if (flag_graphics) gr_writestream("\n", x, y, string); + if (tnr != NDC) gks_select_xform(tnr); + + if (flag_graphics) gr_writestream("\n", x, y, string); } -void gr_inqmathtex(double x, double y, char *string, double *tbx, double *tby) +void gr_inqtext(double x, double y, char *string, double *tbx, double *tby) { - int unused; - int prec; + int errind, tnr, n, wkid, i; + double cpx, cpy; check_autoinit; - gks_inq_text_fontprec(&unused, &unused, &prec); - if (prec == 3) + gks_inq_current_xformno(&errind, &tnr); + if (tnr != NDC) gks_select_xform(NDC); + + if (strchr(string, '\n') != NULL || strchr(string, '$')) + text_impl(x, y, string, 1, tbx, tby); + else { - mathtex2(x, y, string, 1, tbx, tby); + gks_inq_open_ws(1, &errind, &n, &wkid); + gks_inq_text_extent(wkid, x, y, string, &errind, &cpx, &cpy, tbx, tby); } - else + + if (tnr != NDC) { - mathtex(x, y, string, 1, tbx, tby); + gks_select_xform(tnr); + + for (i = 0; i < 4; i++) + { + tbx[i] = (tbx[i] - nx.b) / nx.a; + tby[i] = (tby[i] - nx.d) / nx.c; + if (lx.scale_options) + { + tbx[i] = x_log(tbx[i]); + tby[i] = y_log(tby[i]); + } + } } } diff --git a/lib/gr/mathtex2.c b/lib/gr/mathtex2.c index 38690336a..55941e3cd 100644 --- a/lib/gr/mathtex2.c +++ b/lib/gr/mathtex2.c @@ -968,6 +968,7 @@ typedef struct BoxModelNode_ double canvas_height = 0; double canvas_width = 0; +double canvas_depth = 0; static double font_size; static double transformation[6]; @@ -3472,6 +3473,7 @@ static void mathtex_to_box_model(const char *mathtex, double *width, double *hei assert(get_box_model_node(result_box_model_node_index)->type == BT_HLIST); canvas_height = result_node->u.hlist.height + result_node->u.hlist.depth; canvas_width = result_node->u.hlist.width; + canvas_depth = result_node->u.hlist.depth; if (width) { *width = result_node->u.hlist.width; @@ -3523,7 +3525,7 @@ static void calculate_alignment_offsets(int horizontal_alignment, int vertical_a case GKS_K_TEXT_VALIGN_NORMAL: case GKS_K_TEXT_VALIGN_BASE: default: - *y_offset = 0; + *y_offset = -canvas_depth / window_height; break; } } @@ -3787,7 +3789,7 @@ static unsigned int get_codepoint_for_character_variant(unsigned int codepoint, } } -void mathtex2(double x, double y, const char *formula, int inquire, double *tbx, double *tby) +void mathtex2(double x, double y, const char *formula, int inquire, double *tbx, double *tby, double *baseline) { int unused; int previous_bearing_x_direction; @@ -3880,6 +3882,11 @@ void mathtex2(double x, double y, const char *formula, int inquire, double *tbx, tby[2] = ymax; tby[3] = ymax; angle = -atan2(chupx, chupy); + if (baseline) + { + baseline[0] = x + x_offset * cos(angle) - (y_offset + canvas_depth / window_height) * sin(angle); + baseline[1] = y + x_offset * sin(angle) + (y_offset + canvas_depth / window_height) * cos(angle); + } for (i = 0; i < 4; i++) { double rx, ry; From 16cc0436e04b9a294cc4e18e0f7fe6cbb3eed1cc Mon Sep 17 00:00:00 2001 From: Florian Rhiem Date: Wed, 27 Oct 2021 10:46:01 +0200 Subject: [PATCH 17/22] Fix GKSTerm Save As dialog --- lib/gks/quartz/GKSView.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/gks/quartz/GKSView.m b/lib/gks/quartz/GKSView.m index 934b811a2..68e69067d 100644 --- a/lib/gks/quartz/GKSView.m +++ b/lib/gks/quartz/GKSView.m @@ -829,7 +829,7 @@ - (IBAction)saveDocumentAs:(id)sender [self savePanelDidEnd:savePanel returnCode:result contextInfo:saveFormatPopUp]; }]; #else - [savePanel beginSheetModalForWindow:cWindow + [savePanel beginSheetModalForWindow:[self window] completionHandler:^(NSInteger result) { [self savePanelDidEnd:savePanel returnCode:result contextInfo:saveFormatPopUp]; }]; From 5053ca907cbe3ba48df0d808ba3ca989f2bfba59 Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Wed, 27 Oct 2021 13:30:05 +0200 Subject: [PATCH 18/22] Fixed problem with macOS 'system()' function --- lib/gks/socket.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/lib/gks/socket.c b/lib/gks/socket.c index 737575e93..8812034a6 100644 --- a/lib/gks/socket.c +++ b/lib/gks/socket.c @@ -15,8 +15,9 @@ #include #include #include -#include #include +#include +#include #else #include #endif @@ -93,9 +94,54 @@ static DWORD WINAPI gksqt_tread(LPVOID parm) static void *gksqt_tread(void *arg) { +#ifdef __APPLE__ + sigset_t blockMask, origMask; + struct sigaction saIgnore, saOrigQuit, saOrigInt, saDefault; + pid_t pid; + + sigemptyset(&blockMask); + sigaddset(&blockMask, SIGCHLD); + sigprocmask(SIG_BLOCK, &blockMask, &origMask); + + saIgnore.sa_handler = SIG_IGN; + saIgnore.sa_flags = 0; + sigemptyset(&saIgnore.sa_mask); + sigaction(SIGINT, &saIgnore, &saOrigInt); + sigaction(SIGQUIT, &saIgnore, &saOrigQuit); + + pid = fork(); + if (pid < 0) + { + fprintf(stderr, "Fork failed\n"); + return -1; + } + else if (pid == 0) + { + saDefault.sa_handler = SIG_DFL; + saDefault.sa_flags = 0; + sigemptyset(&saDefault.sa_mask); + + if (saOrigInt.sa_handler != SIG_IGN) sigaction(SIGINT, &saDefault, NULL); + if (saOrigQuit.sa_handler != SIG_IGN) sigaction(SIGQUIT, &saDefault, NULL); + + sigprocmask(SIG_SETMASK, &origMask, NULL); + + is_running = 1; + execl("/bin/sh", "sh", "-c", (char *)arg, (char *)NULL); + is_running = 0; + + exit(127); + } + + sigprocmask(SIG_SETMASK, &origMask, NULL); + sigaction(SIGINT, &saOrigInt, NULL); + sigaction(SIGQUIT, &saOrigQuit, NULL); +#else is_running = 1; system((char *)arg); is_running = 0; +#endif + return NULL; } From c4d8c123390a9b38910f7d6827fcf2430d9cd02a Mon Sep 17 00:00:00 2001 From: Ingo Meyer Date: Thu, 28 Oct 2021 10:29:01 +0200 Subject: [PATCH 19/22] Add custom color support to pie plots This commit adds a new util function `set_next_color` which sets colors from given args (and falls back to default colors). Each call to `set_next_color` sets the next color from an color array (compare to `strtok`). --- lib/grm/plot.c | 122 +++++++++++++++++++++++++++++++--- lib/grm/plot_int.h | 20 ++++++ lib/grm/test/public_api/pie.c | 14 +++- 3 files changed, 145 insertions(+), 11 deletions(-) diff --git a/lib/grm/plot.c b/lib/grm/plot.c index 51e76044b..27e5f1685 100644 --- a/lib/grm/plot.c +++ b/lib/grm/plot.c @@ -4922,19 +4922,17 @@ error_t plot_pie(grm_args_t *subplot_args) gr_setfillintstyle(GKS_K_INTSTYLE_SOLID); gr_settextalign(GKS_K_TEXT_HALIGN_CENTER, GKS_K_TEXT_VALIGN_HALF); - cleanup_and_set_error_if(!args_first_value(series, "x", "D", &x, &x_length), ERROR_PLOT_MISSING_DATA); normalized_x = normalize(x_length, x); cleanup_and_set_error_if(normalized_x == NULL, ERROR_MALLOC); normalized_x_int = normalize_int(x_length, x, 1000); cleanup_and_set_error_if(normalized_x_int == NULL, ERROR_MALLOC); + set_next_color(series, "c", GR_COLOR_FILL); start_angle = 90; for (i = 0; i < x_length; ++i) { - gr_uselinespec(""); - gr_inqlinecolorind(&color_ind); - gr_setfillcolorind(color_ind); + gr_inqfillcolorind(&color_ind); gr_inqcolor(color_ind, (int *)color_rgb); set_text_color_for_background(color_rgb[0] / 255.0, color_rgb[1] / 255.0, color_rgb[2] / 255.0); end_angle = start_angle - normalized_x[i] * 360.0; @@ -4950,7 +4948,9 @@ error_t plot_pie(grm_args_t *subplot_args) { start_angle += 360.0; } + set_next_color(NULL, NULL, GR_COLOR_FILL); } + set_next_color(NULL, NULL, GR_COLOR_RESET); if (args_values(subplot_args, "title", "s", &title)) { @@ -5480,17 +5480,17 @@ error_t plot_draw_legend(grm_args_t *subplot_args) error_t plot_draw_pie_legend(grm_args_t *subplot_args) { + grm_args_t *series; const char **labels, **current_label; unsigned int num_labels; - grm_args_t **current_series; const double *viewport; double px, py, w, h; double tbx[4], tby[4]; int color_ind; - return_error_if(!args_first_value(subplot_args, "labels", "S", &labels, &num_labels), ERROR_PLOT_MISSING_LABELS); logger((stderr, "Draw a pie legend with %d labels\n", num_labels)); + args_values(subplot_args, "series", "a", &series); /* series exists always */ args_values(subplot_args, "viewport", "D", &viewport); gr_savestate(); gr_selntran(0); @@ -5517,18 +5517,18 @@ error_t plot_draw_pie_legend(grm_args_t *subplot_args) gr_drawrect(px - 0.02, px + w + 0.02, py - 0.5 * h - 0.02, py + 0.5 * h + 0.02); gr_settextalign(GKS_K_TEXT_HALIGN_LEFT, GKS_K_TEXT_VALIGN_HALF); gr_uselinespec(" "); + set_next_color(series, "c", GR_COLOR_FILL); for (current_label = labels; *current_label != NULL; ++current_label) { - gr_uselinespec(""); - gr_inqlinecolorind(&color_ind); - gr_setfillcolorind(color_ind); gr_fillrect(px, px + 0.02, py - 0.01, py + 0.01); gr_setlinecolorind(1); gr_drawrect(px, px + 0.02, py - 0.01, py + 0.01); gr_textext(px + 0.03, py, (char *)*current_label); gr_inqtextext(0, 0, *(char **)current_label, tbx, tby); px += tbx[2] + 0.05; + set_next_color(NULL, NULL, GR_COLOR_FILL); } + set_next_color(NULL, NULL, GR_COLOR_RESET); gr_selntran(1); gr_restorestate(); @@ -7211,6 +7211,110 @@ void draw_xticklabel(double x1, double x2, const char *label, double available_w gr_textext(x1, x2, new_label + cur_start); } +/*! + * \brief Set colors from color index or rgb arrays. + * + * Call the function first with an argument container and a key. Afterwards, call the `set_next_color` with `NULL` + * pointers to iterate through the color arrays. If `key` does not exist in `args`, the function falls back to default + * colors. + * + * \param args The argument container which stores the color values. + * \param key The key of the colors in the argument container. The key may reference integer or double arrays. + * Integer arrays describe colors of the GKS color table (0 - 1255). Double arrays contain RGB tuples in the + * range [0.0, 1.0]. If key does not exist, the routine falls back to default colors (taken from + * `gr_uselinespec`). + * \param color_type The color type to set. Can be one of `GR_COLOR_LINE`, `GR_COLOR_MARKER`, `GR_COLOR_FILL`, + * `GR_COLOR_TEXT`, `GR_COLOR_BORDER` or any combination of them (combined with OR). The special value + * `GR_COLOR_RESET` resets all color modifications. + */ +void set_next_color(const grm_args_t *args, const char *key, gr_color_type_t color_type) +{ + const static int fallback_color_indices[] = {989, 982, 980, 981, 996, 983, 995, 988, 986, 990, + 991, 984, 992, 993, 994, 987, 985, 997, 998, 999}; + static double saved_color[3]; + static int last_array_index = -1; + static const int *color_indices = NULL; + static const double *color_rgb_values = NULL; + static unsigned int color_array_length = -1; + int current_array_index = last_array_index + 1; + int color_index; + int reset = (color_type == GR_COLOR_RESET); + int gks_errind = GKS_K_NO_ERROR; + + if (reset || (args != NULL && key != NULL)) + { + if (last_array_index >= 0 && color_rgb_values != NULL) + { + gr_setcolorrep(PLOT_CUSTOM_COLOR_INDEX, saved_color[0], saved_color[1], saved_color[2]); + } + last_array_index = -1; + if (!reset && args != NULL && key != NULL) + { + if (!args_first_value(args, key, "I", &color_indices, &color_array_length) && + !args_first_value(args, key, "D", &color_rgb_values, &color_array_length)) + { + /* use fallback colors if `key` cannot be read from `args` */ + logger((stderr, "Cannot read \"%s\" from args, falling back to default colors\n", key)); + color_indices = fallback_color_indices; + color_array_length = array_size(fallback_color_indices); + } + } + else + { + color_indices = NULL; + color_rgb_values = NULL; + color_array_length = -1; + } + + if (reset) + { + return; + } + } + + if (last_array_index < 0 && color_rgb_values != NULL) + { + gks_inq_color_rep(1, PLOT_CUSTOM_COLOR_INDEX, GKS_K_VALUE_SET, &gks_errind, &saved_color[0], &saved_color[1], + &saved_color[2]); + } + + current_array_index %= color_array_length; + + if (color_indices != NULL) + { + color_index = color_indices[current_array_index]; + last_array_index = current_array_index; + } + else if (color_rgb_values != NULL) + { + gr_setcolorrep(PLOT_CUSTOM_COLOR_INDEX, color_rgb_values[current_array_index], + color_rgb_values[current_array_index + 1], color_rgb_values[current_array_index + 2]); + color_index = PLOT_CUSTOM_COLOR_INDEX; + last_array_index = current_array_index + 2; + } + + if (color_type & GR_COLOR_LINE) + { + gr_setlinecolorind(color_index); + } + if (color_type & GR_COLOR_MARKER) + { + gr_setmarkercolorind(color_index); + } + if (color_type & GR_COLOR_FILL) + { + gr_setfillcolorind(color_index); + } + if (color_type & GR_COLOR_TEXT) + { + gr_settextcolorind(color_index); + } + if (color_type & GR_COLOR_BORDER) + { + gr_setbordercolorind(color_index); + } +} + /* ========================= methods ================================================================================ */ /* ------------------------- args set ------------------------------------------------------------------------------- */ diff --git a/lib/grm/plot_int.h b/lib/grm/plot_int.h index f9942d71a..4c0937de1 100644 --- a/lib/grm/plot_int.h +++ b/lib/grm/plot_int.h @@ -35,6 +35,8 @@ extern const char *plot_clear_exclude_keys[]; /* ------------------------- plot ----------------------------------------------------------------------------------- */ +/* ~~~~~~~~~~~~~~~~~~~~~~~~~ plot arguments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + #define ROOT_DEFAULT_APPEND_PLOTS 0 #define PLOT_DEFAULT_WIDTH 600.0 #define PLOT_DEFAULT_HEIGHT 450.0 @@ -80,6 +82,11 @@ extern const char *plot_clear_exclude_keys[]; #define PLOT_SURFACE_GRIDIT_N 200 +/* ~~~~~~~~~~~~~~~~~~~~~~~~~ util ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +#define PLOT_CUSTOM_COLOR_INDEX 979 + + /* ========================= datatypes ============================================================================== */ /* ------------------------- plot ----------------------------------------------------------------------------------- */ @@ -108,6 +115,18 @@ typedef enum GR_OPTION_SHADED_MESH = 6 } gr_option_t; +/* ~~~~~~~~~~~~~~~~~~~~~~~~~ util ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +typedef enum +{ + GR_COLOR_RESET = 0, + GR_COLOR_LINE = 1 << 0, + GR_COLOR_MARKER = 1 << 1, + GR_COLOR_FILL = 1 << 2, + GR_COLOR_TEXT = 1 << 3, + GR_COLOR_BORDER = 1 << 4 +} gr_color_type_t; + /* ========================= functions ============================================================================== */ @@ -206,6 +225,7 @@ error_t classes_polar_histogram(grm_args_t *subplot_args, double *r_max); double get_lightness_from_rbg(double r, double g, double b); void set_text_color_for_background(double r, double g, double b); void draw_xticklabel(double x1, double x2, const char *label, double available_width); +void set_next_color(const grm_args_t *args, const char *key, gr_color_type_t color_type); #endif /* ifndef GRM_PLOT_INT_H_INCLUDED */ diff --git a/lib/grm/test/public_api/pie.c b/lib/grm/test/public_api/pie.c index 15e0009a6..4af3acfea 100644 --- a/lib/grm/test/public_api/pie.c +++ b/lib/grm/test/public_api/pie.c @@ -4,9 +4,11 @@ #define array_size(x) (sizeof(x) / sizeof((x)[0])) -static void test_quiver(void) +static void test_pie(void) { const double x[] = {188.6, 107.8, 100.3, 99.0}; + const double c[] = {93 / 255.0, 57 / 255.0, 101 / 255.0, 175 / 255.0, 130 / 255.0, 185 / 255.0, + 207 / 255.0, 180 / 255.0, 213 / 255.0, 223 / 255.0, 205 / 255.0, 227 / 255.0}; const char *labels[] = {"Czech Republic", "Austria", "Romania", "Germany"}; grm_args_t *args; @@ -21,13 +23,21 @@ static void test_quiver(void) printf("Press any key to continue...\n"); getchar(); + printf("plot a pie chart with x and custom colors\n"); + grm_args_push(args, "c", "nD", array_size(c), c); + + grm_plot(args); + + printf("Press any key to continue...\n"); + getchar(); + grm_args_delete(args); } int main(void) { - test_quiver(); + test_pie(); grm_finalize(); return 0; From 607f6347e3fee33bb7e2dfde22deba72446a6c48 Mon Sep 17 00:00:00 2001 From: Josef Heinen Date: Thu, 28 Oct 2021 11:46:21 +0200 Subject: [PATCH 20/22] Add gr_textx method for application compatibility --- lib/gr/gr.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++- lib/gr/gr.h | 5 ++++ lib/gr/import.c | 12 ++++++--- 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 243a9e1a3..f63fd6b92 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -11034,6 +11034,49 @@ void gr_text(double x, double y, char *string) if (flag_graphics) gr_writestream("\n", x, y, string); } +/*! + * Draw a text at position `x`, `y` using the given options and current text + * attributes. + * + * \param[in] x The X coordinate of the starting position of the text string + * \param[in] y The Y coordinate of the starting position of the text string + * \param[in] string The text to be drawn + * \param[in] opts Bit mask including text options (GKS_TEXT_USE_WC, + * GKS_TEXT_ENABLE_INLINE_MATH) + * + * The values for `x` and `y` specify the text position. If the GKS_TEXT_USE_WC + * option is set, they are interpreted as world cordinates, otherwise as + * normalized device coordinates. The string may contain new line characters + * and inline math expressions ($...$). The latter are only taken into account, + * if the GKS_TEXT_ENABLE_INLINE_MATH option is set. + * The attributes that control the appearance of text are text font and + * precision, character expansion factor, character spacing, text color index, + * character height, character up vector, text path and text alignment. + */ +void gr_textx(double x, double y, char *string, int opts) +{ + int errind, tnr; + double xn = x, yn = y; + + check_autoinit; + + gks_inq_current_xformno(&errind, &tnr); + if (tnr != NDC && (opts & GR_TEXT_USE_WC) != 0) + { + gr_wctondc(&xn, &yn); + gks_select_xform(NDC); + } + + if ((strchr(string, '\n') != NULL || strchr(string, '$')) && (opts & GR_TEXT_ENABLE_INLINE_MATH) != 0) + text_impl(xn, yn, string, 0, NULL, NULL); + else + gks_text(xn, yn, string); + + if (tnr != NDC) gks_select_xform(tnr); + + if (flag_graphics) gr_writestream("\n", x, y, string); +} + void gr_inqtext(double x, double y, char *string, double *tbx, double *tby) { int errind, tnr, n, wkid, i; @@ -11052,7 +11095,32 @@ void gr_inqtext(double x, double y, char *string, double *tbx, double *tby) gks_inq_text_extent(wkid, x, y, string, &errind, &cpx, &cpy, tbx, tby); } - if (tnr != NDC) + if (tnr != NDC) gks_select_xform(tnr); +} + +void gr_inqtextx(double x, double y, char *string, int opts, double *tbx, double *tby) +{ + int errind, tnr, n, wkid, i; + double xn = x, yn = y, cpx, cpy; + + check_autoinit; + + gks_inq_current_xformno(&errind, &tnr); + if (tnr != NDC && (opts & GR_TEXT_USE_WC) != 0) + { + gr_wctondc(&xn, &yn); + gks_select_xform(NDC); + } + + if ((strchr(string, '\n') != NULL || strchr(string, '$')) && (opts & GR_TEXT_ENABLE_INLINE_MATH) != 0) + text_impl(xn, yn, string, 1, tbx, tby); + else + { + gks_inq_open_ws(1, &errind, &n, &wkid); + gks_inq_text_extent(wkid, xn, yn, string, &errind, &cpx, &cpy, tbx, tby); + } + + if (tnr != NDC && (opts & GR_TEXT_USE_WC) != 0) { gks_select_xform(tnr); diff --git a/lib/gr/gr.h b/lib/gr/gr.h index 9d8fc6653..9f023f78b 100644 --- a/lib/gr/gr.h +++ b/lib/gr/gr.h @@ -26,6 +26,9 @@ extern "C" { #define GR_VOLUME_ABSORPTION 1 #define GR_VOLUME_MIP 2 +#define GR_TEXT_USE_WC (1 << 0) +#define GR_TEXT_ENABLE_INLINE_MATH (1 << 1) + typedef struct { double x, y; @@ -45,7 +48,9 @@ DLLEXPORT void gr_updatews(void); DLLEXPORT void gr_polyline(int, double *, double *); DLLEXPORT void gr_polymarker(int, double *, double *); DLLEXPORT void gr_text(double, double, char *); +DLLEXPORT void gr_textx(double, double, char *, int); DLLEXPORT void gr_inqtext(double, double, char *, double *, double *); +DLLEXPORT void gr_inqtextx(double, double, char *, int, double *, double *); DLLEXPORT void gr_fillarea(int, double *, double *); DLLEXPORT void gr_cellarray(double, double, double, double, int, int, int, int, int, int, int *); DLLEXPORT void gr_nonuniformcellarray(double *, double *, int, int, int, int, int, int, int *); diff --git a/lib/gr/import.c b/lib/gr/import.c index 014cd7717..d95c8482c 100644 --- a/lib/gr/import.c +++ b/lib/gr/import.c @@ -102,6 +102,7 @@ static char *format[] = { "surface:iiFFFi", "text:ffs", "textext:ffs", + "textx:ffsi", "titles3d:sss", "tricont:iFFFiF", "trisurf:iFFF", @@ -535,18 +536,21 @@ static void gr(int id) gr_textext(f_arg[0], f_arg[1], s_arg[0]); break; case 82: - gr_titles3d(s_arg[0], s_arg[1], s_arg[2]); + gr_textx(f_arg[0], f_arg[1], s_arg[0], i_arg[0]); break; case 83: - gr_tricontour(i_arg[0], f_arr[0], f_arr[1], f_arr[2], i_arg[2], f_arr[3]); + gr_titles3d(s_arg[0], s_arg[1], s_arg[2]); break; case 84: - gr_trisurface(i_arg[0], f_arr[0], f_arr[1], f_arr[2]); + gr_tricontour(i_arg[0], f_arr[0], f_arr[1], f_arr[2], i_arg[2], f_arr[3]); break; case 85: - gr_uselinespec(s_arg[0]); + gr_trisurface(i_arg[0], f_arr[0], f_arr[1], f_arr[2]); break; case 86: + gr_uselinespec(s_arg[0]); + break; + case 87: gr_verrorbars(i_arg[0], f_arr[0], f_arr[1], f_arr[2], f_arr[3]); break; } From 7a5e57da1d3e9cbf2f712f81812ab553eb5b2ad7 Mon Sep 17 00:00:00 2001 From: Daniel Kaiser Date: Fri, 29 Oct 2021 07:29:50 +0000 Subject: [PATCH 21/22] Apply 4 suggestion(s) to 1 file(s) --- lib/gr/gr.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 696af5331..a9bb3c0d4 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -11043,14 +11043,14 @@ void gr_text(double x, double y, char *string) * \param[in] x The X coordinate of the starting position of the text string * \param[in] y The Y coordinate of the starting position of the text string * \param[in] string The text to be drawn - * \param[in] opts Bit mask including text options (GKS_TEXT_USE_WC, - * GKS_TEXT_ENABLE_INLINE_MATH) + * \param[in] opts Bit mask including text options (GR_TEXT_USE_WC, + * GR_TEXT_ENABLE_INLINE_MATH) * - * The values for `x` and `y` specify the text position. If the GKS_TEXT_USE_WC + * The values for `x` and `y` specify the text position. If the GR_TEXT_USE_WC * option is set, they are interpreted as world cordinates, otherwise as * normalized device coordinates. The string may contain new line characters * and inline math expressions ($...$). The latter are only taken into account, - * if the GKS_TEXT_ENABLE_INLINE_MATH option is set. + * if the GR_TEXT_ENABLE_INLINE_MATH option is set. * The attributes that control the appearance of text are text font and * precision, character expansion factor, character spacing, text color index, * character height, character up vector, text path and text alignment. @@ -11077,7 +11077,7 @@ void gr_textx(double x, double y, char *string, int opts) if (tnr != NDC) gks_select_xform(tnr); - if (flag_graphics) gr_writestream("\n", x, y, string); + if (flag_graphics) gr_writestream("\n", x, y, string, opts); } void gr_inqtext(double x, double y, char *string, double *tbx, double *tby) From c80516eaf9ff8fb38f2ba1de2534a845c40797e2 Mon Sep 17 00:00:00 2001 From: Daniel Kaiser Date: Fri, 29 Oct 2021 10:22:17 +0200 Subject: [PATCH 22/22] Fix memory leaks in gks text routines --- lib/gks/gks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/gks/gks.c b/lib/gks/gks.c index 5173d2f34..0069698ab 100644 --- a/lib/gks/gks.c +++ b/lib/gks/gks.c @@ -1183,6 +1183,7 @@ void gks_text(double px, double py, char *str) gks_input2utf8(str, utf8_str, ENCODING_LATIN1); gks_ft_text(px, py, utf8_str, s, gks_ft_gdp); + gks_free(utf8_str); } else gks_ft_text(px, py, str, s, gks_ft_gdp); @@ -2933,6 +2934,7 @@ void gks_inq_text_extent(int wkid, double px, double py, char *str, int *errind, gks_input2utf8(str, utf8_str, ENCODING_LATIN1); gks_ft_inq_text_extent(px, py, utf8_str, s, gks_ft_gdp, bx, by); + gks_free(utf8_str); } else gks_ft_inq_text_extent(px, py, str, s, gks_ft_gdp, bx, by);