Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion doc/rst/source/text.rst
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,14 @@ Optional Arguments
columns, one offset pair per text record, in the same order as the input
data. Values are in the current length unit [:term:`PROJ_LENGTH_UNIT`].
- **+v** - Draw a line from the original point to the shifted point;
append a *pen* to change the attributes for this line. **Note**:
append a *pen* to change the attributes for this line. To place an arrow
head at the original point, add the pen modifier **+vb**\ *vspecs* (e.g.,
**-D**\ 1c+v1p,red+vb0.3c+gred); a head at the text end is not supported.
Add the pen modifier **+o**\ *offset*, given *before* **+vb**\ (e.g., **-D**\ 1c+v1p,red+o0.1c+vb0.3c+gred),
to stop the line, and its head, that distance short of the point, e.g., to clear a symbol drawn there;
the offset is as for :ref:`line attributes <reference/features:Specifying line attributes>`
in :doc:`plot`. Only the data-point end is affected.
See :ref:`Technical reference - Drawing of Vectors<reference/features:The Drawing of Vectors>`. **Note**:
The **-Dj**\|\ **J** selection cannot be used with paragraph mode (|-M|).

.. _-F:
Expand Down
47 changes: 47 additions & 0 deletions src/gmt_plot.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
* gmt_map_text
* gmt_map_title
* gmt_plane_perspective
* gmt_plot_end_vectors
* gmt_plot_geo_ellipse
* gmt_plot_grid_graticules
* gmt_plot_line
Expand Down Expand Up @@ -6186,6 +6187,52 @@ GMT_LOCAL bool gmtplot_these_are_duplicates (double x0, double y0, double x1, do
return true;
}

void gmt_plot_end_vectors (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, struct GMT_PEN *P) {
/* Maybe add vector heads. Here, x,y are in inches on the plot */
unsigned int k, current[2] = {0,0}, next[2] = {1,0};
double dim[PSL_MAX_DIMS], angle, s, c, L;
char *end[2] = {"start", "end"};
if (n < 2) return; /* No line of mine */
if (P->end[0].V == NULL && P->end[1].V == NULL) return; /* No arrow heads requested */
current[1] = (unsigned int)n-1; next[1] = (unsigned int)n-2;
PSL_command (GMT->PSL, "V\n");
GMT->PSL->current.linewidth = -1.0; /* Will be changed by next PSL_setlinewidth */
gmt_M_memset (dim, PSL_MAX_DIMS, double);
for (k = 0; k < 2; k++) {
if (P->end[k].V == NULL) continue;
P->end[k].V->v.status |= PSL_VEC_LINE; /* Flag so head is not skipped due to "short" vector */
/* Add vector heads to this end */
PSL_comment (GMT->PSL, "Add vector head to %s of line\n", end[k]);
angle = d_atan2d (y[current[k]] - y[next[k]], x[current[k]] - x[next[k]]);
sincosd (angle, &s, &c);
L = (P->end[k].V->v.v_kind[1] == PSL_VEC_TERMINAL) ? 1e-3 : P->end[k].length;
P->end[k].V->v.v_width = (float)(P->end[k].V->v.pen.width * GMT->session.u2u[GMT_PT][GMT_INCH]); /* Set symbol pen width */
dim[PSL_VEC_XTIP] = x[current[k]] + c * L;
dim[PSL_VEC_YTIP] = y[current[k]] + s * L;
dim[PSL_VEC_TAIL_WIDTH] = P->end[k].V->v.v_width;
dim[PSL_VEC_HEAD_LENGTH] = P->end[k].V->v.h_length;
dim[PSL_VEC_HEAD_WIDTH] = P->end[k].V->v.h_width;
dim[PSL_VEC_HEAD_SHAPE] = P->end[k].V->v.v_shape;
dim[PSL_VEC_STATUS] = (double)P->end[k].V->v.status;
dim[PSL_VEC_HEAD_TYPE_BEGIN] = (double)P->end[k].V->v.v_kind[0];
dim[PSL_VEC_HEAD_TYPE_END] = (double)P->end[k].V->v.v_kind[1];
dim[PSL_VEC_TRIM_BEGIN] = (double)P->end[k].V->v.v_trim[0];
dim[PSL_VEC_TRIM_END] = (double)P->end[k].V->v.v_trim[1];
if (P->end[k].V->v.status & PSL_VEC_OUTLINE2) { /* Gave specific head outline pen */
PSL_defpen (GMT->PSL, "PSL_vecheadpen", P->end[k].V->v.pen.width, P->end[k].V->v.pen.style, P->end[k].V->v.pen.offset, P->end[k].V->v.pen.rgb);
dim[PSL_VEC_HEAD_PENWIDTH] = P->end[k].V->v.pen.width;
}
else { /* Set default based on line pen */
PSL_defpen (GMT->PSL, "PSL_vecheadpen", 0.5 * P->width, P->style, P->offset, P->rgb);
dim[PSL_VEC_HEAD_PENWIDTH] = 0.5 * P->width;
}
gmt_setfill (GMT, &P->end[k].V->v.fill, (P->end[k].V->v.status & PSL_VEC_OUTLINE2) == PSL_VEC_OUTLINE2);
PSL_plotsymbol (GMT->PSL, x[current[k]], y[current[k]], dim, PSL_VECTOR);
}
GMT->PSL->current.linewidth = -1.0; /* Will be changed by next PSL_setlinewidth */
PSL_command (GMT->PSL, "U\n");
}

