diff --git a/doc/rst/source/explain_line_draw.rst_ b/doc/rst/source/explain_line_draw.rst_ index 4d31a8ac0d8..0a507c034c5 100644 --- a/doc/rst/source/explain_line_draw.rst_ +++ b/doc/rst/source/explain_line_draw.rst_ @@ -13,4 +13,10 @@ - For gragraphic projections, *x* and *y* are parallels and meridians. - For polar projections, *x* and *y* are theta and radius. + When used together with |-S|\ **v** (Cartesian vectors), the **x** and **y** + directives produce *broken* (right-angle elbow) arrows: instead of a straight line + from tail to tip, the vector is drawn along two perpendicular legs meeting at a + corner. **x** draws the horizontal leg first, then the vertical leg; **y** draws + the vertical leg first, then the horizontal leg. The arrowhead is placed at the tip. + **Note**: In :doc:`plot3d`, the |-A| option requires constant *z*-coordinates. diff --git a/doc/rst/source/plot.rst b/doc/rst/source/plot.rst index 0f0ce787ff3..9fd53a7f5a4 100644 --- a/doc/rst/source/plot.rst +++ b/doc/rst/source/plot.rst @@ -468,6 +468,14 @@ to cm given the scale 3.60:: gmt plot -R20/40/-20/0 -JM6i -Sv0.15i+e+z3.6c -Gred -W0.25p -Baf data.txt -pdf map +To draw the same Cartesian vectors as broken (right-angle elbow) arrows, traveling +horizontally first and then vertically to reach the tip, use |-A|\ **x**:: + + gmt plot -R0/50/-50/50 -JX6i -Sv0.15i+e -Gred -W0.5p -Baf -Ax -pdf map << EOF + 10 10 45 2i + 30 -20 0 1.5i + EOF + To plot a triangle with smooth color gradients using RGB values directly:: cat << EOF > triangle.txt diff --git a/src/psxy.c b/src/psxy.c index e573a8adbee..ce4043b65db 100644 --- a/src/psxy.c +++ b/src/psxy.c @@ -2279,11 +2279,11 @@ EXTERN_MSC int GMT_psxy (void *V_API, int mode, void *args) { v4_rgb = current_fill.rgb; else v4_rgb = GMT->session.no_rgb; - if (v4_outline) gmt_setpen (GMT, &Ctrl->W.pen); + if (v4_outline) gmt_setpen(GMT, &Ctrl->W.pen); if (S.v.status & PSL_VEC_BEGIN) v4_outline += 8; /* Double-headed */ dim[PSL_VEC_HEAD_SHAPE] = GMT->current.setting.map_vector_shape; dim[PSL_VEC_HEAD_WIDTH] *= 0.5; /* Since it was double in the parsing */ - psl_vector_v4 (PSL, xpos[item], plot_y, dim, v4_rgb, v4_outline); + psl_vector_v4(PSL, xpos[item], plot_y, dim, v4_rgb, v4_outline); } else { /* Current vector model */ dim[PSL_VEC_HEAD_SHAPE] = S.v.v_shape; @@ -2293,7 +2293,51 @@ EXTERN_MSC int GMT_psxy (void *V_API, int mode, void *args) { dim[PSL_VEC_TRIM_BEGIN] = (double)S.v.v_trim[0]; dim[PSL_VEC_TRIM_END] = (double)S.v.v_trim[1]; dim[PSL_VEC_HEAD_PENWIDTH] = s * headpen_width; /* Possibly shrunk head pen width */ - PSL_plotsymbol (PSL, xpos[item], plot_y, dim, PSL_VECTOR); + if (Ctrl->A.active && (Ctrl->A.mode == GMT_STAIRS_X || Ctrl->A.mode == GMT_STAIRS_Y)) { + /* Broken (right-angle elbow) arrow: two legs meeting at a corner point. + * Round line caps are used so each leg's semicircular endpoint fills the + * outer-corner gap that butt caps would leave at the elbow. */ + double elbow_x, elbow_y, leg1_length, leg2_length, s1, s2; + unsigned int leg1_status, leg2_status; + if (Ctrl->A.mode == GMT_STAIRS_X) { /* Horizontal first, then vertical */ + elbow_x = x_2; elbow_y = plot_y; + } + else { /* GMT_STAIRS_Y: vertical first, then horizontal */ + elbow_x = xpos[item]; elbow_y = y_2; + } + leg1_length = hypot(elbow_x - xpos[item], elbow_y - plot_y); + leg2_length = hypot(x_2 - elbow_x, y_2 - elbow_y); + s1 = gmt_get_vector_shrinking(GMT, &(S.v), data_magnitude, leg1_length); + s2 = gmt_get_vector_shrinking(GMT, &(S.v), data_magnitude, leg2_length); + PSL_setlinecap(PSL, PSL_ROUND_CAP); /* Round ends fill the elbow corner */ + /* Leg 1: tail → elbow. Keep begin-head (if any); no end-head. */ + leg1_status = S.v.status & ~(PSL_VEC_END | PSL_VEC_END_L | PSL_VEC_END_R | PSL_VEC_OFF_END); + dim[PSL_VEC_XTIP] = elbow_x; + dim[PSL_VEC_YTIP] = elbow_y; + dim[PSL_VEC_TAIL_WIDTH] = s1 * S.v.v_width; + dim[PSL_VEC_HEAD_LENGTH] = s1 * S.v.h_length; + dim[PSL_VEC_HEAD_WIDTH] = s1 * S.v.h_width; + dim[PSL_VEC_HEAD_PENWIDTH] = s1 * headpen_width; + dim[PSL_VEC_TRIM_BEGIN] = (double)S.v.v_trim[0]; + dim[PSL_VEC_TRIM_END] = 0.0; + dim[PSL_VEC_STATUS] = (double)leg1_status; + PSL_plotsymbol(PSL, xpos[item], plot_y, dim, PSL_VECTOR); + /* Leg 2: elbow → tip. Keep end-head (if any); no begin-head. */ + leg2_status = S.v.status & ~(PSL_VEC_BEGIN | PSL_VEC_BEGIN_L | PSL_VEC_BEGIN_R | PSL_VEC_OFF_BEGIN); + dim[PSL_VEC_XTIP] = x_2; + dim[PSL_VEC_YTIP] = y_2; + dim[PSL_VEC_TAIL_WIDTH] = s2 * S.v.v_width; + dim[PSL_VEC_HEAD_LENGTH] = s2 * S.v.h_length; + dim[PSL_VEC_HEAD_WIDTH] = s2 * S.v.h_width; + dim[PSL_VEC_HEAD_PENWIDTH] = s2 * headpen_width; + dim[PSL_VEC_TRIM_BEGIN] = 0.0; + dim[PSL_VEC_TRIM_END] = (double)S.v.v_trim[1]; + dim[PSL_VEC_STATUS] = (double)leg2_status; + PSL_plotsymbol(PSL, elbow_x, elbow_y, dim, PSL_VECTOR); + PSL_setlinecap(PSL, PSL_BUTT_CAP); /* Restore default */ + } + else + PSL_plotsymbol(PSL, xpos[item], plot_y, dim, PSL_VECTOR); } break; case GMT_SYMBOL_GEOVECTOR: diff --git a/test/baseline/psxy.dvc b/test/baseline/psxy.dvc index 3fdd3c0e8c4..8c3b55c999a 100644 --- a/test/baseline/psxy.dvc +++ b/test/baseline/psxy.dvc @@ -1,6 +1,6 @@ outs: -- md5: 887b912ced405376a548ce0a575b8c18.dir - nfiles: 144 +- md5: 510204715a512de392ee93ea016a172f.dir + nfiles: 145 path: psxy hash: md5 - size: 9428273 + size: 9459764 diff --git a/test/psxy/elbowvec.sh b/test/psxy/elbowvec.sh new file mode 100755 index 00000000000..6906798f560 --- /dev/null +++ b/test/psxy/elbowvec.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +# Test broken/elbow arrows: -Sv combined with -Ax (horizontal-first) or -Ay (vertical-first). +# Three arrows 120 degrees apart (30/150/330 deg Cartesian CCW-from-E). +# Tails at three corners, tips all near centre, so the L-legs are clearly visible +# and non-overlapping in both -Ax and -Ay modes. +# +# With -JX3i/-R-3/3/-3/3: scale = 0.5 in/unit, 1.5i = 3 data units. +# Arrow 1: tail(-2,-2) angle=30 tip( 0.60,-0.50) -Ax elbow at( 0.60,-2) +# Arrow 2: tail( 2, 0) angle=150 tip(-0.60, 1.50) -Ax elbow at(-0.60, 0) +# Arrow 3: tail( 0, 2) angle=330 tip( 2.60, 0.50) -Ax elbow at( 2.60, 2) +ps=elbowvec.ps + +cat << EOF > elbowvec.txt +-2 -2 30 1.5i + 2 0 150 1.5i + 0 2 330 1.5i +EOF + +R=-R-3/3/-3/3 +J=-JX3i + +# Left panel: horizontal-first elbow (-Ax) +gmt psxy elbowvec.txt $R $J -P -Bafg1 -BWSne+t"Ax" \ + -Sv0.2i+e -Gred -W1p -Ax -K -X1.25i -Y4.5i > $ps + +# Right panel: vertical-first elbow (-Ay) +gmt psxy elbowvec.txt $R $J -O -Bafg1 -BWSne+t"Ay" \ + -Sv0.2i+e -Gred -W1p -Ay -X3.75i >> $ps