diff --git a/.gitignore b/.gitignore index 993160c76..6ed07ffb9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1,29 @@ .* -!.gitignore !.git-blame-ignore-rev !.githooks -build/ -dist/ -*.o +!.gitignore *.a -*.so -*.so.dSYM -*.dylib *.app +*.dylib +*.o *.pc +*.pkg +*.so +*.so.dSYM +Makedefs +QMakefile +build/ +cmake-build-*/ +demo +dist/ +gksm gr*.deb gr*.rpm -cmake-build-*/ -moc_*.h +gr_version.h +js/fonts/ +js/gr.js +js/libGR.js +lib/gks/qt/gksqt moc_*.cpp +moc_*.h qrc_*.cpp -QMakefile -gr_version.h -Makedefs -demo -gksm diff --git a/js/jsterm.js b/js/jsterm.js index 20da6d196..fb42ca7ea 100644 --- a/js/jsterm.js +++ b/js/jsterm.js @@ -82,10 +82,14 @@ 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) { - for (pid in widgets) { + 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(); } @@ -178,13 +182,17 @@ 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'; - 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 +429,13 @@ 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.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(); @@ -1116,8 +1127,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]; } 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); 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]; }]; 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; } diff --git a/lib/gr/gr.c b/lib/gr/gr.c index 6da063e01..a9bb3c0d4 100644 --- a/lib/gr/gr.c +++ b/lib/gr/gr.c @@ -202,6 +202,18 @@ 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; + double baseline[2]; +} 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 +256,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,186 +1820,6 @@ 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) -{ - int errind, tnr, halign, valign, n; - char *s, *t; - double ux, uy, angle, height; - double rx, ry, sx, sy; - - check_autoinit; - - gks_inq_current_xformno(&errind, &tnr); - if (tnr != NDC) gks_select_xform(NDC); - - if (strchr(string, '\n') != NULL) - { - 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; - - n = 0; - s = string; - while (*s) - if (*s++ == '\n') n++; - - rx = x; - ry = y; - switch (valign) - { - 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; - } - - t = gks_strdup(string); - n = 0; - s = strtok(t, "\n"); - while (s != NULL) - { - sx = rx + sin(angle) * n * height; - sy = ry - cos(angle) * n * height; - gks_text(sx, sy, s); - s = strtok(NULL, "\n"); - n++; - } - free(t); - } - else - 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, halign, valign; - char *s, *t; - double ux, uy, angle, width = 0.0, height, chh; - int i; - int n, wkid = 0; - double rx, ry, xx, yy, cpx, cpy; - - check_autoinit; - - gks_inq_current_xformno(&errind, &tnr); - if (tnr != NDC) gks_select_xform(NDC); - - gks_inq_open_ws(1, &errind, &n, &wkid); - - if (strchr(string, '\n') != NULL) - { - gks_inq_text_align(&errind, &halign, &valign); - gks_inq_text_upvec(&errind, &ux, &uy); - - gks_set_text_upvec(0.0, 1.0); - - t = gks_strdup(string); - n = 0; - s = strtok(t, "\n"); - while (s != NULL) - { - 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++; - } - free(t); - - gks_set_text_upvec(ux, uy); - - angle = -atan2(ux, uy); - gks_inq_text_height(&errind, &chh); - height = chh * n * 1.5; - - rx = x; - switch (halign) - { - case 2: - rx -= 0.5 * width; - break; - case 3: - rx -= width; - break; - } - ry = y; - switch (valign) - { - case 1: - ry -= height - chh * 0.04; - break; - case 2: - ry -= height; - break; - case 3: - ry -= 0.5 * height; - break; - case 5: - ry -= chh * 0.04; - break; - } - tbx[0] = rx; - tbx[1] = rx + width; - tbx[2] = tbx[1]; - 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++) - { - 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; - } - - cpx = tbx[1]; - cpy = tby[1]; - } - else - gks_inq_text_extent(wkid, x, y, string, &errind, &cpx, &cpy, tbx, tby); - - if (tnr != NDC) - { - 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]); - } - } - } -} - /*! * Allows you to specify a polygonal shape of an area to be filled. * @@ -2911,8 +2745,8 @@ void gr_gridit(int nd, double *xd, double *yd, double *zd, int nx, int ny, doubl /* CALL THE SMOOTH SURFACE FIT ROUTINE */ md = 1; ncp = 4; - iwk = (int *)calloc(31 * nd + nx * ny, sizeof(int)); - wk = (double *)calloc(6 * (nd + 1), sizeof(double)); + iwk = (int *)xcalloc(31 * nd + nx * ny, sizeof(int)); + wk = (double *)xcalloc(6 * (nd + 1), sizeof(double)); idsfft(&md, &ncp, &nd, xd, yd, zd, &nx, &ny, x, y, z, iwk, wk); @@ -8920,10 +8754,10 @@ int gr_hexbin(int n, double *x, double *y, int nbins) ycorr = (vymax - vymin) - ((imax - 2) * 1.5 * R + (imax % 2) * R); ycorr = ycorr / 2; - cell = (int *)calloc(lmax + 1, sizeof(int)); - cnt = (int *)calloc(lmax + 1, sizeof(int)); - xcm = (double *)calloc(lmax + 1, sizeof(double)); - ycm = (double *)calloc(lmax + 1, sizeof(double)); + cell = (int *)xcalloc(lmax + 1, sizeof(int)); + cnt = (int *)xcalloc(lmax + 1, sizeof(int)); + xcm = (double *)xcalloc(lmax + 1, sizeof(double)); + ycm = (double *)xcalloc(lmax + 1, sizeof(double)); rx[0] = vxmin; rx[1] = vxmax; @@ -10827,7 +10661,7 @@ static void mathtex(double x, double y, char *string, int inquire, double *tbx, } } -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); /*! * Generate a character string starting at the given location. Strings can be @@ -10847,7 +10681,7 @@ void gr_mathtex(double x, double y, char *string) gks_inq_text_fontprec(&unused, &unused, &prec); if (prec == 3) { - mathtex2(x, y, string, 0, NULL, NULL); + mathtex2(x, y, string, 0, NULL, NULL, NULL); } else { @@ -10867,7 +10701,7 @@ void gr_inqmathtex(double x, double y, char *string, double *tbx, double *tby) gks_inq_text_fontprec(&unused, &unused, &prec); if (prec == 3) { - mathtex2(x, y, string, 1, tbx, tby); + mathtex2(x, y, string, 1, tbx, tby, NULL); } else { @@ -10875,6 +10709,441 @@ void gr_inqmathtex(double x, double y, char *string, double *tbx, double *tby) } } +static void append(double x, double y, char *string, int line_number, int math) +{ + text_node_t *prev = text; + int errInd, n, wkId, font, prec; + double cpx, cpy, tbx[4], tby[4]; + char *src, *dest; + + text = (text_node_t *)xcalloc(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 *)xcalloc(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) + { + gks_inq_text_fontprec(&errInd, &font, &prec); + if (prec == 3) + { + mathtex2(0, 0, text->string, 1, tbx, tby, text->baseline); + } + else + { + mathtex(0, 0, text->string, 1, tbx, tby); + } + } + else + { + if (*text->string) + gks_inq_text_extent(wkId, 0, 0, text->string, &errInd, &cpx, &cpy, tbx, tby); + else + { + gks_inq_text_extent(wkId, 0, 0, "Ag", &errInd, &cpx, &cpy, tbx, tby); + tbx[0] = tbx[1] = 0; + } + } + + text->width = tbx[1] - tbx[0]; + text->height = tby[2] - tby[1]; +} + +static text_node_t *parse(double x, double y, char *string, int inline_math) +{ + char *s, *start, *end; + int line_number, math; + + head = text = NULL; + line_number = 1; + math = 0; + + s = (char *)xcalloc(strlen(string) + 1, sizeof(char)); + strcpy(s, string); + + start = end = s; + while (*end) + { + if (*end == '\n') + { + math = 0; + *end++ = '\0'; + append(x, y, start, line_number, math); + start = end; + line_number++; + } + else if (inline_math && !math && *end == '$' && *(end + 1) == '$') + { + end += 2; + } + else if (inline_math && *end == '$') + { + *end++ = '\0'; + append(x, y, start, line_number, math); + math = !math; + start = end; + } + else + end++; + } + append(x, y, start, line_number, math); + free(s); + + return head; +} + +static void text_impl(double x, double y, char *string, int inline_math, int inquire, double *tbx, double *tby) +{ + 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; + + gks_inq_text_upvec(&errInd, &chuX, &chuY); + gks_set_text_upvec(0, 1); + angle = -atan2(chuX, chuY); + + gks_inq_text_height(&errInd, &charHeight); + + gks_inq_text_align(&errInd, &hAlign, &vAlign); + gks_set_text_align(GKS_K_TEXT_HALIGN_LEFT, GKS_K_TEXT_VALIGN_HALF); + + text = textP = parse(x, y, string, inline_math); + 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; + + totalWidth = max(totalWidth, lineWidth); + totalHeight += lineHeight; + + while (textP != NULL && textP->line_number == lineNumber) + { + textP->x += xOff; + textP->y -= yOff; + + xOff += textP->width; + totalWidth = max(totalWidth, xOff); + + textP->line_width = lineWidth; + textP = textP->next; + } + yOff += 0.5 * lineHeight; + lineNumber += 1; + } + + gks_set_text_upvec(chuX, chuY); + + 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; + } + + 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; + } + + xx = textP->x - x; + switch (hAlign) + { + case 2: + xx -= 0.5 * totalWidth; + break; + case 3: + xx -= totalWidth; + break; + default: + break; + } + + yy = textP->y - y; + if (inline_math) yy -= 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; + } + + sx = x + cos(angle) * xx - sin(angle) * yy; + sy = y + sin(angle) * xx + cos(angle) * yy; + + if (textP->math) + gr_mathtex(sx, sy, textP->string); + else + gks_text(sx, sy, textP->string); + + textP = textP->next; + } + } + else + { + xx = x; + switch (hAlign) + { + case 2: + xx -= 0.5 * totalWidth; + break; + case 3: + xx -= totalWidth; + break; + default: + break; + } + yy = y; + switch (vAlign) + { + case 3: + yy += 0.5 * totalHeight; + break; + case 4: + yy += totalHeight; + break; + case 5: + yy += totalHeight; + break; + default: + break; + } + + 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 = 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; + } + } + + while (text != NULL) + { + text_node_t *next = text->next; + free(text->string); + free(text); + text = next; + } + + 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, '$') != NULL) + text_impl(x, y, string, 1, 0, NULL, NULL); + else + gks_text(x, y, string); + + if (tnr != NDC) gks_select_xform(tnr); + + 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 (GR_TEXT_USE_WC, + * GR_TEXT_ENABLE_INLINE_MATH) + * + * 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 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. + */ +void gr_textx(double x, double y, char *string, int opts) +{ + int errind, tnr; + double xn = x, yn = y; + int inline_math = (opts & GR_TEXT_ENABLE_INLINE_MATH) != 0; + + check_autoinit; + + gks_inq_current_xformno(&errind, &tnr); + if (tnr != NDC) + { + if ((opts & GR_TEXT_USE_WC) != 0) gr_wctondc(&xn, &yn); + gks_select_xform(NDC); + } + + if (strchr(string, '\n') != NULL || (strchr(string, '$') != NULL && inline_math)) + text_impl(xn, yn, string, inline_math, 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, opts); +} + +void gr_inqtext(double x, double y, char *string, double *tbx, double *tby) +{ + int errind, tnr, n, wkid; + 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, '$') != NULL) + text_impl(x, y, string, 1, 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) 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; + int inline_math = (opts & GR_TEXT_ENABLE_INLINE_MATH) != 0; + + check_autoinit; + + gks_inq_current_xformno(&errind, &tnr); + if (tnr != NDC) + { + if ((opts & GR_TEXT_USE_WC) != 0) gr_wctondc(&xn, &yn); + gks_select_xform(NDC); + } + + if (strchr(string, '\n') != NULL || (strchr(string, '$') != NULL && inline_math)) + text_impl(xn, yn, string, inline_math, 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) + { + gks_select_xform(tnr); + + if ((opts & GR_TEXT_USE_WC) != 0) + { + 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]); + } + } + } + } +} + void gr_beginselection(int index, int type) { check_autoinit; 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; } diff --git a/lib/gr/mathtex2.c b/lib/gr/mathtex2.c index 44c79f487..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]; @@ -1101,6 +1102,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 +1114,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 +3115,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) @@ -3457,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; @@ -3508,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; } } @@ -3772,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; @@ -3865,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; diff --git a/lib/grm/plot.c b/lib/grm/plot.c index 43cb751de..27e5f1685 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; @@ -2279,7 +2310,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 +2349,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 +2498,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) { @@ -3240,7 +3272,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; } @@ -4890,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; @@ -4918,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)) { @@ -5171,8 +5203,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 +5213,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); @@ -5453,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); @@ -5490,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(); @@ -7184,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 15f8337f3..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 @@ -56,6 +58,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 @@ -79,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 ----------------------------------------------------------------------------------- */ @@ -107,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 ============================================================================== */ @@ -132,6 +152,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); @@ -204,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/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; 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; diff --git a/packaging/redhat/gr.spec b/packaging/redhat/gr.spec index f979ea83a..67d30b3ca 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 @@ -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}