Icons are changed
[gnuplot] / src / gadgets.c
1 #ifndef lint
2 static char *RCSid() { return RCSid("gadgets.c,v 1.1.3.1 2000/05/03 21:47:15 hbb Exp"); }
3 #endif
4
5 /* GNUPLOT - gadgets.c */
6
7 /*[
8  * Copyright 2000, 2004   Thomas Williams, Colin Kelley
9  *
10  * Permission to use, copy, and distribute this software and its
11  * documentation for any purpose with or without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and
13  * that both that copyright notice and this permission notice appear
14  * in supporting documentation.
15  *
16  * Permission to modify the software is granted, but not the right to
17  * distribute the complete modified source code.  Modifications are to
18  * be distributed as patches to the released version.  Permission to
19  * distribute binaries produced by compiling modified sources is granted,
20  * provided you
21  *   1. distribute the corresponding source modifications from the
22  *    released version in the form of a patch file along with the binaries,
23  *   2. add special version identification to distinguish your version
24  *    in addition to the base release version number,
25  *   3. provide your name and address as the primary contact for the
26  *    support of your modified version, and
27  *   4. retain our contact information in regard to use of the base
28  *    software.
29  * Permission to distribute the released version of the source code along
30  * with corresponding source modifications in the form of a patch file is
31  * granted with same provisions 2 through 4 for binary distributions.
32  *
33  * This software is provided "as is" without express or implied warranty
34  * to the extent permitted by applicable law.
35 ]*/
36
37 #include "gadgets.h"
38 #include "command.h"
39 #include "graph3d.h" /* for map3d_position_r() */
40 #include "graphics.h"
41 #include "plot3d.h" /* For is_plot_with_palette() */
42
43 #include "pm3d.h"
44
45 /* This file contains mainly a collection of global variables that
46  * used to be in 'set.c', where they didn't really belong. They
47  * describe the status of several parts of the gnuplot plotting engine
48  * that are used by both 2D and 3D plots, and thus belong neither to
49  * graphics.c nor graph3d.c, alone. This is not a very clean solution,
50  * but better than mixing internal status and the user interface as we
51  * used to have it, in set.c and setshow.h */
52
53 legend_key keyT = DEFAULT_KEY_PROPS;
54
55 /* Description of the color box associated with CB_AXIS */
56 color_box_struct color_box; /* initialized in init_color() */
57 color_box_struct default_color_box = {SMCOLOR_BOX_DEFAULT, 'v', 1, LT_BLACK, LAYER_FRONT,
58                                         {screen, screen, screen, 0.90, 0.2, 0.0},
59                                         {screen, screen, screen, 0.05, 0.6, 0.0}};
60
61 /* The graph box, in terminal coordinates, as calculated by boundary()
62  * or boundary3d(): */
63 BoundingBox plot_bounds;
64
65 /* The bounding box for the entire drawable area  of current terminal */
66 BoundingBox canvas;
67
68 /* The bounding box against which clipping is to be done */
69 BoundingBox *clip_area = &plot_bounds;
70
71 /* 'set size', 'set origin' setttings */
72 float xsize = 1.0;              /* scale factor for size */
73 float ysize = 1.0;              /* scale factor for size */
74 float zsize = 1.0;              /* scale factor for size */
75 float xoffset = 0.0;            /* x origin */
76 float yoffset = 0.0;            /* y origin */
77 float aspect_ratio = 0.0;       /* don't attempt to force it */
78 int aspect_ratio_3D = 0;        /* 2 will put x and y on same scale, 3 for z also */
79
80 /* EAM Augest 2006 - 
81    redefine margin as t_position so that absolute placement is possible */
82 /* space between left edge and plot_bounds.xleft in chars (-1: computed) */
83 t_position lmargin = DEFAULT_MARGIN_POSITION;
84 /* space between bottom and plot_bounds.ybot in chars (-1: computed) */
85 t_position bmargin = DEFAULT_MARGIN_POSITION;
86 /* space between right egde and plot_bounds.xright in chars (-1: computed) */
87 t_position rmargin = DEFAULT_MARGIN_POSITION;
88 /* space between top egde and plot_bounds.ytop in chars (-1: computed) */
89 t_position tmargin = DEFAULT_MARGIN_POSITION;
90
91 /* File descriptor for output during 'set table' mode */
92 FILE *table_outfile = NULL;
93 TBOOLEAN table_mode = FALSE;
94
95 /* Pointer to the start of the linked list of 'set label' definitions */
96 struct text_label *first_label = NULL;
97
98 /* Pointer to first 'set linestyle' definition in linked list */
99 struct linestyle_def *first_linestyle = NULL;
100
101 /* Pointer to first 'set style arrow' definition in linked list */
102 struct arrowstyle_def *first_arrowstyle = NULL;
103
104 /* set arrow */
105 struct arrow_def *first_arrow = NULL;
106
107 #ifdef EAM_OBJECTS
108 /* Pointer to first object instance in linked list */
109 struct object *first_object = NULL;
110 #endif
111
112 /* 'set title' status */
113 text_label title = EMPTY_LABELSTRUCT;
114
115 /* 'set timelabel' status */
116 text_label timelabel = EMPTY_LABELSTRUCT;
117 int timelabel_rotate = FALSE;
118 int timelabel_bottom = TRUE;
119
120 /* flag for polar mode */
121 TBOOLEAN polar = FALSE;
122
123 /* zero threshold, may _not_ be 0! */
124 double zero = ZERO;
125
126 /* Status of 'set pointsize' command */
127 double pointsize = 1.0;
128
129 /* set border */
130 int draw_border = 31;
131 int border_layer = 1;
132 # define DEFAULT_BORDER_LP { 0, -2, 0, 1.0, 1.0, 0 }
133 struct lp_style_type border_lp = DEFAULT_BORDER_LP;
134 const struct lp_style_type default_border_lp = DEFAULT_BORDER_LP;
135
136 /* set clip */
137 TBOOLEAN clip_lines1 = TRUE;
138 TBOOLEAN clip_lines2 = FALSE;
139 /* FIXME HBB 20000521: clip_points is only used by 2D plots. This may
140  * well constitute a yet unknown bug... */
141 TBOOLEAN clip_points = FALSE;
142
143 /* set samples */
144 int samples_1 = SAMPLES;
145 int samples_2 = SAMPLES;
146
147 /* set angles */
148 double ang2rad = 1.0;           /* 1 or pi/180, tracking angles_format */
149
150 enum PLOT_STYLE data_style = POINTSTYLE;
151 enum PLOT_STYLE func_style = LINES;
152
153 TBOOLEAN parametric = FALSE;
154
155 /* If last plot was a 3d one. */
156 TBOOLEAN is_3d_plot = FALSE;
157
158 #ifdef WITH_IMAGE
159 /* If last plot was one using color bus, e.g., image, map, pm3d. */
160 TBOOLEAN is_cb_plot = FALSE;
161 #endif
162
163 fill_style_type default_fillstyle = { FS_EMPTY, 100, 0, LT_UNDEFINED } ;
164
165 #ifdef EAM_OBJECTS
166 /* Default rectangle style - background fill, black border */
167 struct object default_rectangle = DEFAULT_RECTANGLE_STYLE;
168 #endif
169
170 /* filledcurves style options */
171 filledcurves_opts filledcurves_opts_data = EMPTY_FILLEDCURVES_OPTS;
172 filledcurves_opts filledcurves_opts_func = EMPTY_FILLEDCURVES_OPTS;
173
174 /* Prefer line styles over plain line types */
175 TBOOLEAN prefer_line_styles = FALSE;
176
177 #ifdef EAM_HISTOGRAMS
178 histogram_style histogram_opts = DEFAULT_HISTOGRAM_STYLE;
179 #endif
180
181 /*****************************************************************/
182 /* Routines that deal with global objects defined in this module */
183 /*****************************************************************/
184
185 /* Clipping to the bounding box: */
186
187 /* Test a single point to be within the BoundingBox.
188  * Sets the returned integers 4 l.s.b. as follows:
189  * bit 0 if to the left of xleft.
190  * bit 1 if to the right of xright.
191  * bit 2 if below of ybot.
192  * bit 3 if above of ytop.
193  * 0 is returned if inside.
194  */
195 int
196 clip_point(unsigned int x, unsigned int y)
197 {
198     int ret_val = 0;
199
200     if (!clip_area)
201         return 0;
202     if ((int)x < clip_area->xleft)
203         ret_val |= 0x01;
204     if ((int)x > clip_area->xright)
205         ret_val |= 0x02;
206     if ((int)y < clip_area->ybot)
207         ret_val |= 0x04;
208     if ((int)y > clip_area->ytop)
209         ret_val |= 0x08;
210
211     return ret_val;
212 }
213
214 /* Clip the given line to drawing coords defined by BoundingBox.
215  *   This routine uses the cohen & sutherland bit mapping for fast clipping -
216  * see "Principles of Interactive Computer Graphics" Newman & Sproull page 65.
217  */
218 void
219 draw_clip_line(int x1, int y1, int x2, int y2)
220 {
221     struct termentry *t = term;
222
223 #if defined(ATARI) || defined(MTOS)
224     /* HBB 20000522: why would this test be particular to ATARIs? And
225      * what was the bug this is supposed to fix? */
226     if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0)
227         return;                 /* temp bug fix */
228 #endif
229
230     /* HBB 20000522: I've made this routine use the clippling in
231      * clip_line(), in a movement to reduce code duplication. There
232      * was one very small difference between these two routines. See
233      * clip_line() for a comment about it, at the relevant place. */
234     if (!clip_line(&x1, &y1, &x2, &y2))
235         /* clip_line() returns zero --> segment completely outside
236          * bounding box */
237         return;
238
239     /* HBB 20000617: there was a design error, here. By nature, this
240      * is a 2D routine. It should never have to check for hidden3d
241      * related variables, or call the hidden3d routine
242      * draw_line_hidden, like this. I've thrown this out. */
243
244     /* FIXME HBB 20000522: I strongly doubt this can work as
245      * advertised.  It's supposed to allow for continuous contour
246      * curves, but as soon as the contour curve moves outside the
247      * boundary, you get overpainting instead, as the contour curve
248      * walks along the border. Or worse artefacts.  */
249 #if 0 /* UNUSED */
250     if (!suppressMove)
251 #endif
252         (*t->move) (x1, y1);
253     (*t->vector) (x2, y2);
254 }
255
256 void
257 draw_clip_arrow( int sx, int sy, int ex, int ey, int head)
258 {
259     struct termentry *t = term;
260
261     /* Don't draw head if the arrow itself is clipped */
262     if (clip_point(sx,sy))
263         head &= ~BACKHEAD;
264     if (clip_point(ex,ey))
265         head &= ~END_HEAD;
266     clip_line(&sx, &sy, &ex, &ey);
267
268     /* Call terminal routine to draw the clipped arrow */
269     (*t->arrow)((unsigned int)sx, (unsigned int)sy,
270                 (unsigned int)ex, (unsigned int)ey, head);
271 }
272
273
274 /* And text clipping routine. */
275 void
276 clip_put_text(unsigned int x, unsigned y, char *str)
277 {
278     struct termentry *t = term;
279
280     if (clip_point(x, y))
281         return;
282
283     (*t->put_text) (x, y, str);
284 }
285
286
287 /* Clip the given line to drawing coords defined by BoundingBox.
288  *   This routine uses the cohen & sutherland bit mapping for fast clipping -
289  * see "Principles of Interactive Computer Graphics" Newman & Sproull page 65.
290  */
291 /* FIXME HBB 20000521: the parameters of this routine should become
292  * unsigned ints. */
293 int
294 clip_line(int *x1, int *y1, int *x2, int *y2)
295 {
296     int x, y, dx, dy, x_intr[4], y_intr[4], count, pos1, pos2;
297     int x_max, x_min, y_max, y_min;
298     pos1 = clip_point(*x1, *y1);
299     pos2 = clip_point(*x2, *y2);
300     if (!pos1 && !pos2)
301         return 1;               /* segment is totally in */
302     if (pos1 & pos2)
303         return 0;               /* segment is totally out. */
304     /* Here part of the segment MAY be inside. test the intersection
305      * of this segment with the 4 boundaries for hopefully 2 intersections
306      * in. If none are found segment is totaly out.
307      * Under rare circumstances there may be up to 4 intersections (e.g.
308      * when the line passes directly through at least one corner). In
309      * this case it is sufficient to take any 2 intersections (e.g. the
310      * first two found).
311      */
312     count = 0;
313     dx = *x2 - *x1;
314     dy = *y2 - *y1;
315     /* Find intersections with the x parallel bbox lines: */
316     if (dy != 0) {
317         x = (clip_area->ybot - *y2) * dx / dy + *x2;    /* Test for clip_area->ybot boundary. */
318         if (x >= clip_area->xleft && x <= clip_area->xright) {
319             x_intr[count] = x;
320             y_intr[count++] = clip_area->ybot;
321         }
322         x = (clip_area->ytop - *y2) * dx / dy + *x2;    /* Test for clip_area->ytop boundary. */
323         if (x >= clip_area->xleft && x <= clip_area->xright) {
324             x_intr[count] = x;
325             y_intr[count++] = clip_area->ytop;
326         }
327     }
328     /* Find intersections with the y parallel bbox lines: */
329     if (dx != 0) {
330         y = (clip_area->xleft - *x2) * dy / dx + *y2;   /* Test for clip_area->xleft boundary. */
331         if (y >= clip_area->ybot && y <= clip_area->ytop) {
332             x_intr[count] = clip_area->xleft;
333             y_intr[count++] = y;
334         }
335         y = (clip_area->xright - *x2) * dy / dx + *y2;  /* Test for clip_area->xright boundary. */
336         if (y >= clip_area->ybot && y <= clip_area->ytop) {
337             x_intr[count] = clip_area->xright;
338             y_intr[count++] = y;
339         }
340     }
341     if (count < 2)
342         return 0;
343
344     if (*x1 < *x2) {
345         x_min = *x1;
346         x_max = *x2;
347     } else {
348         x_min = *x2;
349         x_max = *x1;
350     }
351     if (*y1 < *y2) {
352         y_min = *y1;
353         y_max = *y2;
354     } else {
355         y_min = *y2;
356         y_max = *y1;
357     }
358
359     if (pos1 && pos2) {         /* Both were out - update both */
360         *x1 = x_intr[0];
361         *y1 = y_intr[0];
362         *x2 = x_intr[1];
363         *y2 = y_intr[1];
364     } else if (pos1) {          /* Only x1/y1 was out - update only it */
365         /* This is about the only real difference between this and
366          * draw_clip_line(): it compares for '>0', here */
367         if (dx * (*x2 - x_intr[0]) + dy * (*y2 - y_intr[0]) >= 0) {
368             *x1 = x_intr[0];
369             *y1 = y_intr[0];
370         } else {
371             *x1 = x_intr[1];
372             *y1 = y_intr[1];
373         }
374     } else {                    /* Only x2/y2 was out - update only it */
375         /* Same difference here, again */
376         if (dx * (x_intr[0] - *x1) + dy * (y_intr[0] - *y1) >= 0) {
377             *x2 = x_intr[0];
378             *y2 = y_intr[0];
379         } else {
380             *x2 = x_intr[1];
381             *y2 = y_intr[1];
382         }
383     }
384
385     if (*x1 < x_min || *x1 > x_max || *x2 < x_min || *x2 > x_max || *y1 < y_min || *y1 > y_max || *y2 < y_min || *y2 > y_max)
386         return 0;
387
388     return 1;
389 }
390
391
392 /* Two routines to emulate move/vector sequence using line drawing routine. */
393 static unsigned int move_pos_x, move_pos_y;
394
395 void
396 clip_move(unsigned int x, unsigned int y)
397 {
398     move_pos_x = x;
399     move_pos_y = y;
400 }
401
402 void
403 clip_vector(unsigned int x, unsigned int y)
404 {
405     draw_clip_line(move_pos_x, move_pos_y, x, y);
406     move_pos_x = x;
407     move_pos_y = y;
408 }
409
410 /* Common routines for setting text or line color from t_colorspec */
411
412 void
413 apply_pm3dcolor(struct t_colorspec *tc, const struct termentry *t)
414 {
415
416     /* Replace colorspec with that of the requested line style */
417     struct lp_style_type style;
418     if (tc->type == TC_LINESTYLE) {
419         lp_use_properties(&style, tc->lt, 0);
420         (*t->linetype)(style.l_type);
421         tc = &style.pm3d_color;
422     }
423
424     if (tc->type == TC_DEFAULT) {
425         (*t->linetype)(LT_BLACK);
426         return;
427     }
428     if (tc->type == TC_LT) {
429         if (t->set_color)
430             t->set_color(tc);
431         else
432             (*t->linetype)(tc->lt);
433         return;
434     }
435     if (tc->type == TC_RGB && t->set_color) {
436         t->set_color(tc);
437         return;
438     }
439     if (!is_plot_with_palette() || !t->set_color) {
440         (*t->linetype)(LT_BLACK);
441         return;
442     }
443     switch (tc->type) {
444         case TC_Z:    set_color(cb2gray(z2cb(tc->value))); break;
445         case TC_CB:   set_color(cb2gray(tc->value));       break;
446         case TC_FRAC: set_color(sm_palette.positive == SMPAL_POSITIVE ?
447                                 tc->value : 1-tc->value);
448                       break;
449     }
450     if (tc->type == TC_LT) {
451         (*t->linetype)(tc->lt);
452         return;
453     }
454 }
455
456 void
457 reset_textcolor(const struct t_colorspec *tc, const struct termentry *t)
458 {
459     if (tc->type != TC_DEFAULT)
460         (*t->linetype)(LT_BLACK);
461 }
462
463
464 void
465 default_arrow_style(struct arrow_style_type *arrow)
466 {
467     static const struct lp_style_type tmp_lp_style = DEFAULT_LP_STYLE_TYPE;
468
469     arrow->layer = 0;
470     arrow->lp_properties = tmp_lp_style;
471     arrow->head = 1;
472     arrow->head_length = 0.0;
473     arrow->head_lengthunit = first_axes;
474     arrow->head_angle = 15.0;
475     arrow->head_backangle = 90.0;
476     arrow->head_filled = 0;
477 }
478
479 #ifdef EAM_DATASTRINGS
480 void
481 free_labels(struct text_label *label)
482 {
483 struct text_label *temp;
484 char *master_font = label->font;
485
486     /* Labels generated by 'plot with labels' all use the same font */
487     if (master_font)
488         free(master_font);
489
490     while (label) {
491         if (label->text)
492             free(label->text);
493         if (label->font && label->font != master_font)
494             free(label->font);
495         temp=label->next;
496         free(label);
497         label = temp;
498     }
499
500 }
501 #endif
502
503 void
504 get_offsets(
505     struct text_label *this_label,
506     struct termentry *t,
507     int *htic, int *vtic)
508 {
509     if (this_label->lp_properties.pointflag) {
510         *htic = (pointsize * t->h_tic * 0.5);
511         *vtic = (pointsize * t->v_tic * 0.5);
512     } else {
513         *htic = 0;
514         *vtic = 0;
515     }
516     if (is_3d_plot) {
517         int htic2, vtic2;
518         map3d_position_r(&(this_label->offset), &htic2, &vtic2, "get_offsets");
519         *htic += htic2;
520         *vtic += vtic2;
521     } else {
522         double htic2, vtic2;
523         map_position_r(&(this_label->offset), &htic2, &vtic2, "get_offsets");
524         *htic += (int)htic2;
525         *vtic += (int)vtic2;
526     }
527 }
528
529
530 /*
531  * Write one label, with all the trimmings.
532  * This routine is used for both 2D and 3D plots.
533  */
534 void
535 write_label(unsigned int x, unsigned int y, struct text_label *this_label)
536 {
537         int htic, vtic;
538         int justify = JUST_TOP; /* This was the 2D default; 3D had CENTRE */
539
540         apply_pm3dcolor(&(this_label->textcolor),term);
541         ignore_enhanced(this_label->noenhanced);
542
543         get_offsets(this_label, term, &htic, &vtic);
544         if (this_label->rotate && (*term->text_angle) (this_label->rotate)) {
545             write_multiline(x + htic, y + vtic, this_label->text,
546                             this_label->pos, justify, this_label->rotate,
547                             this_label->font);
548             (*term->text_angle) (0);
549         } else {
550             write_multiline(x + htic, y + vtic, this_label->text,
551                             this_label->pos, justify, 0, this_label->font);
552         }
553         /* write_multiline() clips text to on_page; do the same for any point */
554         if (this_label->lp_properties.pointflag && on_page(x,y)) {
555             term_apply_lp_properties(&this_label->lp_properties);
556             (*term->point) (x, y, this_label->lp_properties.p_type);
557             /* the default label color is that of border */
558             term_apply_lp_properties(&border_lp);
559         }
560
561         ignore_enhanced(FALSE);
562 }