diff --git a/doc/rst/source/cookbook/options.rst b/doc/rst/source/cookbook/options.rst index 8c962595e42..c49a5f38813 100644 --- a/doc/rst/source/cookbook/options.rst +++ b/doc/rst/source/cookbook/options.rst @@ -287,23 +287,27 @@ tick, and gridline intervals, axes labels, and annotation units. The Frame settings are specified by -- **-B**\ [*axes*][**+b**][**+g**\ *fill*][**+i**\ [*val*]][**+n**][**+o**\ *lon/lat*][**+t**\ *title*] +- **-B**\ [*axes*][**+b**][**+g**\ *fill*][**+i**\ [*val*]][**+n**][**+o**\ *lon/lat*][**+t**\ *title*][**+w**\ [*pen*]][**+x**\ *fill*][**+y**\ *fill*][**+z**\ *fill*] Here, the optional *axes* dictates which of the axes should be drawn -and possibly annotated. By default, all 4 map boundaries (or plot axes) +and possibly annotated. By default, all four map boundaries (or plot axes) are plotted (denoted **W**, **E**, **S**, **N**). To change this selection, append the codes for those you want (e.g., **WSn**). In this example, the lower case **n** denotes to draw the axis and (major and minor) tick marks on the "northern" (top) edge of the plot. The upper case **WS** will annotate the "western" and "southern" axes with numerals and plot the any axis labels in addition to draw axis/tick-marks. For 3-D plots you can -also specify **Z** or **z**. By default a single vertical axes will then be +also specify **Z** or **z**. To *just* draw an axis without annotation and +ticks you can use the **l**\ (eft), **r**\ (ight), **b**\ (ottom), **t**\ (op) +and (for 3-D) **u**\ (p) codes. By default, a single vertical axes will then be plotted at the most suitable map corner. You can override this by appending any combination of corner ids **1234**, where **1** represents the lower left -corner and the order goes counter-clockwise. Append **+b** to draw the outline -of the 3-D box defined by **-R**; this modifier is also needed to display -gridlines in the x–z, y–z planes. You may paint the -map canvas by appending the **+g**\ *fill* modifier [Default is no fill]. +corner and the order goes counter-clockwise. Use **+w** to draw the outlines of +the x-z and y-z planes [no outlines] and optionally append the *pen* to use +[:term:`MAP_GRID_PEN_PRIMARY`]. Alternatively, append **+b** to also draw the front lines +of the 3-D cube defined by **-R**. You can paint the interior of the canvas with +**+g**\ *fill* (this also sets fill for the two back-walls in 3-D). +Use **+x**, **+y**, and **+z** to control the painting of planes *yz*, *xz* and *xy*, respectively [Default is no fill]. Use **+i** to annotate an internal meridian or parallel when the axis that normally would be drawn and annotated does not exist (e.g., azimuthal map with 360-degree range has no latitude axis, and a global Hammer map has no longitude axis); diff --git a/doc/rst/source/explain_-B_full.rst_ b/doc/rst/source/explain_-B_full.rst_ index 568f3cfd4e0..3fbc88b3986 100644 --- a/doc/rst/source/explain_-B_full.rst_ +++ b/doc/rst/source/explain_-B_full.rst_ @@ -3,22 +3,24 @@ **-B**\ [**p**\|\ **s**]\ *parameters* Set map Frame and Axes parameters. The Frame parameters are specified by - **-B**\ [*axes*][**+b**][**+g**\ *fill*][**+i**\ [*val*]][**+n**][**+o**\ *lon/lat*][**+t**\ *title*] + **-B**\ [*axes*][**+b**][**+g**\ *fill*][**+i**\ [*val*]][**+n**][**+o**\ *lon/lat*][**+t**\ *title*][**+w**\ [*pen*]][**+x**\ *fill*][**+y**\ *fill*][**+z**\ *fill*] - where *axes* selects which axes to plot. By default, all 4 map boundaries + where *axes* selects which axes to plot. By default, all four map boundaries (or plot axes) are plotted (named **W**, **E**, **S**, **N**). To customize, append the codes for those you want (e.g., **WSn**). Upper case means plot and annotate while lower case just plots and ticks the specified axes. To *just* draw an axis without annotation and ticks you can use the **l**\ (eft), - **r**\ (ight), **b**\ (ottom), **t**\ (opt) and (for 3-D) **u**\ (p) codes. If a 3-D basemap - is selected with **-p** and **-Jz**, append **Z**, **z**, or **u** to control the - appearance of the vertical axis. By default a single vertical axes will be - plotted at the most suitable map corner. Override the default by appending + **r**\ (ight), **b**\ (ottom), **t**\ (op) and (for 3-D) **u**\ (p) codes. + If a 3-D basemap is selected with **-p** and **-Jz**, append **Z**, **z**, or + **u** to control the appearance of the vertical axis. By default a single vertical + axes will be plotted at the most suitable map corner. Override the default by appending any combination of corner ids **1234**, where **1** represents the lower left - corner and the order goes counter-clockwise. Append **+b** to draw the outline of the 3-D - cube defined by **-R**; this modifier is also needed to display gridlines in - the x-z, y-z planes. Note that for 3-D views the title, if given, will be - suppressed. You can paint the interior of the canvas with **+g**\ *fill*. + corner and the order goes counter-clockwise. Use **+w** to draw the outlines of + the x-z and y-z planes [no outline] and optionally append the *pen* to use + [:term:`MAP_GRID_PEN_PRIMARY`]. Alternatively, append **+b** to also draw the front lines + of the 3-D cube defined by **-R**. You can paint the interior of the canvas with + **+g**\ *fill* (this also sets fill for the two back-walls in 3-D). + Use **+x**, **+y**, and **+z** to control the painting of planes *yz*, *xz* and *xy*, respectively [Default is no fill]. Use **+i** to annotate an internal meridian or parallel when the axis that normally would be drawn and annotated does not exist (e.g., azimuthal map with 360-degree range has no latitude axis, and a global Hammer map has no longitude axis); optionally append the parallel or meridian [0]. diff --git a/src/gmt_init.c b/src/gmt_init.c index 43069fa6ddc..dfe81ac3e4a 100644 --- a/src/gmt_init.c +++ b/src/gmt_init.c @@ -2473,14 +2473,14 @@ GMT_LOCAL bool gmtinit_true_false_or_error (char *value, bool *answer) { } /*! . */ -GMT_LOCAL int gmtinit_decode4_wesnz (struct GMT_CTRL *GMT, const char *in, unsigned int side[], bool *draw_box, int part) { +GMT_LOCAL int gmtinit_decode4_wesnz (struct GMT_CTRL *GMT, const char *in, unsigned int side[], unsigned int *draw_box, int part) { /* Scans the WESNZwesnz+ flags at the end of string "in" and sets the side/drawbox parameters * and returns the length of the remaining string. Assumes any +g has been removed from in. */ int i, k, orig_i; - unsigned int side_orig[5]; - bool go = true, orig_draw_box = *draw_box; + unsigned int side_orig[5], orig_draw_box = *draw_box; + bool go = true; GMT->current.map.frame.set_frame[part]++; if (GMT->current.map.frame.set_frame[GMT_PRIMARY] > 1 || GMT->current.map.frame.set_frame[GMT_SECONDARY] > 1) { @@ -2496,7 +2496,7 @@ GMT_LOCAL int gmtinit_decode4_wesnz (struct GMT_CTRL *GMT, const char *in, unsig for (k = 0; go && i >= 0 && strchr ("WESNZwesnz+", in[i]); i--) { if (k == 0 && part == 0) { /* Wipe out default values when the first flag is found */ for (k = 0; k < 5; k++) side[k] = 0; - *draw_box = false; + *draw_box = GMT_3D_NONE; } if (in[i] == 's') { /* Since s can mean both "draw south axis" and "seconds", check further */ if (side[S_SIDE]) go = false; /* If S was set already then s probably means seconds */ @@ -2518,7 +2518,7 @@ GMT_LOCAL int gmtinit_decode4_wesnz (struct GMT_CTRL *GMT, const char *in, unsig case 'n': side[N_SIDE] |= GMT_AXIS_BARB; break; case 'z': side[Z_SIDE] |= GMT_AXIS_BARB; break; /* Draw 3-D box */ - case '+': *draw_box = true; break; + case '+': *draw_box = GMT_3D_BOX; break; } } if (i >= 0 && in[i] == ',') i--; /* Special case for -BCcustomfile,WESNwesn to avoid the filename being parsed for WESN */ @@ -3749,7 +3749,7 @@ GMT_LOCAL int gmtinit_parse4_B_option (struct GMT_CTRL *GMT, char *in) { gmtinit_handle_dosfile (GMT, in, 0); /* Temporarily replace DOS files like X:/ with X;/ to avoid colon trouble */ #endif - for (i = (int)strlen(in) - 1, ignore = false; !GMT->current.map.frame.paint && !error && i >= 0; i--) { /** Look for +gcurrent.map.frame.paint[GMT_Z] && !error && i >= 0; i--) { /** Look for +g/ */ @@ -3775,9 +3775,9 @@ GMT_LOCAL int gmtinit_parse4_B_option (struct GMT_CTRL *GMT, char *in) { #ifdef _WIN32 gmtinit_handle_dosfile (GMT, out1, 1); /* Undo any DOS files like X;/ back to X:/ */ #endif - if (gmt_getfill (GMT, out1, &GMT->current.map.frame.fill)) error++; + if (gmt_getfill (GMT, out1, &GMT->current.map.frame.fill[GMT_Z])) error++; if (!error) { - GMT->current.map.frame.paint = true; + GMT->current.map.frame.paint[GMT_Z] = true; g = i; in[g] = '\0'; /* Chop off +g for now */ } @@ -3948,12 +3948,14 @@ GMT_LOCAL int gmtinit_decode5_wesnz (struct GMT_CTRL *GMT, const char *in, bool /* Draw 3-D box */ case '+': if (in[k+1] == 'b') /* Got +b appended to MAP_FRAME_AXES, possibly */ - GMT->current.map.frame.draw_box = true; + GMT->current.map.frame.draw_box |= GMT_3D_BOX; + else if (in[k+1] == 'w') /* Got +w appended to MAP_FRAME_AXES, possibly */ + GMT->current.map.frame.draw_box |= GMT_3D_WALL; else if (in[k+1] == 'n') /* Got +n appended to MAP_FRAME_AXES, means no frame nor annotations desired */ GMT->current.map.frame.no_frame = true; else if (gmt_M_compat_check (GMT, 4)) { GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Modifier + in MAP_FRAME_AXES is deprecated; use +b instead.\n"); - GMT->current.map.frame.draw_box = true; + GMT->current.map.frame.draw_box |= GMT_3D_BOX; } else { GMT_Report (GMT->parent, GMT_MSG_ERROR, "Modifier + in MAP_FRAME_AXES not recognized.\n"); @@ -3987,6 +3989,10 @@ bool gmtlib_B_is_frame (struct GMT_CTRL *GMT, char *in) { if (strstr (in, "+n")) return true; /* Found a +n so likely frame */ if (strstr (in, "+o")) return true; /* Found a +o so likely frame */ if (strstr (in, "+t")) return true; /* Found a +t so likely frame */ + if (strstr (in, "+w")) return true; /* Found a +w so likely frame */ + if (strstr (in, "+x")) return true; /* Found a +x so likely frame */ + if (strstr (in, "+y")) return true; /* Found a +y so likely frame */ + if (strstr (in, "+z")) return true; /* Found a +z so likely frame */ if (strstr (in, "+a")) return false; /* Found a +a so likely axis */ if (strstr (in, "+f")) return false; /* Found a +f so likely axis */ if (strstr (in, "+l")) return false; /* Found a +l so likely axis */ @@ -4003,11 +4009,13 @@ bool gmtlib_B_is_frame (struct GMT_CTRL *GMT, char *in) { /*! . */ GMT_LOCAL int gmtinit_parse5_B_frame_setting (struct GMT_CTRL *GMT, char *in) { + bool did_g = false; unsigned int pos = 0, k, error = 0; char p[GMT_BUFSIZ] = {""}, text[GMT_BUFSIZ] = {""}, *mod = NULL; double pole[2]; + struct GMT_FILL F; - /* Parsing of -B: -B[][+b][+g][+n][+o/][+t] */ + /* Parsing of -B<framesettings>: -B[<axes>][+b][+g<fill>][+n][+o<lon>/<lat>][+t<title>][+w[<pen>]][+x<fill>][+y<fill>][+z<fill>] */ /* First determine that the given -B<in> string is indeed the framesetting option. If not return -1 */ @@ -4018,23 +4026,25 @@ GMT_LOCAL int gmtinit_parse5_B_frame_setting (struct GMT_CTRL *GMT, char *in) { /* OK, here we are pretty sure this is a frame -B statement */ strncpy (text, in, GMT_BUFSIZ-1); - gmt_handle5_plussign (GMT, text, "bginot", 0); /* Temporarily change double plus-signs to double ASCII 1 to avoid +<modifier> angst */ + gmt_handle5_plussign (GMT, text, "bginotwxyz", 0); /* Temporarily change double plus-signs to double ASCII 1 to avoid +<modifier> angst */ GMT->current.map.frame.header[0] = '\0'; + gmt_M_memset (GMT->current.map.frame.paint, 3U, bool); + GMT->current.map.frame.draw_box = GMT_3D_NONE; if ((mod = strchr (text, '+'))) { /* Find start of modifiers, if any */ while ((gmt_strtok (mod, "+", &pos, p))) { /* Parse any +<modifier> statements */ switch (p[0]) { - case 'b': /* Activate 3-D box and x-z, y-z gridlines (if selected) */ - GMT->current.map.frame.draw_box = true; + case 'b': /* Draw 3-D box */ + GMT->current.map.frame.draw_box |= (GMT_3D_BOX|GMT_3D_WALL); break; - case 'g': /* Paint the basemap interior */ - if (p[1] == 0 || gmt_getfill (GMT, &p[1], &GMT->current.map.frame.fill)) { + case 'g': /* Paint the basemap x-y plane and for 3-D back walls */ + if (p[1] == 0 || gmt_getfill (GMT, &p[1], &F)) { GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +g<fill> argument %s\n", &p[1]); error++; } - GMT->current.map.frame.paint = true; + did_g = true; break; - case 'i': /* Turn on internal annotation for radiual or longitudinal axes when there is no other place to annotate */ + case 'i': /* Turn on internal annotation for radial or longitudinal axes when there is no outside place to annotate */ GMT->current.map.frame.internal_annot = 1; /* Longitude/angle */ if (GMT->current.proj.projection == GMT_POLAR) /* Optional argument is an angle, not longitude, so atof will do */ GMT->current.map.frame.internal_arg = (p[1]) ? atof (&p[1]) : GMT->common.R.wesn[XLO]; @@ -4082,6 +4092,40 @@ GMT_LOCAL int gmtinit_parse5_B_frame_setting (struct GMT_CTRL *GMT, char *in) { gmtlib_enforce_rgb_triplets (GMT, GMT->current.map.frame.header, GMT_LEN256); /* If @; is used, make sure the color information passed on to ps_text is in r/b/g format */ } break; + case 'w': /* Set back-wall outline and optionally the pen to use */ + GMT->current.map.frame.draw_box |= GMT_3D_WALL; + GMT->current.map.frame.draw_wall = true; /* We will draw the outline of the walls */ + if (p[1] && gmt_getpen (GMT, &p[1], &GMT->current.map.frame.pen)) { + GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +w<pen> argument %s\n", &p[1]); + error++; + } + else if (p[1] == 0) /* Use grid pen as default */ + gmt_M_memcpy (&GMT->current.map.frame.pen, &GMT->current.setting.map_grid_pen[GMT_PRIMARY], 1, struct GMT_PEN); + break; + case 'x': /* Paint the basemap yz-plane for 3-D plots */ + if (p[1] == 0 || gmt_getfill (GMT, &p[1], &GMT->current.map.frame.fill[GMT_Y])) { + GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +x<fill> argument %s (or possibly missing)\n", &p[1]); + error++; + } + GMT->current.map.frame.paint[GMT_Y] = true; + GMT->current.map.frame.draw_box |= GMT_3D_WALL; + break; + case 'y': /* Paint the basemap xz-plane for 3-D plots */ + if (p[1] == 0 || gmt_getfill (GMT, &p[1], &GMT->current.map.frame.fill[GMT_X])) { + GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +y<fill> argument %s (or possibly missing)\n", &p[1]); + error++; + } + GMT->current.map.frame.paint[GMT_X] = true; + GMT->current.map.frame.draw_box |= GMT_3D_WALL; + break; + case 'z': /* Paint the basemap xy-plane for 3-D plots */ + if (p[1] && gmt_getfill (GMT, &p[1], &GMT->current.map.frame.fill[GMT_Z])) { + GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad +z<fill> argument %s\n", &p[1]); + error++; + } + GMT->current.map.frame.paint[GMT_Z] = true; + GMT->current.map.frame.draw_box |= GMT_3D_WALL; + break; default: GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -B: Unrecognized frame modifier %s\n", p); error++; @@ -4091,6 +4135,19 @@ GMT_LOCAL int gmtinit_parse5_B_frame_setting (struct GMT_CTRL *GMT, char *in) { *mod = '\0'; /* Separate the modifiers from the frame selectors */ } + if (!GMT->current.map.frame.paint[GMT_X] && did_g) { /* Let +g set all sides not already set */ + gmt_M_memcpy (&GMT->current.map.frame.fill[GMT_X], &F, 1U, struct GMT_FILL); + GMT->current.map.frame.paint[GMT_X] = true; + } + if (!GMT->current.map.frame.paint[GMT_Y] && did_g) { /* Let +g set all sides not already set */ + gmt_M_memcpy (&GMT->current.map.frame.fill[GMT_Y], &F, 1U, struct GMT_FILL); + GMT->current.map.frame.paint[GMT_Y] = true; + } + if (!GMT->current.map.frame.paint[GMT_Z] && did_g) { /* Let +g set all sides not already set */ + gmt_M_memcpy (&GMT->current.map.frame.fill[GMT_Z], &F, 1U, struct GMT_FILL); + GMT->current.map.frame.paint[GMT_Z] = true; + } + /* Now parse the frame choices, if any */ error += gmtinit_decode5_wesnz (GMT, text, true); @@ -4105,6 +4162,7 @@ void gmt_init_B (struct GMT_CTRL *GMT) { for (k = 0; k < 6; k++) GMT->current.map.frame.axis[no].item[k].parent = no; if (GMT->current.proj.xyz_projection[no] == GMT_TIME) GMT->current.map.frame.axis[no].type = GMT_TIME; } + gmt_M_memset (GMT->current.map.frame.paint, 3U, bool); GMT->common.B.string[0][0] = GMT->common.B.string[1][0] = '\0'; GMT->current.map.frame.init = true; GMT->current.map.frame.draw = false; @@ -4115,11 +4173,13 @@ void gmt_init_B (struct GMT_CTRL *GMT) { GMT_LOCAL int gmtinit_parse5_B_option (struct GMT_CTRL *GMT, char *in) { /* GMT5 clean version based on new syntax: * Frame settings: - * -B[WESNwesnz|Z[1234]][+b][+g<fill>][+o<lon/lat>][+t<title>] - * +b enables 3-D box and x-z, y-z gridlines. - * +g<fill> as plot interior fill [none]. + * -B[WESNwesnz|Z[1234]][+b][+g<fill>][+o<lon/lat>][+t<title>][+w[<pen>]][+x<fill>][+y<fill>][+z<fill>] + * +b draws 3-D box. + * +g<fill> as plot interior fill and backwalls (if 3-D) [none]. * +t<title> as plot title [none]. + * +w[<pen>] draws the outline of the xz and yz planes [no outline]. * of one or more of w,e,s,n,z then only those axes will be drawn. + * +x, +y, +z paints the back walls for yz, xz, xy [with +g fill if not specified]. * Upper case letters means the chosen axes also will be annotated. * Default is determined by MAP_FRAME_AXES setting [WESN]. * Axis settings: @@ -4162,8 +4222,8 @@ GMT_LOCAL int gmtinit_parse5_B_option (struct GMT_CTRL *GMT, char *in) { if ((error = gmtinit_parse5_B_frame_setting (GMT, in)) >= 0) return (error); /* Parsed the -B frame settings separately */ error = 0; /* Reset since otherwise it is -1 */ - if (strstr (in, "+b") || strstr (in, "+g") || strstr (in, "+i") || strstr (in, "+n") || strstr (in, "+o") || strstr (in, "+o") || strstr (in, "+t")) { - GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -B: Found frame setting modifiers (+b|g|i|n|o|p) mixed with axes settings!\n"); + if (strstr (in, "+b") || strstr (in, "+g") || strstr (in, "+i") || strstr (in, "+n") || strstr (in, "+o") || strstr (in, "+o") || strstr (in, "+t") || strstr (in, "+w") || strstr (in, "+x") || strstr (in, "+y") || strstr (in, "+z")) { + GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -B: Found frame setting modifiers (+b|g|i|n|o|p|W) mixed with axes settings!\n"); return (GMT_PARSE_ERROR); } @@ -5835,7 +5895,7 @@ void gmt_conf (struct GMT_CTRL *GMT) { /* MAP_FRAME_AXES */ strcpy (GMT->current.setting.map_frame_axes, "WESNZ"); for (i = 0; i < 5; i++) GMT->current.map.frame.side[i] = 0; /* Unset default settings */ - GMT->current.map.frame.draw_box = false; + GMT->current.map.frame.draw_box = GMT_3D_NONE; error += gmtinit_decode5_wesnz (GMT, "WESNZ", false); /* MAP_DEFAULT_PEN */ error += gmt_getpen (GMT, "default,black", &GMT->current.setting.map_default_pen); @@ -6513,24 +6573,25 @@ void gmtlib_explain_options (struct GMT_CTRL *GMT, char *options) { gmt_message (GMT, "\t-B Specify both (1) basemap frame settings and (2) axes parameters.\n"); gmt_message (GMT, "\t Frame settings are modified via an optional single invocation of\n"); - gmt_message (GMT, "\t -B[<axes>][+b][+g<fill>][+i[<val>]][+n][+o<lon>/<lat>][+t<title>]\n"); + gmt_message (GMT, "\t -B[<axes>][+b][+g<fill>][+i[<val>]][+n][+o<lon>/<lat>][+t<title>][+w[<pen>]][+x<fill>][+y<fill>][+z<fill>]\n"); gmt_message (GMT, "\t Axes parameters are specified via one or more invocations of\n"); gmt_message (GMT, "\t -B[p|s][x|y|z]<info>\n\n"); gmt_message (GMT, "\t 1. Frame settings control which axes to plot, frame fill, title, and type of gridlines:\n"); gmt_message (GMT, "\t <axes> is a combination of W,E,S,N,Z and plots those axes only [Default is WESNZ (all)].\n"); gmt_message (GMT, "\t Use lower case w,e,s,n,z just to draw and tick (but not annotate) those axes,\n"); gmt_message (GMT, "\t and use l,r,b,t,u just to draw (but not annotate and tick) those axes.\n"); - gmt_message (GMT, "\t For 3-D plots the Z|z[<corners>][+b] controls the vertical axis. The <corners> specifies\n"); - gmt_message (GMT, "\t at which corner(s) to erect the axis via a combination of 1,2,3,4; 1 means lower left corner,\n"); - gmt_message (GMT, "\t 2 is lower right, etc., in a counter-clockwise order. [Default automatically selects one axis].\n"); - gmt_message (GMT, "\t The optional +b will erect a 3-D frame box to outline the 3-D domain [no frame box]. The +b\n"); - gmt_message (GMT, "\t is also required for x-z or y-z gridlines to be plotted (if such gridlines are selected below).\n"); gmt_message (GMT, "\t Append +g<fill> to paint the inside of the map region before further plotting [no fill].\n"); gmt_message (GMT, "\t Append +i<val> to annotate along parallel or meridian <val> [0] when no such axes can be plotted.\n"); gmt_message (GMT, "\t Append +n to have no frame and annotations whatsoever [Default is controlled by WESNZ/wesnz].\n"); gmt_message (GMT, "\t Append +o<plon>/<plat> to draw oblique gridlines about this pole [regular gridlines].\n"); gmt_message (GMT, "\t Note: the +o modifier is ignored unless gridlines are specified via the axes parameters (below).\n"); gmt_message (GMT, "\t Append +t<title> to place a title over the map frame [no title].\n"); + gmt_message (GMT, "\t For 3-D plots the Z|z[<corners>] controls the vertical axis. The <corners> specifies\n"); + gmt_message (GMT, "\t at which corner(s) to erect the z-axis via a combination of 1,2,3,4; 1 means lower left corner,\n"); + gmt_message (GMT, "\t 2 is lower right, etc., in a counter-clockwise order [Default automatically selects one axis].\n"); + gmt_message (GMT, "\t The +w draws the outline of the xz and yz planes [no outlines].\n"); + gmt_message (GMT, "\t The +x|y|z[<fill>] paint the yz, xz, xy planes [no fill]. The +g<fill> sets all three planes.\n"); + gmt_message (GMT, "\t In addition, +b will erect a 3-D frame box to outline the 3-D domain [no frame box].\n"); gmt_message (GMT, "\t 2. Axes settings control the annotation, tick, and grid intervals and labels.\n"); gmt_message (GMT, "\t The full axes specification is\n"); gmt_message (GMT, "\t -B[p|s][x|y|z]<intervals>[+a<angle>|n|p][+f][+l|L<label>][+p<prefix>][+s|S<secondary_label>][+u<unit>]\n"); @@ -6545,8 +6606,7 @@ void gmtlib_explain_options (struct GMT_CTRL *GMT, char *options) { gmt_message (GMT, "\t To prepend a prefix to each annotation (e.g., $ 10, $ 20 ...), add +p<prefix>.\n"); gmt_message (GMT, "\t To append a unit to each annotation (e.g., 5 km, 10 km ...), add +u<unit>.\n"); gmt_message (GMT, "\t Cartesian x-axis takes optional +a<angle> for slanted or +an for orthogonal annotations [+ap].\n"); - gmt_message (GMT, "\t Cartesian y-axis takes optional +ap for parallel annotations [+an].\n"); - gmt_message (GMT, "\t Cartesian z-axis takes optional +an for parallel annotations [+an].\n"); + gmt_message (GMT, "\t Cartesian y- and z-axes take optional +ap for parallel annotations [+an].\n"); gmt_message (GMT, "\t Geographic axes take optional +f for \"fancy\" annotations with W|E|S|N suffices.\n"); gmt_message (GMT, "\t To label an axis, add +l<label>. Use +L to enforce horizontal labels for y-axes.\n"); gmt_message (GMT, "\t For another axis label on the opposite axis, use +s|S as well.\n"); @@ -7658,8 +7718,8 @@ void gmt_syntax (struct GMT_CTRL *GMT, char option) { switch (option) { case 'B': /* Tickmark option */ - gmt_message (GMT, "\t-B[p|s][x|y|z]<intervals>[+a<angle>|n|p][+l|L<label>][+p<prefix>][+u<unit>] -B[<axes>][+b][+g<fill>][+o<lon>/<lat>][+t<title>] OR\n"); - gmt_message (GMT, "\t-B[p|s][x|y|z][a|f|g]<tick>[m][l|p] -B[p|s][x|y|z][+l<label>][+p<prefix>][+u<unit>] -B[<axes>][+b][+g<fill>][+o<lon>/<lat>][+t<title>]\n"); + gmt_message (GMT, "\t-B[p|s][x|y|z]<intervals>[+a<angle>|n|p][+l|L<label>][+p<prefix>][+u<unit>] -B[<axes>][+b][+g<fill>][+o<lon>/<lat>][+t<title>][+w[<pen>]][+x<fill>][+y<fill>][+z<fill>] OR\n"); + gmt_message (GMT, "\t-B[p|s][x|y|z][a|f|g]<tick>[m][l|p] -B[p|s][x|y|z][+l<label>][+p<prefix>][+u<unit>] -B[<axes>][+b][+g<fill>][+o<lon>/<lat>][+t<title>][+w[<pen>]][+x<fill>][+y<fill>][+z<fill>]\n"); break; case 'J': /* Map projection option */ @@ -9728,7 +9788,7 @@ unsigned int gmtlib_setparameter (struct GMT_CTRL *GMT, const char *keyword, cha case GMTCASE_MAP_FRAME_AXES: strncpy (GMT->current.setting.map_frame_axes, value, 5U); for (i = 0; i < 5; i++) GMT->current.map.frame.side[i] = 0; /* Unset default settings */ - GMT->current.map.frame.draw_box = false; + GMT->current.map.frame.draw_box = GMT_3D_NONE; error += (bool)gmtinit_decode5_wesnz (GMT, value, false); break; @@ -12635,11 +12695,11 @@ GMT_LOCAL struct GMT_CTRL *gmtinit_begin_module_sub (struct GMTAPI_CTRL *API, co GMT->common.p.active = GMT->common.s.active = GMT->common.t.active = GMT->common.colon.active = false; gmt_M_memset (GMT->common.b.ncol, 2, int); - /* Initialize bg fill to white although we don't use it until GMT->current.map.frame.paint = true; + /* Initialize bg fill to white although we don't use it until GMT->current.map.frame.paint[GMT_Z] = true; But needed when using images with an transparency layer. */ - GMT->current.map.frame.fill.rgb[0] = GMT->current.map.frame.fill.rgb[1] = GMT->current.map.frame.fill.rgb[2] = 1.0; + GMT->current.map.frame.fill[GMT_Z].rgb[0] = GMT->current.map.frame.fill[GMT_Z].rgb[1] = GMT->current.map.frame.fill[GMT_Z].rgb[2] = 1.0; *Ccopy = Csave; /* Pass back out for safe-keeping by the module until gmt_end_module is called */ @@ -14303,8 +14363,8 @@ void gmt_end_module (struct GMT_CTRL *GMT, struct GMT_CTRL *Ccopy) { gmtinit_free_user_media (GMT); /* Free user-specified media formats */ /* Reset frame fill painting */ - Ccopy->current.map.frame.paint = GMT->current.map.frame.paint; - gmt_M_memcpy (Ccopy->current.map.frame.fill.rgb, GMT->current.map.frame.fill.rgb, 3, double); + Ccopy->current.map.frame.paint[GMT_Z] = GMT->current.map.frame.paint[GMT_Z]; + gmt_M_memcpy (Ccopy->current.map.frame.fill[GMT_Z].rgb, GMT->current.map.frame.fill[GMT_Z].rgb, 3, double); /* GMT_IO */ diff --git a/src/gmt_map.c b/src/gmt_map.c index 858aa48ae17..f1554f5c81f 100644 --- a/src/gmt_map.c +++ b/src/gmt_map.c @@ -6584,7 +6584,7 @@ void gmt_auto_frame_interval (struct GMT_CTRL *GMT, unsigned int axis, unsigned double Hmaj[4] = {2.0, 3.0, 6.0, 12.0}, Hsub[4] = {1.0, 1.0, 3.0, 3.0}; double Omaj[4] = {3.0, 6.0}, Osub[4] = {1.0, 3.0}; double Dmaj[4] = {2.0, 3.0, 7.0, 14.0}, Dsub[4] = {1.0, 1.0, 1.0, 7.0}; - double d, f, p, *maj = defmaj, *sub = defsub; + double d, f, p, sxy = 1.0, sz = 1.0, *maj = defmaj, *sub = defsub; struct GMT_PLOT_AXIS *A = &GMT->current.map.frame.axis[axis]; struct GMT_PLOT_AXIS_ITEM *T; @@ -6595,21 +6595,24 @@ void gmt_auto_frame_interval (struct GMT_CTRL *GMT, unsigned int axis, unsigned !(A->item[item+2].active && A->item[item+2].interval == 0.0) && !(A->item[item+4].active && A->item[item+4].interval == 0.0)) return; + if (GMT->current.proj.three_D) { /* Make an approximate adjustment to the "sizes" given a perspective view */ + sxy = 1.0 - cosd (GMT->current.proj.z_project.view_elevation); + sz = 1.0 - fabs (sind (GMT->current.proj.z_project.view_elevation)); + } /* f = frame width/height (inch); d = domain width/height (world coordinates) */ if (axis == GMT_X) { - f = fabs (GMT->current.proj.rect[XHI] - GMT->current.proj.rect[XLO]); + f = sxy * fabs (GMT->current.proj.rect[XHI] - GMT->current.proj.rect[XLO]); d = fabs (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]); } else if (axis == GMT_Y) { - f = fabs (GMT->current.proj.rect[YHI] - GMT->current.proj.rect[YLO]); + f = sxy * fabs (GMT->current.proj.rect[YHI] - GMT->current.proj.rect[YLO]); d = fabs (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]); } else { - f = fabs (GMT->current.proj.zmax - GMT->current.proj.zmin); + f = sz * fabs (GMT->current.proj.zmax - GMT->current.proj.zmin); d = fabs (GMT->common.R.wesn[ZHI] - GMT->common.R.wesn[ZLO]); } f *= GMT->session.u2u[GMT_INCH][GMT_PT]; /* Change to points */ - /* First guess of interval */ d *= MAX (0.05, MIN (5.0 * GMT->current.setting.font_annot[item].size / f, 0.20)); @@ -6678,7 +6681,8 @@ void gmt_auto_frame_interval (struct GMT_CTRL *GMT, unsigned int axis, unsigned /* Finally set grid interval (if annotation set as well, use major, otherwise minor interval) */ T = &A->item[item+4]; if (T->active && T->interval == 0.0) { - T->interval = set_a ? d : f, T->generated = true; + T->interval = set_a ? d : f, T->generated = true; /* Commented out because f is too fine for gridline spacing, even if no annotations */ + // T->interval = d, T->generated = true; snprintf (tmp, GMT_LEN16, "g%g", T->interval); strcat (string, tmp); if (is_time) T->unit = unit, strcat (string, sunit); } diff --git a/src/gmt_parse.c b/src/gmt_parse.c index 815cf048f87..20107a2f5ec 100644 --- a/src/gmt_parse.c +++ b/src/gmt_parse.c @@ -86,7 +86,7 @@ GMT_LOCAL int gmtparse_B_arg_inspector (struct GMT_CTRL *GMT, char *in) { int gmt4 = 0, gmt5 = 0, n_digits = 0, n_colons = 0, n_slashes = 0, colon_text = 0, wesn_at_end = 0; bool ignore = false; /* true if inside a colon-separated string under GMT4 style assumption */ bool ignore5 = false; /* true if label, title, prefix, suffix */ - bool custom = false; /* True if -B[p|s][x|y|z]c<filename> was given; then we relax checing for .c (old second) */ + bool custom = false; /* True if -B[p|s][x|y|z]c<filename> was given; then we relax checking for .c (old second) */ char mod = 0; if (!in || in[0] == 0) return (9); /* Just a safety precaution, 9 means "either" syntax but it is an empty string */ @@ -124,10 +124,12 @@ GMT_LOCAL int gmtparse_B_arg_inspector (struct GMT_CTRL *GMT, char *in) { if (ignore) continue; /* Don't look inside a title or label */ switch (in[k]) { case '/': if (mod == 0) n_slashes++; break; /* Only GMT4 uses slashes */ - case '+': /* Plus, might be GMT5 modifier switch */ + case '+': /* Plus, might be a GMT5 modifier switch */ if (k < last && in[k+1] == 'u') {mod = 'u'; ignore5 = true; gmt5++;} /* unit (suffix) settings */ else if (k < last && in[k+1] == 'b') {mod = 'b'; ignore5 = false; gmt5++;} /* 3-D box settings */ else if (k < last && in[k+1] == 'g') {mod = 'g'; ignore5 = false; gmt5++;} /* fill settings */ + else if (k < last && in[k+1] == 'i') {mod = 'i'; ignore5 = false; gmt5++;} /* internal annotation settings */ + else if (k < last && in[k+1] == 'n') {mod = 'n'; ignore5 = true; gmt5++;} /* Turn off frames and annotations */ else if (k < last && in[k+1] == 'o') {mod = 'o'; ignore5 = false; gmt5++;} /* oblique pole settings */ else if (k < last && in[k+1] == 'p') {mod = 'p'; ignore5 = true; gmt5++;} /* prefix settings */ else if (k < last && in[k+1] == 'l') {mod = 'l'; ignore5 = true; gmt5++;} /* Label */ @@ -135,7 +137,10 @@ GMT_LOCAL int gmtparse_B_arg_inspector (struct GMT_CTRL *GMT, char *in) { else if (k < last && in[k+1] == 's') {mod = 's'; ignore5 = true; gmt5++;} /* Secondary label */ else if (k < last && in[k+1] == 'S') {mod = 'S'; ignore5 = true; gmt5++;} /* Forced horizontal Secondary lLabel */ else if (k < last && in[k+1] == 't') {mod = 't'; ignore5 = true; gmt5++;} /* title */ - else if (k < last && in[k+1] == 'n') {mod = 'n'; ignore5 = true; gmt5++;} /* Turn off frames and annotations */ + else if (k < last && in[k+1] == 'w') {mod = 'w'; ignore5 = false; gmt5++;} /* Pen for walls */ + else if (k < last && in[k+1] == 'x') {mod = 'x'; ignore5 = false; gmt5++;} /* Paint for yz plane */ + else if (k < last && in[k+1] == 'y') {mod = 'y'; ignore5 = false; gmt5++;} /* Paint for xz plane */ + else if (k < last && in[k+1] == 'z') {mod = 'z'; ignore5 = false; gmt5++;} /* Paint for xy plane */ else if (k && (in[k-1] == 'Z' || in[k-1] == 'z')) {ignore5 = false; gmt4++;} /* Z-axis with 3-D box */ break; case 'c': /* If following a number this is unit c for seconds in GMT4 */ diff --git a/src/gmt_plot.c b/src/gmt_plot.c index 75a30a85327..38c8c19d8f8 100644 --- a/src/gmt_plot.c +++ b/src/gmt_plot.c @@ -1556,13 +1556,20 @@ GMT_LOCAL void gmtplot_map_symbol_ns (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL if (nc) gmt_M_free (GMT, xings); } -GMT_LOCAL void gmtplot_z_gridlines (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double zmin, double zmax, int plane) { - unsigned int k, i, nz, item[2] = {GMT_GRID_UPPER, GMT_GRID_LOWER}; - double dz, zz, min, max, *z = NULL; +GMT_LOCAL void gmtplot_z_gridlines (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double zmin, double zmax, int plane, unsigned int mode3d, int quadrant) { + unsigned int k, i, nz, n, item[2] = {GMT_GRID_UPPER, GMT_GRID_LOWER}; + int qplane = 1 - plane; + double dz, zz, dd, min, max, z0, z1, xx, dummy, value, *z = NULL, *d = NULL; char *plane_name[2] = {"y-z", "x-z"}; - min = (plane == GMT_Y) ? GMT->current.proj.rect[XLO] : GMT->current.proj.rect[YLO]; - max = (plane == GMT_Y) ? GMT->current.proj.rect[XHI] : GMT->current.proj.rect[YHI]; + if (qplane == GMT_X) { + value = (quadrant == 4) ? GMT->common.R.wesn[YHI] : GMT->common.R.wesn[YLO]; + min = GMT->current.proj.rect[XLO]; max = GMT->current.proj.rect[XHI]; + } + else { + value = (quadrant == 3) ? GMT->common.R.wesn[XHI] : GMT->common.R.wesn[XLO]; + min = GMT->current.proj.rect[YLO]; max = GMT->current.proj.rect[YHI]; + } for (k = 0; k < 2; k++) { if (fabs (GMT->current.setting.map_grid_cross_size[k]) > 0.0) continue; @@ -1581,10 +1588,30 @@ GMT_LOCAL void gmtplot_z_gridlines (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, zz = gmt_z_to_zz (GMT, z[i]); PSL_plotsegment (PSL, min, zz, max, zz); } + z0 = gmt_z_to_zz (GMT, z[0]); + z1 = gmt_z_to_zz (GMT, z[nz-1]); + + dd = gmtlib_get_map_interval (GMT, &GMT->current.map.frame.axis[qplane].item[item[k]]); + if (!GMT->current.map.frame.axis[qplane].item[item[k]].active || fabs(dd) == 0.0) continue; + n = gmtlib_coordinate_array (GMT, GMT->common.R.wesn[2*qplane], GMT->common.R.wesn[2*qplane+1], &GMT->current.map.frame.axis[qplane].item[item[k]], &d, NULL); + for (i = 0; i < n; i++) { /* Here z acts as y and x|y acts as x */ + /* Draw one horizontal line */ + if (qplane == GMT_X) /* The "x" are projection x or longitude */ + gmt_geo_to_xy (GMT, d[i], value, &xx, &dummy); + else /* The "x" are projection y or latitude */ + gmt_geo_to_xy (GMT, value, d[i], &dummy, &xx); + + PSL_plotsegment (PSL, xx, z0, xx, z1); + } + gmt_M_free (GMT, d); PSL_setdash (PSL, NULL, 0); - gmt_M_free (GMT, z); } + if (!GMT->current.map.frame.draw_wall && z && (mode3d & GMT_3D_BOX) == 0) { + PSL_plotsegment (PSL, min, z0, min, z1); + PSL_plotsegment (PSL, max, z0, max, z1); + } + gmt_M_free (GMT, z); } GMT_LOCAL int gmtplot_save_current_gridlines (struct GMT_CTRL *GMT) { @@ -2625,12 +2652,42 @@ GMT_LOCAL bool gmtplot_is_fancy_boundary (struct GMT_CTRL *GMT) { } } -GMT_LOCAL void gmtplot_vertical_wall (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, int quadrant, double *nesw, bool back) { - int plane = (quadrant + 1) % 2; +GMT_LOCAL void gmtplot_vertical_wall (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, int quadrant, double *nesw, bool back, unsigned int mode3d) { + /* Draws the two vertical walls in the back of the 3-D plot */ + int plane = (quadrant + 1) % 2; /* Black magic that gives us GMT_X (for yz plane) or GMT_Y (for xz plane) */ + double xx[4], yy[4]; + gmt_plane_perspective (GMT, plane, nesw[quadrant % 4]); + /* Assign the coordinates of the four corners of the wall (hence assumed to be rectangular) */ + xx[0] = xx[1] = nesw[(quadrant+1)%4]; xx[2] = xx[3] = nesw[(quadrant+3)%4]; + yy[0] = yy[3] = GMT->current.proj.zmin; yy[1] = yy[2] = GMT->current.proj.zmax; + if (mode3d & GMT_3D_WALL && back) { /* Called for one of the back walls and we wish to draw it */ + int wplane = 1 - plane; /* Get integer for the correct plain side */ + if (GMT->current.map.frame.paint[wplane]) { /* First paint the back wall, with no outline drawn */ + PSL_setfill (PSL, GMT->current.map.frame.fill[wplane].rgb, 0); + PSL_plotbox (PSL, nesw[(quadrant+1)%4], GMT->current.proj.zmin, nesw[(quadrant+3)%4], GMT->current.proj.zmax); + } + if (back) /* Gridlines was requested, so draw those now */ + gmtplot_z_gridlines (GMT, PSL, GMT->common.R.wesn[ZLO], GMT->common.R.wesn[ZHI], plane, mode3d, quadrant); + + if (GMT->current.map.frame.draw_wall) { /* We wanted the outline of the back-wall drawn, do now to overwrite any perimeter gridlines */ + gmt_setpen (GMT, &GMT->current.map.frame.pen); + PSL_plotline (PSL, xx, yy, 4, PSL_MOVE|PSL_STROKE); + } + } + else if (back) /* Get here if no back-walls were filled our outlined */ + gmtplot_z_gridlines (GMT, PSL, GMT->common.R.wesn[ZLO], GMT->common.R.wesn[ZHI], plane, mode3d, quadrant); +} + +GMT_LOCAL void gmtplot_cube_box (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, int quadrant, double *nesw) { + /* Draws the 3-D cube lines */ + int plane = (quadrant + 1) % 2; /* Black magic that gives us GMT_X (for yz plane) or GMT_Y (for xz plane) */ + double xx[4], yy[4]; gmt_plane_perspective (GMT, plane, nesw[quadrant % 4]); - PSL_plotbox (PSL, nesw[(quadrant+1)%4], GMT->current.proj.zmin, nesw[(quadrant+3)%4], GMT->current.proj.zmax); - if (back) - gmtplot_z_gridlines (GMT, PSL, GMT->common.R.wesn[ZLO], GMT->common.R.wesn[ZHI], plane); + /* Assign the coordinates of the four corners of the wall (hence assumed to be rectangular) */ + xx[0] = xx[1] = nesw[(quadrant+1)%4]; xx[2] = xx[3] = nesw[(quadrant+3)%4]; + yy[0] = yy[3] = GMT->current.proj.zmin; yy[1] = yy[2] = GMT->current.proj.zmax; + gmt_setpen (GMT, &GMT->current.map.frame.pen); + PSL_plotline (PSL, xx, yy, 4, PSL_MOVE|PSL_STROKE); } GMT_LOCAL void gmtplot_timestamp (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double x, double y, unsigned int justify, char *U_label) { @@ -5466,7 +5523,7 @@ void gmt_map_basemap (struct GMT_CTRL *GMT) { PSL_setdash (PSL, NULL, 0); /* To ensure no dashed pens are set prior */ - gmt_vertical_axis (GMT, GMT->current.plot.mode_3D); + if (GMT->current.proj.three_D && GMT->current.map.frame.drawz) GMT->current.map.frame.plotted_header = true; /* Just so it is not plotted by gmtplot_map_boundary first */ if (GMT->current.proj.got_azimuths) gmt_M_uint_swap (GMT->current.map.frame.side[E_SIDE], GMT->current.map.frame.side[W_SIDE]); /* Temporary swap to trick justify machinery */ @@ -5491,6 +5548,10 @@ void gmt_map_basemap (struct GMT_CTRL *GMT) { PSL_setdash (PSL, NULL, 0); + if (GMT->current.proj.three_D && GMT->current.map.frame.drawz) GMT->current.map.frame.plotted_header = false; /* Now we can plot the title [if selected via -B+t] */ + + gmt_vertical_axis (GMT, GMT->current.plot.mode_3D); + PSL_comment (PSL, "End of basemap\n"); for (side = 0; side < 4; side++) { /* Reset annotation crowdedness arrays */ @@ -5504,6 +5565,19 @@ void gmt_map_basemap (struct GMT_CTRL *GMT) { PSL_setcolor (PSL, GMT->current.setting.map_default_pen.rgb, PSL_IS_STROKE); } +GMT_LOCAL bool gmtplot_z_axis_side (struct GMT_CTRL *GMT, unsigned int axis, unsigned int quadrant) { + bool below; + axis++; /* 1-4 */ + switch (axis) { + case 1: below = (quadrant == 2); break; + case 2: below = (quadrant != 3); break; + case 3: below = (quadrant != 2); break; + case 4: below = (quadrant == 3); break; + default: below = true; /* Just to avoid Coverity issues */ + } + return below; +} + void gmt_vertical_axis (struct GMT_CTRL *GMT, unsigned int mode) { /* Mode means: 1 = background walls and title, 2 = foreground walls and axis, 3 = all */ unsigned int fore, back, old_plane, form; @@ -5513,6 +5587,11 @@ void gmt_vertical_axis (struct GMT_CTRL *GMT, unsigned int mode) { if (!GMT->current.proj.three_D) return; if (!GMT->current.map.frame.drawz) return; + if (GMT->current.map.frame.axis[GMT_Z].item[GMT_GRID_UPPER].active || GMT->current.map.frame.axis[GMT_Z].item[GMT_GRID_LOWER].active) { + GMT->current.map.frame.draw_box |= GMT_3D_WALL; + GMT->current.map.frame.draw_wall = true; + } + nesw[0] = GMT->current.proj.rect[YHI], nesw[1] = GMT->current.proj.rect[XHI], nesw[2] = GMT->current.proj.rect[YLO], nesw[3] = GMT->current.proj.rect[XLO]; fore = mode & 2, back = mode & 1; @@ -5527,12 +5606,18 @@ void gmt_vertical_axis (struct GMT_CTRL *GMT, unsigned int mode) { PSL_setfill (PSL, GMT->session.no_rgb, 1); gmt_setpen (GMT, &GMT->current.setting.map_grid_pen[GMT_PRIMARY]); if (fore) { - gmtplot_vertical_wall (GMT, PSL, GMT->current.proj.z_project.quadrant + 3, nesw, false); - gmtplot_vertical_wall (GMT, PSL, GMT->current.proj.z_project.quadrant , nesw, false); + gmtplot_vertical_wall (GMT, PSL, GMT->current.proj.z_project.quadrant + 3, nesw, false, GMT->current.map.frame.draw_box); + gmtplot_vertical_wall (GMT, PSL, GMT->current.proj.z_project.quadrant , nesw, false, GMT->current.map.frame.draw_box); } if (back) { - gmtplot_vertical_wall (GMT, PSL, GMT->current.proj.z_project.quadrant + 1, nesw, true); - gmtplot_vertical_wall (GMT, PSL, GMT->current.proj.z_project.quadrant + 2, nesw, true); + gmtplot_vertical_wall (GMT, PSL, GMT->current.proj.z_project.quadrant + 1, nesw, true, GMT->current.map.frame.draw_box); + gmtplot_vertical_wall (GMT, PSL, GMT->current.proj.z_project.quadrant + 2, nesw, true, GMT->current.map.frame.draw_box); + } + if (GMT->current.map.frame.draw_box & GMT_3D_BOX) { + gmtplot_cube_box (GMT, PSL, GMT->current.proj.z_project.quadrant + 3, nesw); + gmtplot_cube_box (GMT, PSL, GMT->current.proj.z_project.quadrant , nesw); + gmtplot_cube_box (GMT, PSL, GMT->current.proj.z_project.quadrant + 1, nesw); + gmtplot_cube_box (GMT, PSL, GMT->current.proj.z_project.quadrant + 2, nesw); } } @@ -5540,6 +5625,7 @@ void gmt_vertical_axis (struct GMT_CTRL *GMT, unsigned int mode) { if (fore && GMT->current.map.frame.side[Z_SIDE]) { unsigned int k, n_z, quadrant, corner_to_quadrant[5] = {0, 2, 1, 4, 3}, z_axis[4]; /* Given corner ID 1-4, return quadrant, or vice versa (0 is unused) */ + bool below; gmt_M_memcpy (z_axis, GMT->current.map.frame.z_axis, 4, unsigned int); for (k = n_z = 0; k < 4; k++) /* Count # of vertical axes specified; if 0 then we do an auto-select */ if (z_axis[k]) n_z++; @@ -5548,13 +5634,14 @@ void gmt_vertical_axis (struct GMT_CTRL *GMT, unsigned int mode) { for (k = 0; k < 4; k++) { if (z_axis[k] == 0) continue; /* Not drawing this vertical axis */ quadrant = corner_to_quadrant[k+1]; /* Given corner (k+1), return quadrant */ + below = gmtplot_z_axis_side (GMT, k, GMT->current.proj.z_project.quadrant); gmt_xyz_to_xy (GMT, nesw[(quadrant/2*2+1)%4], nesw[((quadrant+1)/2*2)%4], GMT->common.R.wesn[ZLO], &xx, &yy); /* Restrict reduced azimuth to -45 to 45 range */ az = GMT->current.proj.z_project.view_azimuth - 90.0 - floor ((GMT->current.proj.z_project.view_azimuth - 45.0) / 90.0) * 90.0; PSL_command (PSL, "/PSL_GPP matrix currentmatrix def [%.12g %.12g %.12g %.12g %.12g %.12g] concat\n", cosd(az), sind(az) * GMT->current.proj.z_project.sin_el, 0.0, GMT->current.proj.z_project.cos_el, xx * PSL->internal.x2ix, yy * PSL->internal.y2iy); gmt_xy_axis (GMT, 0.0, -GMT->common.R.wesn[ZLO], GMT->current.proj.zmax - GMT->current.proj.zmin, GMT->common.R.wesn[ZLO], - GMT->common.R.wesn[ZHI], &GMT->current.map.frame.axis[GMT_Z], true, GMT->current.map.frame.side[Z_SIDE]); + GMT->common.R.wesn[ZHI], &GMT->current.map.frame.axis[GMT_Z], below, GMT->current.map.frame.side[Z_SIDE]); PSL_command (PSL, "PSL_GPP setmatrix\n"); } } @@ -7578,10 +7665,10 @@ struct PSL_CTRL *gmt_plotinit (struct GMT_CTRL *GMT, struct GMT_OPTION *options) PSL = GMT->PSL; /* Shorthand */ - if (GMT->current.map.frame.paint) { /* Must squirrel this away for now since we may call psbasemap during the movie-indicators below */ + if (GMT->current.map.frame.paint[GMT_Z]) { /* Must squirrel this away for now since we may call psbasemap during the movie-indicators below */ do_paint = true; - gmt_M_memcpy (&fill, &GMT->current.map.frame.fill, 1U, struct GMT_FILL); - GMT->current.map.frame.paint = false; /* Turn off for now */ + gmt_M_memcpy (&fill, &GMT->current.map.frame.fill[GMT_Z], 1U, struct GMT_FILL); + GMT->current.map.frame.paint[GMT_Z] = false; /* Turn off for now */ } PSL->internal.verbose = GMT->current.setting.verbose; /* Inherit verbosity level from GMT */ @@ -8024,21 +8111,21 @@ struct PSL_CTRL *gmt_plotinit (struct GMT_CTRL *GMT, struct GMT_OPTION *options) PSL_command (PSL, "}!\n"); } if (do_paint) { /* Reset any canvas coloring here */ - GMT->current.map.frame.paint = true; - gmt_M_memcpy (&GMT->current.map.frame.fill, &fill, 1U, struct GMT_FILL); + GMT->current.map.frame.paint[GMT_Z] = true; + gmt_M_memcpy (&GMT->current.map.frame.fill[GMT_Z], &fill, 1U, struct GMT_FILL); } return (PSL); } void gmt_plotcanvas (struct GMT_CTRL *GMT) { - if (GMT->current.map.frame.paint) { /* Paint the inside of the map with specified fill */ + if (GMT->current.map.frame.paint[GMT_Z]) { /* Paint the inside of the map (xy plane) with specified fill */ double *x = NULL, *y = NULL; uint64_t np; bool donut; - PSL_comment (GMT->PSL, "Fill the canvas %s\n", gmtlib_putfill (GMT, &GMT->current.map.frame.fill)); + PSL_comment (GMT->PSL, "Fill the canvas %s\n", gmtlib_putfill (GMT, &GMT->current.map.frame.fill[GMT_Z])); np = gmt_map_clip_path (GMT, &x, &y, &donut); - gmt_setfill (GMT, &GMT->current.map.frame.fill, 0); + gmt_setfill (GMT, &GMT->current.map.frame.fill[GMT_Z], 0); PSL_plotpolygon (GMT->PSL, x, y, (int)((1 + donut) * np)); gmt_M_free (GMT, x); gmt_M_free (GMT, y); diff --git a/src/gmt_project.h b/src/gmt_project.h index 0f09e0181c9..2a4c4ad4a84 100644 --- a/src/gmt_project.h +++ b/src/gmt_project.h @@ -140,6 +140,12 @@ enum GMT_enum_zdown {GMT_ZDOWN_R = 0, /* Default: Annotating radius */ GMT_ZDOWN_ZP = 2, /* Annotating planetary radius - r */ GMT_ZDOWN_ZR = 3}; /* Annotating given radius - r */ +/* For drawing the 3-D backboard */ +enum GMT_enum_3Dmode { + GMT_3D_NONE = 0, /* Default: No 3-D backboard */ + GMT_3D_WALL = 1, /* Draw the backboard */ + GMT_3D_BOX = 2}; /* Draw the backboard and the 3-D wire box */ + /* gmt_M_is_periodic means the east and west meridians of a global map are separated */ #define gmt_M_is_periodic(C) (gmt_M_is_cylindrical (C) || gmt_M_is_misc (C)) @@ -503,19 +509,21 @@ struct GMT_PLOT_AXIS { /* Information for one time axis */ struct GMT_PLOT_FRAME { /* Various parameters for plotting of time axis boundaries */ struct GMT_PLOT_AXIS axis[3]; /* One each for x, y, and z */ char header[GMT_LEN256]; /* Plot title */ - struct GMT_FILL fill; /* Fill for the basemap inside, if paint == true */ + struct GMT_FILL fill[3]; /* Fill for the basemap inside for planes x,y,z, if paint == true */ + struct GMT_PEN pen; /* Pen for the 3-D back wall outlines */ bool plotted_header; /* true if header has been plotted */ bool init; /* true if -B was used at all */ bool set; /* true if -B was used to set any increments */ bool draw; /* true if -B<int> was used, even -B0, as sign to draw axes */ bool drawz; /* true if -B<int> was used, even -Bz0, as sign to draw z axes */ - bool paint; /* true if -B +g<fill> was used */ - bool draw_box; /* true if a 3-D Z-box is desired */ + bool paint[3]; /* true if -B +x[<fill>], +y[<fill>], +g<fill> was used */ bool no_frame; /* true if we just want gridlines but no frame, i.e +n was used */ bool check_side; /* true if lon and lat annotations should be on x and y axis only */ bool primary; /* true if current axis is primary, false if secondary */ bool set_both; /* true if -B argument applies to both x and y axes */ bool obl_grid; /* true if +o was given to draw oblique gridlines */ + bool draw_wall; /* true if +w was given to draw backwall outline */ + unsigned int draw_box; /* 0 = no 3-D frame. 1 if 3-D Z-box is desired [default], 2 if no -Z box lines covering up the plot */ unsigned int internal_annot; /* 1 (longitude) or 2 (latitude or radius) if +i was given to draw internal annotations */ unsigned int set_frame[2]; /* 1 if a -B<WESNframe> setting was given */ unsigned int horizontal; /* 1 is S/N annotations should be parallel to axes, 2 if forced */ diff --git a/src/grdimage.c b/src/grdimage.c index 7bb1c7eb99f..2cc31908795 100644 --- a/src/grdimage.c +++ b/src/grdimage.c @@ -1260,9 +1260,9 @@ EXTERN_MSC int GMT_grdimage (void *V_API, int mode, void *args) { But what would it take to have a user selected bg color? */ double o, t; /* o - opacity, t = transparency */ o = Img_proj->data[node_RGBA] / 255.0; t = 1 - o; - rgb[0] = o * rgb[0] + t * GMT->current.map.frame.fill.rgb[0]; - rgb[1] = o * rgb[1] + t * GMT->current.map.frame.fill.rgb[1]; - rgb[2] = o * rgb[2] + t * GMT->current.map.frame.fill.rgb[2]; + rgb[0] = o * rgb[0] + t * GMT->current.map.frame.fill[GMT_Z].rgb[0]; + rgb[1] = o * rgb[1] + t * GMT->current.map.frame.fill[GMT_Z].rgb[1]; + rgb[2] = o * rgb[2] + t * GMT->current.map.frame.fill[GMT_Z].rgb[2]; node_RGBA++; } } diff --git a/src/psbasemap.c b/src/psbasemap.c index e0c7b670827..72335ffd077 100644 --- a/src/psbasemap.c +++ b/src/psbasemap.c @@ -192,8 +192,8 @@ static int parse (struct GMT_CTRL *GMT, struct PSBASEMAP_CTRL *Ctrl, struct GMT_ case 'G': /* Set canvas color */ if (gmt_M_compat_check (GMT, 4)) { GMT_Report (API, GMT_MSG_COMPAT, "Option -G is deprecated; -B...+g%s was set instead, use this in the future.\n", opt->arg); - GMT->current.map.frame.paint = true; - if (gmt_getfill (GMT, opt->arg, &GMT->current.map.frame.fill)) { + GMT->current.map.frame.paint[GMT_Z] = true; + if (gmt_getfill (GMT, opt->arg, &GMT->current.map.frame.fill[GMT_Z])) { gmt_fill_syntax (GMT, 'G', NULL, " "); n_errors++; } diff --git a/src/pscoast.c b/src/pscoast.c index 0557a777a0a..d99a50bedc1 100644 --- a/src/pscoast.c +++ b/src/pscoast.c @@ -149,8 +149,8 @@ static void *New_Ctrl (struct GMT_CTRL *GMT) { /* Allocate and initialize a new C->A.info.high = GSHHS_MAX_LEVEL; /* Include all GSHHS levels */ C->D.set = 'l'; /* Low-resolution coastline data */ - if (GMT->current.map.frame.paint) /* Default Ocean color = Frame background color */ - C->S.fill = GMT->current.map.frame.fill; + if (GMT->current.map.frame.paint[GMT_Z]) /* Default Ocean color = Frame background color */ + C->S.fill = GMT->current.map.frame.fill[GMT_Z]; else gmt_init_fill (GMT, &C->S.fill, 1.0, 1.0, 1.0); /* Default Ocean color = white */ C->C.fill[LAKE] = C->C.fill[RIVER] = C->S.fill; /* Default Lake/Riverlake color = Ocean color */ @@ -932,8 +932,8 @@ EXTERN_MSC int GMT_pscoast (void *V_API, int mode, void *args) { clobber_background = true; recursive = false; if (!Ctrl->S.active) { /* Since we are painting wet areas we must now reset them to white */ - if (GMT->current.map.frame.paint) /* Let ocean color match cancas fill color */ - fill[0] = GMT->current.map.frame.fill; + if (GMT->current.map.frame.paint[GMT_Z]) /* Let ocean color match cancas fill color */ + fill[0] = GMT->current.map.frame.fill[GMT_Z]; else gmt_init_fill (GMT, &fill[0], 1.0, 1.0, 1.0); /* Default Ocean color = white */ } diff --git a/src/psrose.c b/src/psrose.c index 56b1d305948..0b220dcb5bf 100644 --- a/src/psrose.c +++ b/src/psrose.c @@ -743,8 +743,8 @@ EXTERN_MSC int GMT_psrose (void *V_API, int mode, void *args) { Return (GMT_PROJECTION_ERROR); } - if (GMT->current.map.frame.paint) { /* Until psrose uses a polar projection we must bypass the basemap fill and do it ourself here */ - GMT->current.map.frame.paint = false; /* Turn off so gmt_plotinit won't fill */ + if (GMT->current.map.frame.paint[GMT_Z]) { /* Until psrose uses a polar projection we must bypass the basemap fill and do it ourself here */ + GMT->current.map.frame.paint[GMT_Z] = false; /* Turn off so gmt_plotinit won't fill */ do_fill = true; } if ((PSL = gmt_plotinit (GMT, options)) == NULL) Return (GMT_RUNTIME_ERROR); @@ -779,14 +779,14 @@ EXTERN_MSC int GMT_psrose (void *V_API, int mode, void *args) { if (do_fill) { /* Until psrose uses a polar projection we must bypass the basemap fill and do it ourself here */ double dim = 2.0 * Ctrl->S.scale; - GMT->current.map.frame.paint = true; /* Restore original setting */ + GMT->current.map.frame.paint[GMT_Z] = true; /* Restore original setting */ if (half_only) { /* Clip the bottom half of the circle */ double xc[4], yc[4]; xc[0] = xc[3] = -Ctrl->S.scale; xc[1] = xc[2] = Ctrl->S.scale; yc[0] = yc[1] = 0.0; yc[2] = yc[3] = Ctrl->S.scale; PSL_beginclipping (PSL, xc, yc, 4, GMT->session.no_rgb, 3); } - gmt_setfill (GMT, &GMT->current.map.frame.fill, 0); + gmt_setfill (GMT, &GMT->current.map.frame.fill[GMT_Z], 0); PSL_plotsymbol (PSL, 0.0, 0.0, &dim, PSL_CIRCLE); if (half_only) PSL_endclipping (PSL, 1); /* Reduce polygon clipping by one level */ } diff --git a/src/psternary.c b/src/psternary.c index 1201d7fbe92..635d56335b6 100644 --- a/src/psternary.c +++ b/src/psternary.c @@ -426,10 +426,10 @@ EXTERN_MSC int GMT_psternary (void *V_API, int mode, void *args) { * axis arguments. We also must handle the canvas filling separately. The three axis are 60 degrees * relative to each other and we do this directly with PSL calls. */ - if (GMT->current.map.frame.paint) { /* Paint the inside of the map with specified fill */ - gmt_setfill (GMT, &GMT->current.map.frame.fill, 0); + if (GMT->current.map.frame.paint[GMT_Z]) { /* Paint the inside of the map with specified fill */ + gmt_setfill (GMT, &GMT->current.map.frame.fill[GMT_Z], 0); PSL_plotpolygon (PSL, tri_x, tri_y, 4); - GMT->current.map.frame.paint = false; + GMT->current.map.frame.paint[GMT_Z] = false; } /* Count how many of the three sides will be drawn */ if (GMT->current.map.frame.side[S_SIDE]) n_sides++; /* The bottom (a) side */