void gmt_plot_line (struct GMT_CTRL *GMT, double *x, double *y, unsigned int *pen, uint64_t n, unsigned int mode) {
/* Mode = PSL_LINEAR [0] or PSL_BEZIER [1] */
/* Drawing lines of finite thickness that cross map boundaries is tricky because the exit or entry point
Expand Down
1 change: 1 addition & 0 deletions src/gmt_prototypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ EXTERN_MSC void gmt_set_basemap_orders (struct GMT_CTRL *GMT, unsigned int frame
EXTERN_MSC void gmt_map_clip_off (struct GMT_CTRL *GMT);
EXTERN_MSC void gmt_map_clip_on (struct GMT_CTRL *GMT, double rgb[], unsigned int flag);
EXTERN_MSC void gmt_BB_clip_on (struct GMT_CTRL *GMT, double rgb[], unsigned int flag);
EXTERN_MSC void gmt_plot_end_vectors (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, struct GMT_PEN *P);
EXTERN_MSC void gmt_plot_line (struct GMT_CTRL *GMT, double *x, double *y, unsigned int *pen, uint64_t n, unsigned int mode);
EXTERN_MSC void gmt_setpen (struct GMT_CTRL *GMT, struct GMT_PEN *pen);
EXTERN_MSC void gmt_setfill (struct GMT_CTRL *GMT, struct GMT_FILL *fill, int outline);
Expand Down
63 changes: 51 additions & 12 deletions src/pstext.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ static void *New_Ctrl (struct GMT_CTRL *GMT) { /* Allocate and initialize a new
/* Initialize values whose defaults are not 0/false/NULL */

C->D.pen = C->W.pen = GMT->current.setting.map_default_pen;
gmt_M_memset (C->D.pen.end, 2, struct GMT_LINE_END); /* Leader ignores the default pen trims and heads (and must not free its heads) */
C->C.dx = C->C.dy = GMT_TEXT_CLEARANCE; /* 15% of font size is default clearance */
C->C.percent = true;
C->C.mode = 'o'; /* Rectangular box shape */
Expand All @@ -163,10 +164,51 @@ static void *New_Ctrl (struct GMT_CTRL *GMT) { /* Allocate and initialize a new
static void Free_Ctrl (struct GMT_CTRL *GMT, struct PSTEXT_CTRL *C) { /* Deallocate control structure */
if (!C) return;
gmt_M_str_free (C->D.file);
gmt_freepen (GMT, &C->D.pen);
gmt_M_str_free (C->F.text);
gmt_M_free (GMT, C);
}

#define PSTEXT_LEADER_NP 101 /* Number of vertices used to trim the leader */

GMT_LOCAL bool pstext_trim_leader (struct GMT_CTRL *GMT, double *x, double *y, struct GMT_PEN *pen) {
/* Trim the data-point end of the leader (x[0],y[0]) -> (x[1],y[1]), in inches, with the pen's +o and +v via gmt_trim_line as plot does.
* The text end is never trimmed since the box clipping is relative to it. Returns true if nothing is left to draw. */
uint64_t n = PSTEXT_LEADER_NP;
double *xx = gmt_M_memory (GMT, NULL, n, double), *yy = gmt_M_memory (GMT, NULL, n, double), xe, ye;
struct GMT_PEN P = *pen; /* Shallow copy so we can skip the text end */

P.end[1].offset = 0.0;
for (uint64_t k = 0; k < n; k++) { /* gmt_trim_line needs vertices to trim along, and treats reaching the last one as trimmed away */
double f = (double)k / (double)(n - 1);
xx[k] = x[0] + f * (x[1] - x[0]); yy[k] = y[0] + f * (y[1] - y[0]);
}
for (uint64_t k = 0; k < n; k++) { /* gmt_trim_line works in input coordinates */
gmt_xy_to_geo (GMT, &xx[k], &yy[k], xx[k], yy[k]);
if (gmt_M_is_dnan (xx[k]) || gmt_M_is_dnan (yy[k])) { /* Not invertible (e.g., corner outside the map domain): draw untrimmed */
gmt_M_free (GMT, xx); gmt_M_free (GMT, yy);
return false;
}
}
if (gmt_trim_line (GMT, &xx, &yy, &n, &P)) { /* Trimmed away completely */
gmt_M_free (GMT, xx); gmt_M_free (GMT, yy);
return true;
}
gmt_geo_to_xy (GMT, xx[0], yy[0], &xe, &ye); /* New start of the leader */
x[0] = xe; y[0] = ye;
gmt_M_free (GMT, xx); gmt_M_free (GMT, yy);
return false;
}

GMT_LOCAL void pstext_draw_leader (struct GMT_CTRL *GMT, struct PSTEXT_INFO *T, double *xx, double *yy, char *text, double offset[]) {
/* Draw leader line from the data point (xx[0],yy[0]) to the text at (xx[1],yy[1]), with optional +o trim and vector head at the data point */
double x[2] = {xx[0], xx[1]}, y[2] = {yy[0], yy[1]};
if (gmt_trim_requested (GMT, &T->vecpen) && pstext_trim_leader (GMT, x, y, &T->vecpen)) return;
gmt_setpen (GMT, &T->vecpen);
PSL_plotline_clipped_by_textbox (GMT->PSL, x[0], y[0], x[1], y[1], T->font.size, text, T->paragraph_angle, T->block_justify, offset);
gmt_plot_end_vectors (GMT, x, y, 2, &T->vecpen);
}

GMT_LOCAL void pstext_output_words (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double x, double y, char *text, struct PSTEXT_INFO *T, struct PSTEXT_CTRL *Ctrl) {
double offset[2];

Expand All @@ -186,8 +228,8 @@ GMT_LOCAL void pstext_output_words (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL,
PSL_setfont (PSL, T->font.id);

if (T->boxflag & 32) { /* Need to draw a vector from (x,y) to the offset text */
gmt_setpen (GMT, &(T->vecpen));
PSL_plotsegment (PSL, x, y, x + T->x_offset, y + T->y_offset);
double xx[2] = {x, x + T->x_offset}, yy[2] = {y, y + T->y_offset};
pstext_draw_leader (GMT, T, xx, yy, NULL, offset);
}
if (Ctrl->D.justify) /* Smart offset according to justification (from Dave Huang) */
gmt_smart_justify (GMT, T->block_justify, T->paragraph_angle, T->x_offset, T->y_offset, &x, &y, Ctrl->D.justify);
Expand All @@ -207,7 +249,7 @@ GMT_LOCAL void pstext_output_words (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL,
}
if (T->boxflag & 1) gmt_setpen (GMT, &(T->boxpen)); /* Change current pen */
if (T->boxflag & 2) fill = &(T->boxfill); /* Determine if fill or not */
if (T->boxflag & 3) gmt_setfill (GMT, fill, T->boxflag & 1); /* Change current fill and/or outline */
gmt_setfill (GMT, fill, T->boxflag & 1); /* Change current fill and/or outline (none if only a leader line) */
/* Compute text box, draw/fill it, and in the process store the text in the PS file for next command */
PSL_plotparagraphbox (PSL, x, y, T->font.size, text, T->paragraph_angle, T->block_justify, offset, mode);
/* Passing NULL means we typeset using the last stored paragraph info */
Expand Down Expand Up @@ -357,7 +399,7 @@ static int usage (struct GMTAPI_CTRL *API, int level) {
"Use -Dj to move text origin away from point (direction determined by text's justification). "
"Upper case -DJ will shorten diagonal shifts at corners by sqrt(2). Cannot be used with -M. Optional modifiers:");
GMT_Usage (API, 3, "+f: Read per-record offsets (dx dy) from <file> (two columns in current distance units); overrides fixed <dx>/<dy>.");
GMT_Usage (API, 3, "+v: Draw line from text to original point; optionally append a <pen> [%s].", gmt_putpen (API->GMT, &API->GMT->current.setting.map_default_pen));
GMT_Usage (API, 3, "+v: Draw line from text to original point; optionally append a <pen> [%s]. Add pen modifier +vb<specs> for a vector head at the point, and, before +vb, +o<offset> (as for plot -W) to stop the line short of it.", gmt_putpen (API->GMT, &API->GMT->current.setting.map_default_pen));
GMT_Usage (API, 1, "\n-F[+a[<angle>]][+c[<justify>]][+f[<font>]][+h|l|r[<first>]|+t<text>|+z[<fmt>]][+j[<justify>]]");
GMT_Usage (API, -2, "Specify values for text attributes that apply to all text records:");
GMT_Usage (API, 3, "+a Specify baseline <angle> for all text [0].");
Expand Down Expand Up @@ -482,6 +524,7 @@ static int parse (struct GMT_CTRL *GMT, struct PSTEXT_CTRL *Ctrl, struct GMT_OPT
if (opt->arg[j] == 'v') { /* Want to draw a line from point to offset point */
Ctrl->D.line = true;
n_errors += gmt_M_check_condition(GMT, opt->arg[j+1] && gmt_getpen (GMT, &opt->arg[j+1], &Ctrl->D.pen), "Option -D: Give pen after +v\n");
n_errors += gmt_M_check_condition(GMT, Ctrl->D.pen.end[1].V != NULL, "Option -D: A vector head can only be placed at the data point end of the line; use pen modifier +vb<specs>\n");
if (opt->arg[j-1] == '+') /* New-style syntax */
opt->arg[j-1] = 0;
else
Expand Down Expand Up @@ -1006,10 +1049,8 @@ EXTERN_MSC int GMT_pstext (void *V_API, int mode, void *args) {
offset[0] = T.x_space;
offset[1] = T.y_space;
}
if (T.boxflag & 32) { /* Draw line from original point to shifted location, clipped at textbox edge */
gmt_setpen(GMT, &T.vecpen);
PSL_plotline_clipped_by_textbox(PSL, xx[0], yy[0], xx[1], yy[1], T.font.size, use_text, T.paragraph_angle, T.block_justify, offset);
}
if (T.boxflag & 32) /* Draw line from original point to shifted location, clipped at textbox edge */
pstext_draw_leader (GMT, &T, xx, yy, use_text, offset);
if (!Ctrl->G.mode && T.boxflag & 3) { /* Plot the box beneath the text */
if (Ctrl->S.active) { /* Lay down shaded box first */
PSL_setfill (PSL, Ctrl->S.fill.rgb, 0); /* shade color */
Expand Down Expand Up @@ -1485,10 +1526,8 @@ EXTERN_MSC int GMT_pstext (void *V_API, int mode, void *args) {
offset[0] = T.x_space;
offset[1] = T.y_space;
}
if (T.boxflag & 32) { /* Draw line from original point to shifted location, clipped at textbox edge */
gmt_setpen (GMT, &T.vecpen);
PSL_plotline_clipped_by_textbox(PSL, xx[0], yy[0], xx[1], yy[1], T.font.size, use_text, T.paragraph_angle, T.block_justify, offset);
}
if (T.boxflag & 32) /* Draw line from original point to shifted location, clipped at textbox edge */
pstext_draw_leader (GMT, &T, xx, yy, use_text, offset);
if (!Ctrl->G.mode && T.boxflag & 3) { /* Plot the box beneath the text */
if (Ctrl->S.active) { /* Lay down shaded box first */
PSL_setfill (PSL, Ctrl->S.fill.rgb, 0); /* shade color */
Expand Down
48 changes: 1 addition & 47 deletions src/psxy.c
Original file line number Diff line number Diff line change
Expand Up @@ -470,52 +470,6 @@ GMT_LOCAL int psxy_plot_decorations (struct GMT_CTRL *GMT, struct GMT_DATASET *D
return GMT_NOERROR;
}

GMT_LOCAL void psxy_plot_end_vectors (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, struct GMT_PEN *P) {
/* Maybe add vector heads. Here, x,y are in inches on the plot */
unsigned int k, current[2] = {0,0}, next[2] = {1,0};
double dim[PSL_MAX_DIMS], angle, s, c, L;
char *end[2] = {"start", "end"};
if (n < 2) return; /* No line of mine */
if (P->end[0].V == NULL && P->end[1].V == NULL) return; /* No arrow heads requested */
current[1] = (unsigned int)n-1; next[1] = (unsigned int)n-2;
PSL_command (GMT->PSL, "V\n");
GMT->PSL->current.linewidth = -1.0; /* Will be changed by next PSL_setlinewidth */
gmt_M_memset (dim, PSL_MAX_DIMS, double);
for (k = 0; k < 2; k++) {
if (P->end[k].V == NULL) continue;
P->end[k].V->v.status |= PSL_VEC_LINE; /* Flag so head is not skipped due to "short" vector */
/* Add vector heads to this end */
PSL_comment (GMT->PSL, "Add vector head to %s of line\n", end[k]);
angle = d_atan2d (y[current[k]] - y[next[k]], x[current[k]] - x[next[k]]);
sincosd (angle, &s, &c);
L = (P->end[k].V->v.v_kind[1] == PSL_VEC_TERMINAL) ? 1e-3 : P->end[k].length;
P->end[k].V->v.v_width = (float)(P->end[k].V->v.pen.width * GMT->session.u2u[GMT_PT][GMT_INCH]); /* Set symbol pen width */
dim[PSL_VEC_XTIP] = x[current[k]] + c * L;
dim[PSL_VEC_YTIP] = y[current[k]] + s * L;
dim[PSL_VEC_TAIL_WIDTH] = P->end[k].V->v.v_width;
dim[PSL_VEC_HEAD_LENGTH] = P->end[k].V->v.h_length;
dim[PSL_VEC_HEAD_WIDTH] = P->end[k].V->v.h_width;
dim[PSL_VEC_HEAD_SHAPE] = P->end[k].V->v.v_shape;
dim[PSL_VEC_STATUS] = (double)P->end[k].V->v.status;
dim[PSL_VEC_HEAD_TYPE_BEGIN] = (double)P->end[k].V->v.v_kind[0];
dim[PSL_VEC_HEAD_TYPE_END] = (double)P->end[k].V->v.v_kind[1];
dim[PSL_VEC_TRIM_BEGIN] = (double)P->end[k].V->v.v_trim[0];
dim[PSL_VEC_TRIM_END] = (double)P->end[k].V->v.v_trim[1];
if (P->end[k].V->v.status & PSL_VEC_OUTLINE2) { /* Gave specific head outline pen */
PSL_defpen (GMT->PSL, "PSL_vecheadpen", P->end[k].V->v.pen.width, P->end[k].V->v.pen.style, P->end[k].V->v.pen.offset, P->end[k].V->v.pen.rgb);
dim[PSL_VEC_HEAD_PENWIDTH] = P->end[k].V->v.pen.width;
}
else { /* Set default based on line pen */
PSL_defpen (GMT->PSL, "PSL_vecheadpen", 0.5 * P->width, P->style, P->offset, P->rgb);
dim[PSL_VEC_HEAD_PENWIDTH] = 0.5 * P->width;
}
gmt_setfill (GMT, &P->end[k].V->v.fill, (P->end[k].V->v.status & PSL_VEC_OUTLINE2) == PSL_VEC_OUTLINE2);
PSL_plotsymbol (GMT->PSL, x[current[k]], y[current[k]], dim, PSL_VECTOR);
}
GMT->PSL->current.linewidth = -1.0; /* Will be changed by next PSL_setlinewidth */
PSL_command (GMT->PSL, "U\n");
}

GMT_LOCAL bool psxy_is_stroke_symbol (int symbol) {
/* Return true if cross, x, y, - symbols */
if (symbol == PSL_CROSS) return true;
Expand Down Expand Up @@ -3260,7 +3214,7 @@ EXTERN_MSC int GMT_psxy (void *V_API, int mode, void *args) {
else if (draw_line) {
if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, L->data[GMT_X], L->data[GMT_Y], L->n_rows)) == 0) continue;
if (S.symbol != GMT_SYMBOL_FRONT || !S.f.invisible) gmt_plot_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, current_pen.mode);
psxy_plot_end_vectors (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.n, &current_pen); /* Maybe add vector heads */
gmt_plot_end_vectors (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.n, &current_pen); /* Maybe add vector heads */
}
}
if (S.symbol == GMT_SYMBOL_FRONT) { /* Must also draw fault crossbars */
Expand Down
Loading