1 /*
2 Gri - A language for scientific graphics programming
3 Copyright (C) 2008 Daniel Kelley
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #define NEW_TRANSFORM 1 // 11jun95, try new mapping method
21
22 // Global variable/function names begin with _gr.
23
24 // Sections of uncertain or buggy code are sometimes marked by
25 // comments containing the strings "BUG" or "??".
26
27 // Cross-dependent code is sometimes marked with comments
28 // containing the string XREF.
29
30
31 #include <ctype.h>
32 #include <string.h>
33 #include <time.h>
34 #include <math.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <readline/readline.h>
38 #include <readline/history.h>
39
40 // Local includes
41 #include "gr.hh"
42 #include "postscpt.hh"
43 #include "private.hh"
44 #include "macro.hh"
45 #include "GriPath.hh"
46 #include "GriState.hh"
47 #include "defaults.hh"
48 #include "superus.hh"
49
50 static void close_ps_file(FILE * fp);
51 static void handle_landscape_scale(FILE * fp);
52 static void insert_ps_header(FILE * fp, bool privacy);
53 static void skip_ps_header(FILE * PSfile);
54 static void create_font_encoding(const char *fontname);
55
56
57 static char creator_name[256] = "";
58 static char ps_filename[256] = "gri.ps"; /* basename */
59 static char ps_filename_used[256] = ""; /* actual name used */
60 static bool user_gave_ps_filename = false; /* did user give the name? */
61 static int which_page = 0; /* page counter */
62
63 bool _no_bounding_box = false;
64
65
66 extern GriState _griState; // <-> gri.cc
67 extern bool _drawingstarted;
68 extern int _debugFlag;
69 //
70 // Utilities.
71 //
72
73 // gr_multiple - is x a multiple of d (to given precision)?
74 bool
gr_multiple(double x,double d,double precision)75 gr_multiple(double x, double d, double precision)
76 {
77 double epsilon;
78 if (d < 0.0)
79 d = -d;
80 if (x < 0.0)
81 x = -x;
82 if (precision < 0.0)
83 precision = -precision;
84 epsilon = GRI_ABS(x - d * (floor(0.5 + x / d)));
85 return ((epsilon < precision) ? true : false);
86 }
87
88 //
89 // UNIVERSAL GLOBALS
90 #define default_linewidth 0.5
91 #define LEN_num 50
92 #define LEN_lab 256
93 gr_pen _grCurrentPen;
94 char _grXAxisLabel[LEN_lab]; // name of x axis
95 char _grYAxisLabel[LEN_lab]; // name of y axis
96 char _grNumFormat_x[LEN_num]; // format for x axis
97 char _grNumFormat_y[LEN_num]; // format for y axis
98 char grTempString[_grTempStringLEN]; // local scratch string
99 char grTempString2[_grTempStringLEN]; // local scratch string
100 double _clip_ps_xleft = 0.0, _clip_ps_ybottom = 0.0, _clip_ps_xright = 0.0, _clip_ps_ytop = 0.0;
101 bool _clipping_is_postscript_rect = true;
102 bool _clipping_postscript = false;
103
104 /*
105 * stdin buffer
106 */
107 FBUFFER stdin_buffer =
108 {stdin, 0, 0, 0}; /* note 0 for buffer */
109
110 /* GLOBAL scratch strings */
111 char _grTempString[_grTempStringLEN];
112 char _grTempString2[_grTempStringLEN];
113
114 // Missing values
115 bool _grMissingValueUsed = true;
116 double _grMissingValue = 1.0e22, _grMissingValue_tolerance = 1.0e20;
117
118 double
119 _grCmPerUser_y, /* y cm-on-page / user-unit */
120 _grCmPerUser_x, /* x cm-on-page / user-unit */
121 _grCurrentPoint_x, /* current x coordinate of pen */
122 _grCurrentPoint_y, /* current y coordinate of pen */
123 _grGrayScreenQuantum = .0625, /* gray step on mac */
124 _grMagx = 1.0, /* x magnification on screen */
125 _grMagy = 1.0, /* y magnification on screen */
126 _grOriginx = 0.0, /* x origin on screen */
127 _grOriginy = 0.0, /* y origin on screen */
128 _grPageHeight_cm = 27.94, /* page height in cm */
129 _grPageWidth_cm = 21.59, /* page width in cm */
130 _grPSScale_x = 1.0, /* _Mag factor for x scale on ps */
131 _grPSScale_y = 1.0, /* _Mag factor for y */
132 _grPtPerUser_x = PT_PER_CM, /* x pt-on-page / user-unit
133 * (assume cm) */
134 _grPtPerUser_y = PT_PER_CM, /* y pt-on-page / user-unit
135 * (assume cm) */
136 _grSymbolSize_pt, /* symbol size in point */
137 _grTicSize_cm, /* axis tic size */
138 _grPROJx0, _grPROJxfac, _grPROJy0, _grPROJyfac, _grxl, /* x left - user units */
139 _grxl_pt, /* x left - points */
140 _gryb, /* y bottom - user units */
141 _gryb_pt; /* y bottom - points */
142
143 double _ll_x_pt, _ll_y_pt, _ur_x_pt, _ur_y_pt; // lower-left, point units
144 double _ll_x_us, _ll_y_us, _ur_x_us, _ur_y_us; // upper-right, user units
145
146 gr_axis_properties _grTransform_x = gr_axis_LINEAR;
147 gr_axis_properties _grTransform_y = gr_axis_LINEAR;
148 bool _grTicsPointIn = false;
149 int _grAxisStyle_x = 0; /* always 0 */
150 int _grAxisStyle_y = 0; /* 0 = falsermal, 1 = label horizontal */
151 bool _grDrawingDash = false; /* =1 if drawing dash part of a line */
152 bool _grPS_Landscape = false; /* flag for landscape */
153 //bool _grPS_Landscape = true; /* flag for landscape */
154 int _grSpecifications = 1; /* specifications in gr_begin */
155 int _grNumSubDiv_x; /* # x-subdivision per labelled tic */
156 int _grNumSubDiv_y; /* # x-subdivision per labelled tic */
157 bool _grNeedBegin = true; /* Need to call gr_begin()? */
158 bool _grPathExists = false; /* does a postscript 'path' exist? */
159 bool _grWritePS = true; /* ==1 if postscript to be written */
160
161 // The output may be in PostScript or SVG
162 FILE *_grPS;
163 FILE *_grSVG;
164
165 // Local prototypes.
166 static void set_page_characteristics();
167
168 // Always returns true
169 bool
gr_textsave(const char * s)170 gr_textsave(const char *s)
171 {
172 static bool first = true;
173 unsigned int len;
174 len = strlen(s);
175 if (first == true) {
176 stdin_buffer.fp = stdin;
177 GET_STORAGE(stdin_buffer.buf, char, 2 + len);
178 strcpy(stdin_buffer.buf, s);
179 first = false;
180 } else {
181 stdin_buffer.buf = (char *)realloc(stdin_buffer.buf, 2 + len + stdin_buffer.buf_capacity);
182 strcat(stdin_buffer.buf, s);
183 }
184 strcat(stdin_buffer.buf, "\n");
185 stdin_buffer.buf_capacity += 1 + len;
186 return true;
187 }
188
189 /*
190 * gr_textget (s, max) Get a line of text. If the line ends in backslash,
191 * get next line and concatenate the two together; if succeeding lines end in
192 * backslash, continue the contatenation. RETURN VALUE 1 if got text; 0 if
193 * got EOF.
194 */
195 bool
gr_textget(char * s,int max)196 gr_textget(char *s, int max)
197 {
198 int most = max;
199 bool got_eof = gr_buffgets(s, most, &stdin_buffer);
200 //printf("DEBUG %s:%d in gr_textget. got_eof=%d\n",__FILE__,__LINE__,int(got_eof));
201 if (got_eof) {
202 /*
203 * No newline at end of file. Paste one on.
204 */
205 strcat(s, "\n");
206 return false;
207 } else {
208 int len;
209 while ((len = strlen(s)) > 1 && s[len - 2] == '\\' && most > 0) {
210 most += len - 1;
211 got_eof = gr_buffgets(s + len - 2, most, &stdin_buffer);
212 if (got_eof) {
213 /*
214 * No newline at end of file. Paste one on.
215 */
216 strcat(s, "\n");
217 return true;
218 }
219 }
220 return true;
221 }
222 }
223
224 /*
225 * Get string from io buffer, or from io file, if buffer empty. Stop reading
226 * when newline or eof encountered.
227 *
228 * Return true if got to eof of the file.
229 */
230 bool
gr_buffgets(char * s,unsigned int most,FBUFFER * fbuf)231 gr_buffgets(char *s, unsigned int most, FBUFFER * fbuf)
232 {
233 //printf("DEBUG %s:%d in gr_buffgets fbuf at %x\n",__FILE__,__LINE__,int(fbuf));
234 if (fbuf->buf_position < fbuf->buf_capacity) {
235 //printf("DEBUG getting from buf...\n");
236 unsigned int i;
237 for (i = 0; i < most - 1; i++) {
238 s[i] = fbuf->buf[fbuf->buf_position++];
239 if (s[i] == '\n')
240 break;
241 if (fbuf->buf_position >= fbuf->buf_capacity)
242 break; /* will lose some characters, but no warning */
243 }
244 s[i + 1] = '\0';
245 //printf("DEBUG buf provided <%s>\n",s);
246 } else {
247 #ifdef HAVE_LIBREADLINE
248 char *line;
249 line = readline("gri: ");
250 if (line) {
251 if (strlen(line) > 0) {
252 add_history(line);
253 }
254 } else {
255 return true;
256 }
257 // Tack a newline on end
258 strcpy(s, line);
259 int n = strlen(s);
260 s[n] = '\n';
261 s[n+1] = '\0';
262 free(line);
263 if (feof(stdin))
264 return true;
265 #else
266 fgets(s, most, (FILE *) fbuf->fp);
267 if (feof(fbuf->fp))
268 return true;
269 #endif
270 }
271 return false;
272 }
273
274 // print string
275 void
gr_textput(const char * s)276 gr_textput(const char *s)
277 {
278 unsigned int l = strlen(s);
279 for (unsigned int i = 0; i < l; i++) {
280 if (s[i] == '\\') {
281 if (i < l - 2) {
282 if (s[i + 1] == '>' && s[i + 2] == '>') {
283 putc('\t', stdout);
284 i++;
285 i++;
286 continue;
287 } else if (s[i + 1] == '<' && s[i + 2] == '<') {
288 putc('\n', stdout);
289 i++;
290 i++;
291 continue;
292 }
293 }
294 }
295 putc(s[i], stdout);
296 }
297
298 }
299
300 bool
delete_ps_file()301 delete_ps_file()
302 {
303 //printf("%s:%d delete_ps_file. _grPS at %x\n",__FILE__,__LINE__,int(_grPS));
304 extern output_file_type _output_file_type;
305 if (_output_file_type == postscript) {
306 if (_grPS != NULL) {
307 fclose(_grPS);
308 _grPS = NULL;
309 }
310 return delete_file(ps_filename_used);
311 } else {
312 return true;
313 }
314 }
315
316 // gr_begin() -- prepare for plotting.
317 // specifications==1 normal case
318 // specifications==2 restarting, so don't default stuff
319 void
gr_begin(int specifications)320 gr_begin(int specifications)
321 {
322 extern output_file_type _output_file_type;
323 if (_output_file_type == postscript) {
324 int version;
325 _grNeedBegin = false;
326 _grSpecifications = specifications;
327 // open gri.ps file, avoiding overwriting existing ones
328 //printf("DEBUG 1\n");
329 if (user_gave_ps_filename) {
330 //printf("DEBUG 2. '%s'\n",ps_filename);
331 strcpy(ps_filename_used, ps_filename);
332 } else {
333 #if defined(VMS)
334 // On vax, just use version numbers
335 sprintf(ps_filename_used, "%s", ps_filename);
336 #else
337 for (version = 0; version < 9999; version++) {
338 FILE *trial;
339 sprintf(ps_filename_used, "gri-%02d.ps", version);
340 trial = fopen(ps_filename_used, "r");
341 if (NULL == trial)
342 break;
343 fclose(trial);
344 }
345 #endif
346 }
347 _grPS = fopen(ps_filename_used, "w+");
348 //printf("%s:%d opened _grPS at %x\n",__FILE__,__LINE__,int(_grPS));
349 // if can't open file, try again
350 if (_grPS == NULL) {
351 int ok_after_all = false;
352 #if defined(HAVE_GETENV)
353 char homename[100];
354 if (getenv("home")) {
355 strcpy(homename, getenv("home"));
356 strcat(homename, "/gri.ps");
357 strcpy(ps_filename_used, homename);
358 _grPS = fopen(ps_filename_used, "w+");
359 if (_grPS == NULL) {
360 sprintf(_grTempString, "\
361 Cannot open output PostScript file named\n\t`%s'\nin this directory. Do you have write permission here?", ps_filename_used);
362 gr_textput(_grTempString);
363 gri_exit(1);
364 } else {
365 ok_after_all = 1;
366 }
367 }
368 #endif
369 if (ok_after_all) {
370 sprintf(_grTempString, "Couldn't create intended postscript file, so created `%s' instead\n", ps_filename_used);
371 warning(_grTempString);
372 } else {
373 sprintf(_grTempString, "\
374 Cannot open output PostScript file named\n\t`%s'\nin this directory. Do you have write permission here?", ps_filename);
375 gr_textput(_grTempString);
376 gri_exit(1);
377 }
378 }
379 // write some header stuff in postscript file
380 _grWritePS = true;
381 // define postscript abbreviations
382 insert_ps_header(_grPS, true);
383 }
384 if (specifications == 1)
385 set_page_characteristics();
386 }
387
set_page_characteristics()388 static void set_page_characteristics()
389 {
390 gr_setfont(gr_font_Helvetica);
391 gr_setfontsize_pt(-1.0);
392 _griState.set_linewidth_line(LINEWIDTH_DEFAULT);
393 gr_setsymbolsize_cm(0.2);
394 gr_setticdirection(false); // point out
395 gr_setticsize_cm(0.2);
396 gr_setxlabel("x");
397 gr_setylabel("y");
398 gr_setxnumberformat("%g");
399 gr_setynumberformat("%g");
400 gr_setxpagesize_cm(21.59);
401 gr_setypagesize_cm(27.94);
402 gr_setxtransform(gr_axis_LINEAR);
403 gr_setytransform(gr_axis_LINEAR);
404 gr_setxscale(0.0, 1.0, 0.0, 1.0);
405 gr_setyscale(0.0, 1.0, 0.0, 1.0);
406 gr_setxsubdivisions(1);
407 gr_setysubdivisions(1);
408 _grPathExists = true;
409 }
410
411 static void
insert_ps_header(FILE * fp,bool privacy)412 insert_ps_header(FILE * fp, bool privacy)
413 {
414 extern output_file_type _output_file_type;
415 if (_output_file_type == postscript) {
416 /*
417 * write conforming postscript prolog
418 */
419 fprintf(fp, "%%!PS-Adobe-2.0 EPSF-1.2\n");
420 if (privacy)
421 fprintf(fp, "%%%%Creator: %s\n", "");
422 else
423 fprintf(fp, "%%%%Creator: %s\n", creator_name);
424 fprintf(fp, "%%%%Title: %s\n", ps_filename_used);
425 SECOND_TYPE sec;
426 time(&sec);
427 fprintf(fp, "%%%%CreationDate: %s", asctime(localtime(&sec)));
428 fprintf(fp, "%%%%Pages: (atend)\n");
429 fprintf(fp, "%%%%BoundingBox: (atend)\n");
430
431 extern rectangle _bounding_box;
432 fprintf(fp, "%% trial bounding box %f %f %f %f\n",
433 _bounding_box.llx(),
434 _bounding_box.lly(),
435 _bounding_box.urx(),
436 _bounding_box.ury());
437
438 fprintf(fp, "%%%%TemplateBox: %d %d %d %d\n",
439 0, 0, (int) (8.5 * 72.0), (int) (11.0 * 72.0));
440 fprintf(fp, "%%%%DocumentFonts: (atend)\n");
441 fprintf(fp, "%%%%Orientation: (atend)\n");
442 fprintf(fp, "%%%%Endcomments\n");
443 int i = 0;
444 while (PS_dict[i])
445 fprintf(fp, "%s\n", PS_dict[i++]);
446 handle_landscape_scale(fp);
447 fprintf(fp, "10.0 M\n");
448 fprintf(fp, "1 j\n");
449 create_font_encoding("Courier");
450 create_font_encoding("Courier-Oblique");
451 create_font_encoding("Courier-Bold");
452 create_font_encoding("Courier-BoldOblique");
453 create_font_encoding("Helvetica");
454 create_font_encoding("Helvetica-Bold");
455 create_font_encoding("Helvetica-Oblique");
456 create_font_encoding("Palatino-Roman");
457 create_font_encoding("Palatino-Italic");
458 create_font_encoding("Palatino-Bold");
459 create_font_encoding("Palatino-BoldItalic");
460 create_font_encoding("Symbol");
461 create_font_encoding("Century");
462 create_font_encoding("Times-Roman");
463 create_font_encoding("Times-Italic");
464 create_font_encoding("Times-Bold");
465 create_font_encoding("Times-BoldItalic");
466 fprintf(fp, "%%%%EndProlog\n");
467 which_page++;
468 fprintf(_grPS, "%%%%Page: %d %d\ngsave\n", which_page, which_page);
469 }
470 }
471
472 static void
create_font_encoding(const char * fontname)473 create_font_encoding(const char *fontname)
474 {
475 extern output_file_type _output_file_type;
476 if (_output_file_type == postscript) {
477 fprintf(_grPS,
478 "/%s findfont dup length dict begin\n"
479 " {1 index /FID ne {def} {pop pop} ifelse } forall\n"
480 " /Encoding ISOLatin1Encoding def\n"
481 " currentdict\n"
482 "end\n"
483 "/%s-ISOLatin1 exch definefont pop\n", fontname, fontname);
484 }
485 }
486
487 static void
handle_landscape_scale(FILE * fp)488 handle_landscape_scale(FILE * fp)
489 {
490 extern output_file_type _output_file_type;
491 if (_output_file_type == postscript) {
492 /* put landscape and scale commands in ps output file */
493 if (_grPS_Landscape) {
494 fprintf(fp, "%g 0 translate 90 rotate %% Landscape\n", 8.5 * 72.0);
495 } else {
496 ;
497 }
498 fprintf(fp, "%g %g scale\n", _grPSScale_x, _grPSScale_y);
499 }
500 }
501
502 // gr_cmtouser() -- convert cm on page to user units. COMPARE gr_usertopt(),
503 // which is the inverse
504 void
gr_cmtouser(double x_cm,double y_cm,double * x,double * y)505 gr_cmtouser(double x_cm, double y_cm, double *x, double *y)
506 {
507 // XREF -- axis transform
508 // Do X.
509 switch (_grTransform_x) {
510 case gr_axis_LINEAR:
511 *x = _grxl + (x_cm - _grxl_pt / PT_PER_CM) / _grCmPerUser_x;
512 break;
513 case gr_axis_LOG:
514 *x = _grxl * pow(10.0, (x_cm - _grxl_pt / PT_PER_CM) / _grCmPerUser_x);
515 break;
516 default:
517 gr_Error("unknown axis mapping (internal error)");
518 break;
519 }
520 // Do Y.
521 switch (_grTransform_y) {
522 case gr_axis_LINEAR:
523 *y = _gryb + (y_cm - _gryb_pt / PT_PER_CM) / _grCmPerUser_y;
524 break;
525 case gr_axis_LOG:
526 *y = _gryb * pow(10.0, (y_cm - _gryb_pt / PT_PER_CM) / _grCmPerUser_y);
527 break;
528 default:
529 gr_Error("unknown axis mapping (internal error)");
530 break;
531 }
532 }
533
534 // gr_comment() -- write message in postscript file (without inserting
535 // newline)
gr_comment(const char * message)536 void gr_comment(const char *message)
537 {
538 extern output_file_type _output_file_type;
539 if (_output_file_type == postscript && _grWritePS)
540 fprintf(_grPS, PS_comment, message);
541 }
542
543 /*
544 * gr_currentPSfilename() -- get name of Postscript file SYNOPSIS char
545 * *gr_currentPSfilename(); DESCRIPTION Returns pointer to name of Postscript
546 * file
547 */
gr_currentPSfilename()548 char *gr_currentPSfilename()
549 {
550 return ps_filename_used;
551 }
552
553 /*
554 * gr_currentPSFILEpointer() -- get pointer Postscript file SYNOPSIS char
555 * *gr_currentPSFILEpointer(); DESCRIPTION Returns pointer to Postscript file
556 */
gr_currentPSFILEpointer()557 FILE *gr_currentPSFILEpointer()
558 {
559 return _grPS;
560 }
561
562 /*
563 * gr_current_ps_landscape() -- get present landscape mode RETURN VALUE 0 if
564 * portrait; 1 if landscape
565 */
gr_current_ps_landscape()566 bool gr_current_ps_landscape()
567 {
568 return _grPS_Landscape;
569 }
570
571 /*
572 * gr_currentmissingvalue() -- return current missing value
573 */
gr_currentmissingvalue()574 double gr_currentmissingvalue()
575 {
576 return _grMissingValue;
577 }
578
579 /*
580 * gr_currentsymbolsize_pt() -- return current symbol size in points
581 */
gr_currentsymbolsize_pt()582 double gr_currentsymbolsize_pt()
583 {
584 return _grSymbolSize_pt;
585 }
586
587 /*
588 * gr_currentticsize_cm() -- get current axis ticsize in cm
589 */
gr_currentticsize_cm()590 double gr_currentticsize_cm()
591 {
592 return _grTicSize_cm;
593 }
594
gr_draw_arc_cm(bool filled,double xc,double yc,double r,double angle1,double angle2)595 void gr_draw_arc_cm(bool filled, double xc, double yc, double r, double angle1, double angle2)
596 {
597 extern output_file_type _output_file_type;
598 if (_output_file_type == postscript) {
599 extern FILE *_grPS;
600 set_environment();
601 set_ps_color('p');
602 set_line_width_curve();
603 if (filled) { // this block corrected by Wolfgang Voegeli, fixing SF bug 930259
604 fprintf(_grPS, "%.1f %.1f m %.1f %.1f %.1f %.1f %.1f arc fill\n",
605 xc * PT_PER_CM + r * PT_PER_CM * cos(angle1 / DEG_PER_RAD),
606 yc * PT_PER_CM + r * PT_PER_CM * sin(angle1 / DEG_PER_RAD),
607 xc * PT_PER_CM, yc * PT_PER_CM,
608 r * PT_PER_CM,
609 angle1, angle2);
610 } else {
611 fprintf(_grPS, "%.1f %.1f m %.1f %.1f %.1f %.1f %.1f arc stroke\n",
612 xc * PT_PER_CM + r * PT_PER_CM * cos(angle1 / DEG_PER_RAD),
613 yc * PT_PER_CM + r * PT_PER_CM * sin(angle1 / DEG_PER_RAD),
614 xc * PT_PER_CM, yc * PT_PER_CM,
615 r * PT_PER_CM,
616 angle1, angle2);
617 }
618 double lw = _griState.linewidth_line() / 2.0 / PT_PER_CM;
619 rectangle bbox(xc - r - lw, yc - r - lw,
620 xc + r + lw, yc + r + lw);
621 bounding_box_update(bbox);
622 } else {
623 warning("Sorry, can only draw arcs in postscript files.");
624 }
625 }
626
627 // gr_drawarrow_cm -- Draw a stroke-line arrow
628 // If halfwidth>0, width of arrow head is 2*halfwidth.
629 // If halfwidth<0, width of arrow head is 2*halfwidth*length_of_arrow.
630 void
gr_drawarrow_cm(double x,double y,double xend,double yend,double halfwidth)631 gr_drawarrow_cm(double x, double y, double xend, double yend, double halfwidth)
632 {
633 double length = (xend - x) * (xend - x) + (yend - y) * (yend - y);
634 if (length <= 0.0)
635 return;
636 length = sqrt(length);
637 if (halfwidth < 0)
638 halfwidth = -halfwidth * length;
639 halfwidth /= length;
640 static GriPath p(5); // use static for reuse
641
642 p.clear();
643 p.push_back(0.0, 0.0, 'm');
644 p.push_back(1.0, 0.0, 'l');
645 p.push_back(1.0 - 3.0 * halfwidth, halfwidth, 'm');
646 p.push_back(1.0, 0.0, 'l');
647 p.push_back(1.0 - 3.0 * halfwidth, -halfwidth, 'l');
648 p.rotate(atan2(yend - y, xend - x) * DEG_PER_RAD);
649 p.scale(length);
650 p.translate(x, y);
651 p.stroke(units_cm);
652 }
653
654 // gr_drawarrow2_cm -- Draw an outlined arrow
655 // If halfwidth>0, width of arrow head is 2*halfwidth.
656 // If halfwidth<0, width of arrow head is 2*halfwidth*length_of_arrow.
657 void
gr_drawarrow2_cm(double x,double y,double xend,double yend,double halfwidth)658 gr_drawarrow2_cm(double x, double y, double xend, double yend, double halfwidth)
659 {
660 double length = (xend - x) * (xend - x) + (yend - y) * (yend - y);
661 if (length <= 0.0)
662 return; // ignore zero length arrows
663 length = sqrt(length);
664 if (halfwidth < 0)
665 halfwidth = -halfwidth * length;
666 static GriPath p(9); // use static for reuse
667 p.clear();
668 halfwidth /= length;
669 p.push_back(0.0, 0.4 * halfwidth, 'm');
670 p.push_back(0.0, -0.4 * halfwidth, 'l');
671 p.push_back(1.0 - 3. * halfwidth, -0.4 * halfwidth, 'l');
672 p.push_back(1.0 - 3. * halfwidth, -halfwidth, 'l');
673 p.push_back(1.0, 0.0, 'l');
674 p.push_back(1.0 - 3. * halfwidth, halfwidth, 'l');
675 p.push_back(1.0 - 3. * halfwidth, 0.4 * halfwidth, 'l');
676 p.push_back(0.0, 0.4 * halfwidth, 'l');
677 p.push_back(0.0, -0.4 * halfwidth, 'l');
678 p.rotate(atan2(yend - y, xend - x) * DEG_PER_RAD);
679 p.scale(length);
680 p.translate(x, y);
681 p.stroke(units_cm);
682 }
683
684 // gr_drawarrow3_cm -- Draw swept-back filled arrow
685 //
686 // If halfwidth>0, width of arrow head is 2*halfwidth.
687 // If halfwidth<0, width of arrow head is 2*halfwidth*length_of_arrow.
688 void
gr_drawarrow3_cm(double x,double y,double xend,double yend,double halfwidth)689 gr_drawarrow3_cm(double x, double y, double xend, double yend, double halfwidth)
690 {
691 // METHOD: define arrow on unit square from (0,0) to (1,0) and then
692 // rotate and scale as needed.
693
694 double length = (xend - x) * (xend - x) + (yend - y) * (yend - y);
695 if (length <= 0.0)
696 return; // ignore zero length arrows
697 length = sqrt(length);
698 if (halfwidth < 0)
699 halfwidth = -halfwidth * length;
700 static GriPath p(5); // static to allow re-use of storage
701 halfwidth /= length;
702
703 // Determine angle of rotation, in degrees.
704 double angle_deg = atan2(yend - y, xend - x) * DEG_PER_RAD;
705
706 // ... fill, and then stroke, the arrow part ...
707 p.clear(); // clear storage
708
709 p.push_back(1 - 2.25 * halfwidth, 0.0, 'm');
710 p.push_back(1 - 3. * halfwidth, halfwidth, 'l');
711 p.push_back(1.0, 0.0, 'l');
712 p.push_back(1 - 3. * halfwidth, -halfwidth, 'l');
713 p.push_back(1 - 2.25 * halfwidth, 0.0, 'l');
714
715 p.rotate(angle_deg);
716 p.scale(length);
717 p.translate(x, y);
718 p.fill(units_cm);
719 p.stroke(units_cm);
720
721 // ... then stroke the line.
722 p.clear(); // clear storage
723 p.push_back(0.0, 0.0, 'm');
724 p.push_back(1.0, 0.0, 'l');
725 p.rotate(angle_deg);
726 p.scale(length);
727 p.translate(x, y);
728 p.stroke(units_cm);
729 }
730
731 /*
732 * gr_drawerrorbars() -- draw error bars about a point SYNOPSIS void
733 * gr_drawerrorbars (double x, double xmin, double xmax, double y, double
734 * ymin, double ymax, int type); DESCRIPTION: Draws error bars extending from
735 * xmin to xmax and ymin to ymax. The type of error bars is controlled by
736 * 'type' as follows:
737 *
738 * 0 -> error bars done as lines drawn between the limits
739 *
740 * 1 -> right-angled tags added at ends of limits (NOT IMPLEMENTED) NOTE: The
741 * pen is left at (x,y), so that to draw a symbol with error bars, do
742 * something like this: gr_drawerrorbars(x,x-dx,x+dx,y,y-dy,y+dy,0);
743 * gr_drawsymbol(...,gr_circ_symbol);
744 */
745 void
gr_drawerrorbars(double x,double xmin,double xmax,double y,double ymin,double ymax,int type)746 gr_drawerrorbars(double x, double xmin, double xmax, double y, double ymin, double ymax, int type)
747 {
748 GriPath p(5);
749 switch (type) {
750 case 0:
751 default:
752 p.push_back(xmin, y, 'm');
753 p.push_back(xmax, y, 'l');
754 p.push_back(x, ymin, 'm');
755 p.push_back(x, ymax, 'l');
756 p.push_back(x, y, 'm');
757 p.stroke(units_user);
758 break;
759 }
760 }
761
762 // gr_drawsymbol() -- draw symbol at indicated location in cm
763 // BUG: remove the "n" and "S" when newpath/stroke business sorted
764 void
gr_drawsymbol(double xcm,double ycm,gr_symbol_type symbol_name)765 gr_drawsymbol(double xcm, double ycm, gr_symbol_type symbol_name)
766 {
767 extern output_file_type _output_file_type;
768 if (_output_file_type != postscript) {
769 warning("Sorry, can only draw symbols in postscript mode");
770 return;
771 }
772 double xpt = xcm * PT_PER_CM;
773 double ypt = ycm * PT_PER_CM;
774 if (_clipping_postscript && _clipping_is_postscript_rect) {
775 if (xpt > _clip_ps_xright ||
776 xpt < _clip_ps_xleft ||
777 ypt > _clip_ps_ytop ||
778 ypt < _clip_ps_ybottom) {
779 //printf("clip xrange (%f %f) pt\n",_clip_ps_xleft,_clip_ps_xright);
780 //printf("clip yrange (%f %f) pt\n",_clip_ps_ybottom,_clip_ps_ytop);
781 //printf("clipping (%.0f , %.0f)\n",xpt,ypt);
782 return;
783 }
784 }
785 extern bool _warn_offpage;
786 if (_warn_offpage
787 && ( xcm < OFFPAGE_LEFT
788 || xcm > OFFPAGE_RIGHT
789 || ycm < OFFPAGE_BOTTOM
790 || ycm > OFFPAGE_TOP)) {
791 warning("Drawing a symbol at a location that is offpage.");
792 }
793 switch (symbol_name) {
794 case gr_plus_symbol:
795 fprintf(_grPS, "n %.1f %.1f m %.1f _plus S\n", xpt, ypt, _grSymbolSize_pt);
796 break;
797 case gr_times_symbol:
798 fprintf(_grPS, "n %.1f %.1f m %.1f _times S\n", xpt, ypt, _grSymbolSize_pt);
799 break;
800 case gr_box_symbol:
801 fprintf(_grPS, "n %.1f %.1f m %.1f _box S\n", xpt, ypt, _grSymbolSize_pt);
802 break;
803 case gr_circ_symbol:
804 fprintf(_grPS, "n %.1f %.1f m %.1f _circ S\n", xpt, ypt, _grSymbolSize_pt);
805 break;
806 case gr_diamond_symbol:
807 fprintf(_grPS, "n %.1f %.1f m %.1f _diamond S\n", xpt, ypt, _grSymbolSize_pt);
808 break;
809 case gr_triangleup_symbol:
810 fprintf(_grPS, "n %.1f %.1f m %.1f _triangleup S\n", xpt, ypt, _grSymbolSize_pt);
811 break;
812 case gr_triangleright_symbol:
813 fprintf(_grPS, "n %.1f %.1f m %.1f _triangleright S\n", xpt, ypt, _grSymbolSize_pt);
814 break;
815 case gr_triangledown_symbol:
816 fprintf(_grPS, "n %.1f %.1f m %.1f _triangledown S\n", xpt, ypt, _grSymbolSize_pt);
817 break;
818 case gr_triangleleft_symbol:
819 fprintf(_grPS, "n %.1f %.1f m %.1f _triangleleft S\n", xpt, ypt, _grSymbolSize_pt);
820 break;
821 case gr_asterisk_symbol:
822 fprintf(_grPS, "n %.1f %.1f m %.1f _plus S\n", xpt, ypt, _grSymbolSize_pt);
823 fprintf(_grPS, "n %.1f %.1f m %.1f _times S\n", xpt, ypt, _grSymbolSize_pt);
824 break;
825 case gr_star_symbol:
826 fprintf(_grPS, "n %.1f %.1f m %.1f _triangleup S\n", xpt, ypt, _grSymbolSize_pt);
827 fprintf(_grPS, "n %.1f %.1f m %.1f _triangledown S\n", xpt, ypt, _grSymbolSize_pt);
828 break;
829 case gr_filledbox_symbol:
830 fprintf(_grPS, "n %.1f %.1f m %.1f _filledbox S\n", xpt, ypt, _grSymbolSize_pt);
831 break;
832 case gr_bullet_symbol:
833 fprintf(_grPS, "n %.1f %.1f m %.1f _bull S\n", xpt, ypt, _grSymbolSize_pt);
834 break;
835 case gr_filleddiamond_symbol:
836 fprintf(_grPS, "n %.1f %.1f m %.1f _filleddiamond S\n", xpt, ypt, _grSymbolSize_pt);
837 break;
838 case gr_filledtriangleup_symbol:
839 fprintf(_grPS, "n %.1f %.1f m %.1f _filledtriangleup S\n", xpt, ypt, _grSymbolSize_pt);
840 break;
841 case gr_filledtriangleright_symbol:
842 fprintf(_grPS, "n %.1f %.1f m %.1f _filledtriangleright S\n", xpt, ypt, _grSymbolSize_pt);
843 break;
844 case gr_filledtriangledown_symbol:
845 fprintf(_grPS, "n %.1f %.1f m %.1f _filledtriangledown S\n", xpt, ypt, _grSymbolSize_pt);
846 break;
847 case gr_filledtriangleleft_symbol:
848 fprintf(_grPS, "n %.1f %.1f m %.1f _filledtriangleleft S\n", xpt, ypt, _grSymbolSize_pt);
849 break;
850 case gr_filledhalfmoonup_symbol:
851 fprintf(_grPS, "n %.1f %.1f m %.1f _filledhalfmoonup S\n", xpt, ypt, _grSymbolSize_pt);
852 break;
853 case gr_filledhalfmoondown_symbol:
854 fprintf(_grPS, "n %.1f %.1f m %.1f _filledhalfmoondown S\n", xpt, ypt, _grSymbolSize_pt);
855 break;
856 default: // tiny (1.0 mm) triangle
857 {
858 double old_grSymbolSize_pt = gr_currentsymbolsize_pt();
859 gr_setsymbolsize_cm(0.1);
860 fprintf(_grPS, "n %.1f %.1f m %.1f _triangledown S\n", xpt, ypt, _grSymbolSize_pt);
861 gr_setsymbolsize_pt(old_grSymbolSize_pt);
862 }
863 break;
864 }
865 rectangle box(xcm - _grSymbolSize_pt / 2 / PT_PER_CM,
866 ycm - _grSymbolSize_pt / 2 / PT_PER_CM,
867 xcm + _grSymbolSize_pt / 2 / PT_PER_CM,
868 ycm + _grSymbolSize_pt / 2 / PT_PER_CM);
869 bounding_box_update(box);
870 _drawingstarted = true;
871 }
872
873 //
874 // gr_end() -- end this plot
875
876 // If filename == "" then the window stays open, and the gri-00.ps PostScript
877 // file is closed.
878 //
879 // If filename == "!" then the window is closed. If the filename is a valid
880 // filename, then the window stays open, and the PostScript file is copied
881 // into the indicated file.
882 //
883 // If the filename is "!" followed by a valid filename, then the window
884 // is closed and the PostScript is copied to the indicated file.
885 void
gr_end(const char * filename)886 gr_end(const char *filename)
887 {
888 extern bool _drawingstarted;
889 if (!_drawingstarted) {
890 delete_ps_file();
891 return;
892 }
893 extern output_file_type _output_file_type;
894 if (_output_file_type == postscript || _output_file_type == gif) {
895 extern bool _grNeedBegin;
896 extern bool _grPathExists;
897 extern FILE *_grPS;
898 if (_grNeedBegin)
899 return;
900 fprintf(_grPS, PS_showpage);
901 fprintf(_grPS, "%%%%Trailer\n");
902 extern rectangle _bounding_box;
903 extern rectangle _page_size;
904 if (_no_bounding_box) { // use fullpage
905 if (_page_size.llx() != _page_size.urx()) {
906 fprintf(_grPS, "%%%%BoundingBox: %d %d %d %d\n",
907 int(_page_size.llx()),
908 int(_page_size.lly()),
909 int(_page_size.urx()),
910 int(_page_size.ury()));
911 } else {
912 fprintf(_grPS, "%%%%BoundingBox: %d %d %d %d\n",
913 0,
914 0,
915 int( 8.5 * PT_PER_IN),
916 int(11.0 * PT_PER_IN));
917 }
918 } else {
919 extern bool _user_gave_bounding_box;
920 rectangle bbox;
921 if (_user_gave_bounding_box) {
922 extern rectangle _bounding_box_user;
923 bbox.set(_bounding_box_user.llx(),
924 _bounding_box_user.lly(),
925 _bounding_box_user.urx(),
926 _bounding_box_user.ury());
927 } else {
928 if (_page_size.llx() == _page_size.urx()) {
929 bbox.set(_bounding_box.llx(),
930 _bounding_box.lly(),
931 _bounding_box.urx(),
932 _bounding_box.ury());
933 } else {
934 bbox.set(LARGER_ONE(_bounding_box.llx(), _page_size.llx()),
935 LARGER_ONE(_bounding_box.lly(), _page_size.lly()),
936 SMALLER_ONE(_bounding_box.urx(), _page_size.urx()),
937 SMALLER_ONE(_bounding_box.ury(), _page_size.ury()));
938 }
939 }
940 double ll_x, ll_y, ur_x, ur_y;
941 if (_grPS_Landscape) {
942 gr_rotate_xy(bbox.llx(), bbox.lly(), 90, &ll_x, &ll_y);
943 gr_rotate_xy(bbox.urx(), bbox.ury(), 90, &ur_x, &ur_y);
944 ll_x += 2.54 * 8.5;
945 ur_x += 2.54 * 8.5;
946 double tmp = ll_x;
947 ll_x = ur_x;
948 ur_x = tmp;
949 } else {
950 ll_x = bbox.llx();
951 ll_y = bbox.lly();
952 ur_x = bbox.urx();
953 ur_y = bbox.ury();
954 }
955 #define POS(x) ((x) > 0 ? int(x) : 0)
956 if (_user_gave_bounding_box) {
957 fprintf(_grPS, "%%%%BoundingBox: %d %d %d %d\n",
958 POS(ll_x * PT_PER_CM),
959 POS(ll_y * PT_PER_CM),
960 POS(ur_x * PT_PER_CM),
961 POS(ur_y * PT_PER_CM));
962 } else {
963 fprintf(_grPS, "%%%%BoundingBox: %d %d %d %d\n",
964 POS(floor(-1.5 + ll_x * PT_PER_CM)),
965 POS(floor(-1.5 + ll_y * PT_PER_CM)),
966 POS(floor( 2.5 + ur_x * PT_PER_CM)),
967 POS(floor( 2.5 + ur_y * PT_PER_CM)));
968 }
969 #undef POS
970 }
971 fprintf(_grPS, "%%%%DocumentFonts: Courier Helvetica Palatino-Roman Palatino-Italic Symbol Times-Roman\n");
972 fprintf(_grPS, "%%%%Pages: %d\n", which_page);
973 fprintf(_grPS, "%%%%Orientation: %s\n",_grPS_Landscape?"Landscape":"Portrait");
974 _grPathExists = false;
975 user_gave_ps_filename = false;
976 // See if filename was specified
977 if ((strlen(filename) > 0)) {
978 if (filename[0] == '!') {
979 if (strlen(filename) > 1 && filename[1] != ' ') {
980 // save to named file
981 gr_save_postscript(filename + 1, 1);
982 close_ps_file(_grPS);
983 return;
984 } else {
985 // close without renaming
986 close_ps_file(_grPS);
987 return;
988 }
989 } else if (filename[0] != ' ') {
990 gr_save_postscript(filename, 1);
991 close_ps_file(_grPS);
992 }
993 }
994 } else if (_output_file_type == svg) {
995 fprintf(_grSVG, "</svg>\n");
996 // FIXME: should perhaps compress it...
997 }
998 } // gr_end()
999
1000 static void
close_ps_file(FILE * fp)1001 close_ps_file(FILE * fp)
1002 {
1003 #if 0
1004 extern rectangle _bounding_box;
1005 printf("FILE %s | %d | close_ps_file() | bounding box LLx LLy URx URy %d %d %d %d\n",
1006 __FILE__, __LINE__,
1007 int(_bounding_box.llx() * PT_PER_CM),
1008 int(_bounding_box.lly() * PT_PER_CM),
1009 int(_bounding_box.urx() * PT_PER_CM),
1010 int(_bounding_box.ury() * PT_PER_CM));
1011 #endif
1012 fclose(fp);
1013 _grNeedBegin = true;
1014 }
1015
1016 // gr_error() -- write an fatal message, then die
1017 void
gr_error(const char * lab)1018 gr_error(const char *lab)
1019 {
1020 sprintf(grTempString, "\nFATAL error: %s\n", lab);
1021 gr_textput(grTempString);
1022 abort_gri();
1023 }
1024
1025 void
abort_gri()1026 abort_gri()
1027 {
1028 //abort();
1029 exit(1);
1030 }
1031
1032 /*
1033 * (hue, saturation, value) in (enforced) range from 0 to 1 is translated
1034 * to (red, green, blue) in range 0 to 1. Inputs are clipped to range 0
1035 * to 1 if they are outside the range.
1036 *
1037 * Algorithm: Foley + Van Dam
1038 */
1039 void
gr_hsv2rgb(double h,double s,double v,double * r,double * g,double * b)1040 gr_hsv2rgb(double h, double s, double v, double *r, double *g, double *b)
1041 {
1042 h = 6.0 * pin0_1(h);
1043 s = pin0_1(s);
1044 v = pin0_1(v);
1045 int i = (int) floor(h);
1046 if (i > 5)
1047 i = 5; // Prevent problem if hue is exactly 1
1048 double f = h - i;
1049 double p = v * (1.0 - s);
1050 double q = v * (1.0 - s * f);
1051 double t = v * (1.0 - s * (1.0 - f));
1052 switch (i) {
1053 case 0:
1054 *r = v;
1055 *g = t;
1056 *b = p;
1057 break;
1058 case 1:
1059 *r = q;
1060 *g = v;
1061 *b = p;
1062 break;
1063 case 2:
1064 *r = p;
1065 *g = v;
1066 *b = t;
1067 break;
1068 case 3:
1069 *r = p;
1070 *g = q;
1071 *b = v;
1072 break;
1073 case 4:
1074 *r = t;
1075 *g = p;
1076 *b = v;
1077 break;
1078 case 5:
1079 default:
1080 *r = v;
1081 *g = p;
1082 *b = q;
1083 break;
1084 }
1085 }
1086
1087 // Is value missing? (No notice of whether x or y.)
1088 bool
gr_missing(double x)1089 gr_missing(double x)
1090 {
1091 //printf("gr_missing(%lf) ... ",x);
1092 if (_grMissingValueUsed) {
1093 //printf("GRI MISSING VALUE USED ... ");
1094 if (_grMissingValue == 0.0) // ignore tolerance if 0
1095 return (x == 0.0);
1096 if (x == _grMissingValue) { // speed up (?)
1097 //printf("GRI MISSING VALUE was exact match\n");
1098 return true;
1099 }
1100 x = GRI_ABS(x - _grMissingValue);
1101 if (x < _grMissingValue_tolerance) {
1102 //printf("GRI MISSING VALUE was match within tolerance\n");
1103 return true;
1104 } else {
1105 //printf("GRI MISSING VALUE no match within tolerance\n");
1106 return false;
1107 }
1108 } else {
1109 //printf("GRI MISSING VALUE no missing value used\n");
1110 return false;
1111 }
1112 }
1113
1114 // Is x-value missing? (Notice the x-transform.)
1115 bool
gr_missingx(double x)1116 gr_missingx(double x)
1117 {
1118 extern gr_axis_properties _grTransform_x;
1119 if (_grTransform_x == gr_axis_LOG && x <= 0.0)
1120 return true;
1121 if (_grMissingValue == 0.0)
1122 return (x == 0.0);
1123 if (x == _grMissingValue)
1124 return true;
1125 x = GRI_ABS(x - _grMissingValue);
1126 if (x < _grMissingValue_tolerance)
1127 return true;
1128 else
1129 return false;
1130 }
1131
1132 // Is y-value missing?
1133 bool
gr_missingy(double y)1134 gr_missingy(double y)
1135 {
1136 extern gr_axis_properties _grTransform_y;
1137 if (_grTransform_y == gr_axis_LOG && y <= 0.0)
1138 return true;
1139 if (_grMissingValue == 0.0)
1140 return (y == 0.0);
1141 if (y == _grMissingValue)
1142 return true;
1143 y = GRI_ABS(y - _grMissingValue);
1144 if (y < _grMissingValue_tolerance)
1145 return true;
1146 else
1147 return false;
1148 }
1149
1150 // Move the pen to new location given in points
1151 void
gr_moveto_cm(double x_cm,double y_cm)1152 gr_moveto_cm(double x_cm, double y_cm)
1153 {
1154 double x_pt = x_cm * PT_PER_CM;
1155 double y_pt = y_cm * PT_PER_CM;
1156 extern FILE *_grPS;
1157 // take care of postscript stuff
1158 extern output_file_type _output_file_type;
1159 if (_output_file_type == postscript) {
1160 if (_grWritePS) {
1161 if (_grPathExists) { // a path exists
1162 fprintf(_grPS, PS_stroke);
1163 fprintf(_grPS, PS_weak_newpath);
1164 fprintf(_grPS, PS_moveto, x_pt, y_pt);
1165 } else { // make new path
1166 _grPathExists = true;
1167 fprintf(_grPS, PS_weak_newpath);
1168 fprintf(_grPS, PS_moveto, x_pt, y_pt);
1169 }
1170 }
1171 _grCurrentPoint_x = x_pt;
1172 _grCurrentPoint_y = y_pt;
1173 }
1174 }
1175
1176 // See gr_onxpage_cm() -- see if point is on the page
1177 bool
gr_onxpage_cm(double x_cm)1178 gr_onxpage_cm(double x_cm)
1179 {
1180 return ((x_cm >= 0.0 && x_cm <= _grPageWidth_cm) ? true : false);
1181 }
1182
1183 // See if point is on the page
1184 bool
gr_onypage_cm(double y_cm)1185 gr_onypage_cm(double y_cm)
1186 {
1187 return ((y_cm >= 0.0 && y_cm <= _grPageHeight_cm) ? true : false);
1188 }
1189
1190 // gr_record_handle() -- record a handle in the Postscript file (for later
1191 // use by X window previewer, etc)
1192 void
gr_record_handle(double x_cm,double y_cm)1193 gr_record_handle(double x_cm, double y_cm)
1194 {
1195 sprintf(grTempString, "^ handle %g %g\n", x_cm, y_cm);
1196 gr_comment(grTempString);
1197 }
1198
1199 // gr_record_scale() -- record the scale in the Postscript file (for later
1200 // use by X window previewer, etc)
1201 void
gr_record_scale()1202 gr_record_scale()
1203 {
1204 extern output_file_type _output_file_type;
1205 if (_output_file_type == postscript) {
1206 fprintf(_grPS, "%%^ scale %d %g %g %g %d %g %g %g\n",
1207 _grTransform_x, _grxl_pt, _grxl, _grPtPerUser_x,
1208 _grTransform_y, _gryb_pt, _gryb, _grPtPerUser_y);
1209 }
1210 }
1211
1212 // rgb values each in range 0 to 1; result in same range
1213 void
gr_rgb2cmyk(double R[],double G[],double B[],unsigned int n,double c[],double m[],double y[],double k[])1214 gr_rgb2cmyk(double R[], double G[], double B[],
1215 unsigned int n,
1216 double c[], double m[], double y[], double k[])
1217 {
1218 double Cp, Mp, Yp;
1219 for( unsigned int i = 0; i < n; i += 1 ) {
1220 Cp = 1.0 - R[i];
1221 Mp = 1.0 - G[i];
1222 Yp = 1.0 - B[i];
1223 k[i] = Cp; if (Mp < k[i]) k[i] = Mp; if (Yp < k[i]) k[i] = Yp; // min
1224 if (k[i] == 1.0) {
1225 c[i] = 0.0;
1226 m[i] = 0.0;
1227 y[i] = 0.0;
1228 } else {
1229 c[i] = (Cp - k[i]) / (1.0 - k[i]);
1230 m[i] = (Mp - k[i]) / (1.0 - k[i]);
1231 y[i] = (Yp - k[i]) / (1.0 - k[i]);
1232 }
1233 }
1234 }
1235
1236 /*
1237 * (red, green, blue) in range from 0 to 1 is translated to (hue, saturation,
1238 * brightness) in same range.
1239 *
1240 * NOTE: false checking on range of (r,g,b) is done, and strange results may result
1241 * outside this range.
1242 *
1243 * Algorithm: Foley + Van Dam
1244 */
1245 void
gr_rgb2hsb(double r,double g,double b,double * h,double * s,double * br)1246 gr_rgb2hsb(double r, double g, double b, double *h, double *s, double *br)
1247 {
1248 double mx, mn;
1249 double rc, gc, bc;
1250 mx = r;
1251 if (g > mx)
1252 mx = g;
1253 if (b > mx)
1254 mx = b;
1255 mn = r;
1256 if (g < mn)
1257 mn = g;
1258 if (b < mn)
1259 mn = b;
1260 *br = mx;
1261 if (mx)
1262 *s = (mx - mn) / mx;
1263 else
1264 *s = 0.0;
1265 if (*s) {
1266 rc = (mx - r) / (mx - mn);
1267 gc = (mx - g) / (mx - mn);
1268 bc = (mx - b) / (mx - mn);
1269 if (r == mx)
1270 *h = bc - gc;
1271 else if (g == mx)
1272 *h = 2.0 + rc - bc;
1273 else if (b == mx)
1274 *h = 4.0 + gc - rc;
1275 *h /= 6.0;
1276 if (*h < 0.0)
1277 *h = *h + 1.0;
1278 } else {
1279 *h = 0.0; /* doesn't matter, since black anyway */
1280 }
1281 }
1282
1283 // Move the pen to new location given in cm
1284 void
gr_rmoveto_cm(double rx_cm,double ry_cm)1285 gr_rmoveto_cm(double rx_cm, double ry_cm)
1286 {
1287 rx_cm *= PT_PER_CM;
1288 ry_cm *= PT_PER_CM;
1289 gr_rmoveto_pt(rx_cm, ry_cm);
1290 }
1291
1292 // Move the pen to new location given in pt
1293 void
gr_rmoveto_pt(double rx_pt,double ry_pt)1294 gr_rmoveto_pt(double rx_pt, double ry_pt)
1295 {
1296 extern FILE *_grPS;
1297 if (!_grPathExists)
1298 warning("IGNORING gr_rmoveto() NOT FOLLOWING gr_moveto()");
1299 else {
1300 _grCurrentPoint_x += rx_pt;
1301 _grCurrentPoint_y += ry_pt;
1302 if (_grWritePS)
1303 fprintf(_grPS, PS_rmoveto, rx_pt, ry_pt);
1304 }
1305 }
1306
1307 //
1308 // change to putting output into named file
1309 bool
gr_reopen_postscript(const char * new_name)1310 gr_reopen_postscript(const char *new_name)
1311 {
1312 FILE *new_file;
1313 int i = 0;
1314 fflush(_grPS);
1315 rewind(_grPS);
1316 if ((new_file = fopen((char *) new_name, "w")) == NULL)
1317 return false; // couldn't open it
1318 while (!feof(_grPS)) {
1319 i++;
1320 if (NULL == fgets(grTempString, _grTempStringLEN, _grPS))
1321 break;
1322 if (feof(_grPS))
1323 break;
1324 fputs(grTempString, new_file);
1325 }
1326 fclose(_grPS);
1327 #ifdef VMS
1328 sprintf(grTempString, "DEL %s;*", gr_currentPSfilename());
1329 call_the_OS(grTempString, __FILE__, __LINE__);
1330 strcpy(ps_filename_used, new_name);
1331 #else
1332 sprintf(grTempString, "rm -f %s", gr_currentPSfilename());
1333 call_the_OS(grTempString, __FILE__, __LINE__);
1334 strcpy(ps_filename_used, new_name);
1335 #endif
1336 _grPS = new_file;
1337 return true;
1338 }
1339
1340 // gr_save_postscript() -- save postscript file SYNOPSIS void
1341 // gr_save_postscript(char *PS_name, int normal_scale) DESCRIPTION: Saves the
1342 // postscript commands into the file named 'PS_name'. If normal_scale==1,
1343 // it's at normal scale; otherwise, it's at screen scale
1344 void
gr_save_postscript(const char * PS_name,int normal_scale)1345 gr_save_postscript(const char *PS_name, int normal_scale)
1346 {
1347 FILE *PS_file;
1348 // Rewind gr file. Then skip header
1349 rewind(_grPS);
1350 skip_ps_header(_grPS);
1351 // Open the postscript output file, then write header
1352 if ((PS_file = fopen((char *) PS_name, "w")) == NULL) {
1353 sprintf(grTempString, "Can't open postscript file `%s'", PS_name);
1354 gr_Error(grTempString);
1355 }
1356
1357 extern rectangle _bounding_box;
1358 printf("%s%d gr_save_postscript() ... bounding box %f %f %f %f\n",
1359 __FILE__, __LINE__,
1360 _bounding_box.llx(),
1361 _bounding_box.lly(),
1362 _bounding_box.urx(),
1363 _bounding_box.ury());
1364
1365 if (normal_scale) {
1366 double old_grMagx = _grMagx, old_grMagy = _grMagy;
1367 double old_grOriginx = _grOriginx, old_grOriginy = _grOriginy;
1368 _grMagx = 1.0;
1369 _grMagy = 1.0;
1370 _grOriginx = 0.0;
1371 _grOriginy = 0.0;
1372 insert_ps_header(PS_file, true);
1373 _grMagx = old_grMagx;
1374 _grMagy = old_grMagy;
1375 _grOriginx = old_grOriginx;
1376 _grOriginy = old_grOriginy;
1377 } else
1378 insert_ps_header(PS_file, true);
1379 while (!feof(_grPS)) {
1380 if (NULL == fgets(grTempString, _grTempStringLEN, _grPS))
1381 break;
1382 if (feof(_grPS))
1383 break;
1384 fputs(grTempString, PS_file);
1385 }
1386 fclose(PS_file);
1387 } // gr_save_postscript()
1388
1389 static void
skip_ps_header(FILE * PSfile)1390 skip_ps_header(FILE * PSfile)
1391 {
1392 char S[256];
1393 int header_lines;
1394 /*
1395 * Skip header if it exists
1396 */
1397 if (NULL == fgets(S, 256, PSfile))
1398 gr_Error("PostScript file is empty");
1399 if (feof(PSfile))
1400 gr_Error("PostScript file is empty");
1401 if (!strcmp(S, "%!\n")) { /* old-style */
1402 if (NULL == fgets(S, 256, PSfile))
1403 gr_Error("PostScript file is nearly empty");
1404 if (feof(PSfile))
1405 gr_Error("PostScript file is nearly empty");
1406 if (1 == sscanf(&S[1], "%d header lines follow\n",
1407 &header_lines)) {
1408 while (header_lines-- > 0) {
1409 if (NULL == fgets(S, 256, PSfile))
1410 gr_Error("badly formed (old-style) header");
1411 if (feof(PSfile))
1412 gr_Error("badly formed (old-style) header");
1413 }
1414 } else {
1415 gr_Error("badly formed (old-style) header");
1416 }
1417 } else if (!strcmp(S, "%!PS-Adobe-1.0\n")) /* new-style */
1418 while (strncmp(S, "%%Page:", 7)) {
1419 if (NULL == fgets(S, 256, PSfile))
1420 gr_Error("badly formed header");
1421 if (feof(PSfile))
1422 gr_Error("badly formed header");
1423 }
1424 }
1425
1426 bool
gr_using_missing_value()1427 gr_using_missing_value()
1428 {
1429 return _grMissingValueUsed;
1430 }
1431 void
gr_set_missing_value(double x)1432 gr_set_missing_value(double x)
1433 {
1434 _grMissingValueUsed = true;
1435 _grMissingValue = x;
1436 _grMissingValue_tolerance = 0.0001 * GRI_ABS(x);
1437 }
1438 void
gr_set_missing_value_none()1439 gr_set_missing_value_none()
1440 {
1441 //printf("%s:%d TURNING OFF missing value.\n",__FILE__,__LINE__);
1442 _grMissingValueUsed = false;
1443 }
1444
1445 /*
1446 * gr_setxaxisstyle(int xstyle) Set style for x axis. Presently there are no
1447 * options, so xstyle is ignored.
1448 */
1449 void
gr_setxaxisstyle(int xstyle)1450 gr_setxaxisstyle(int xstyle)
1451 {
1452 _grAxisStyle_x = xstyle;
1453 }
1454
1455 /*
1456 * gr_setyaxisstyle(int ystyle) Set style for y axis. If ystyle = 0 (the
1457 * default), the y label is written vertically. If ystyle = 1, the y label
1458 * is written horizontally.
1459 */
1460 void
gr_setyaxisstyle(int ystyle)1461 gr_setyaxisstyle(int ystyle)
1462 {
1463 _grAxisStyle_y = ystyle;
1464 }
1465
1466 /*
1467 * gr_setscale() -- set scale for PostScript
1468 */
1469 void
gr_setscale(double xfactor,double yfactor)1470 gr_setscale(double xfactor, double yfactor)
1471 {
1472 fprintf(_grPS, "%g %g scale\n", xfactor, yfactor);
1473 }
1474
1475 /*
1476 * gr_setsymbolsize_pt() -- set symbol size in pt
1477 */
1478 void
gr_setsymbolsize_pt(double size_pt)1479 gr_setsymbolsize_pt(double size_pt)
1480 {
1481 _grSymbolSize_pt = size_pt;
1482 }
1483
1484 // Specify whether tics point in
1485 void
gr_setticdirection(bool tics_point_in)1486 gr_setticdirection(bool tics_point_in)
1487 {
1488 _grTicsPointIn = tics_point_in ? true : false;
1489 }
1490
1491 /*
1492 * gr_setticsize_cm() -- set tic size SYNOPSIS void gr_setticsize_cm(double
1493 * new_size) DESCRIPTION: Sets the size of tics on further axis drawing to
1494 * 'new_size',in cm.
1495 */
1496 void
gr_setticsize_cm(double new_size)1497 gr_setticsize_cm(double new_size)
1498 {
1499 _grTicSize_cm = new_size;
1500 }
1501
1502 /*
1503 * gr_settranslate() -- set translation (x,y) for PostScript
1504 */
1505 void
gr_settranslate(double xcm,double ycm)1506 gr_settranslate(double xcm, double ycm)
1507 {
1508 fprintf(_grPS, "%g %g translate\n", xcm * PT_PER_CM, ycm * PT_PER_CM);
1509 }
1510
1511 // gr_setup_creatorname() -- set up creator name for PostScript prolog
1512 // SYNOPSIS void gr_setup_creatorname(char *s) DESCRIPTION: Sets up
1513 // creator name for PostScript prolog
1514 void
gr_setup_creatorname(const char * s)1515 gr_setup_creatorname(const char *s)
1516 {
1517 strcat(creator_name, s);
1518 }
1519
1520 // gr_setup_ps_filename() -- name file for postscript output
1521 // DESCRIPTION: Set the name for the
1522 // postscript output. If this function isn't called prior to gr_begin(), the
1523 // filename will be 'gri-00.ps'. As with all gr functions begining with the
1524 // letters 'gr_setup_ps_', this has an effect ONLY if it preceeds plotting
1525 // calls which affect the pen.
1526 void
gr_setup_ps_filename(const char * new_name)1527 gr_setup_ps_filename(const char *new_name)
1528 {
1529 //printf("%s:%d gr_setup_ps_filename(%s)\n",__FILE__,__LINE__,new_name);
1530 if (_grNeedBegin) {
1531 user_gave_ps_filename = true;
1532 if (strlen(new_name) > 0)
1533 strcpy(ps_filename, new_name);
1534 else
1535 strcpy(ps_filename, "gri.ps");
1536 }
1537 }
1538
1539 /*
1540 * gr_setup_ps_landscape() -- set ps printer to landscape mode
1541 *
1542 * NOTE: Must call before gr_begin()
1543 */
1544 void
gr_setup_ps_landscape()1545 gr_setup_ps_landscape()
1546 {
1547 double t0 = _grPageWidth_cm;
1548 _grPS_Landscape = true;
1549 _grPageWidth_cm = _grPageHeight_cm;
1550 _grPageHeight_cm = t0;
1551 }
1552 void
gr_setup_ps_portrait()1553 gr_setup_ps_portrait()
1554 {
1555 //double t0 = _grPageWidth_cm;
1556 _grPS_Landscape = false;
1557 //_grPageWidth_cm = _grPageHeight_cm;
1558 //_grPageHeight_cm = t0;
1559 }
1560
1561
1562 /*
1563 * gr_setup_ps_scale() -- set enlargement factors for x and y on ps printer
1564 * SYNOPSIS void gr_setup_ps_scale(double xfactor,double yfactor)
1565 * DESCRIPTION: Scales the plot on the page,by multiplying by the factors
1566 * 'xfactor' and 'yfactor'. Note that x and y are exchanged on page if
1567 * gr_setup_grPS_Landscape() is also called. NOTE: Must call before
1568 * gr_begin().
1569 */
1570 void
gr_setup_ps_scale(double xfactor,double yfactor)1571 gr_setup_ps_scale(double xfactor, double yfactor)
1572 {
1573 _grPSScale_x = xfactor;
1574 _grPSScale_y = yfactor;
1575 _grPageWidth_cm *= xfactor;
1576 _grPageHeight_cm *= yfactor;
1577 }
1578
1579 // gr_setxlabel() -- set name for x-axis
1580 void
gr_setxlabel(const char * xlab)1581 gr_setxlabel(const char *xlab)
1582 {
1583 strncpy(_grXAxisLabel, xlab, LEN_lab);
1584 }
1585
1586 // gr_setxnumberformat() -- set format for numbers
1587 void
gr_setxnumberformat(const char * xformat)1588 gr_setxnumberformat(const char *xformat)
1589 {
1590 strncpy(_grNumFormat_x, xformat, LEN_num);
1591 }
1592
1593 // gr_setxpagesize_cm() -- set page size for x
1594 // DESCRIPTION: Sets page size to 'x_cm'.
1595 // Future x pen locations should be in range [0,x_cm]. If 'x_cm'<0,use
1596 // default of 21.59.
1597 void
gr_setxpagesize_cm(double x_cm)1598 gr_setxpagesize_cm(double x_cm)
1599 {
1600 if (x_cm <= 0.0) {
1601 double default_value = 21.59;
1602 sprintf(grTempString,
1603 "(gr_setxpagesize_cm): USED %g INSTEAD OF 'x_cm'=%g",
1604 default_value, x_cm);
1605 warning(grTempString);
1606 _grPageWidth_cm = default_value;
1607 } else {
1608 _grPageWidth_cm = x_cm;
1609 }
1610 }
1611
1612 // gr_setxscale() -- set the scale for x
1613 void
gr_setxscale(double xl_cm,double xr_cm,double xl,double xr)1614 gr_setxscale(double xl_cm, double xr_cm, double xl, double xr)
1615 {
1616 // XREF -- axis transform
1617 /* set up scale */
1618 _grxl_pt = xl_cm * PT_PER_CM;
1619 switch (_grTransform_x) {
1620 case gr_axis_LINEAR:
1621 _grxl = xl;
1622 _grCmPerUser_x = (xr_cm - xl_cm) / (xr - xl);
1623 #if 1
1624 _ll_x_pt = xl_cm * PT_PER_CM;
1625 _ur_x_pt = xr_cm * PT_PER_CM;
1626 _ll_x_us = xl;
1627 _ur_x_us = xr;
1628 #endif
1629 break;
1630 case gr_axis_LOG:
1631 if (xl <= 0.0 || xr <= 0.0) {
1632 gr_Error("Log axis requires x values to exceed 0.\n");
1633 }
1634 _grxl = xl;
1635 _grCmPerUser_x = (xr_cm - xl_cm) / log10(xr / xl);
1636 break;
1637 default:
1638 gr_Error("unknown axis transformation (internal error)");
1639 break;
1640 }
1641 _grPtPerUser_x = _grCmPerUser_x * PT_PER_CM;
1642 /* test for weirdness */
1643 double error_test = GRI_ABS(_grxl_pt / PT_PER_CM - xr_cm);
1644 if (error_test < 0.1) {
1645 sprintf(grTempString,
1646 "(gr_setxscale): _grxl_pt/PT_PER_CM=%g VERY CLOSE TO xr_cm=%g",
1647 _grxl_pt / PT_PER_CM, xr_cm);
1648 warning(grTempString);
1649 }
1650 if (error_test == 0.0) {
1651 sprintf(grTempString,
1652 "(gr_setxscale): _grxl_pt/PT_PER_CM = %g == xr_cm=%g",
1653 _grxl_pt / PT_PER_CM, xr_cm);
1654 warning(grTempString);
1655 }
1656 }
1657
1658 /*
1659 * gr_setxsubdivisions() -- set # subdivisions on x-axis SYNOPSIS void
1660 * gr_setxsubdivisions(int num) DESCRIPTION: Sets the number of subdivisions
1661 * between numbered tics to 'num'
1662 */
1663 void
gr_setxsubdivisions(int num)1664 gr_setxsubdivisions(int num)
1665 {
1666 if (num > 0 && num < 100)
1667 _grNumSubDiv_x = num;
1668 else
1669 _grNumSubDiv_x = 1;
1670 }
1671
1672 // gr_setxtransform() -- set transform for x-mapping
1673 void
gr_setxtransform(gr_axis_properties xstyle)1674 gr_setxtransform(gr_axis_properties xstyle)
1675 {
1676 // XREF -- axis transform
1677 _grTransform_x = xstyle;
1678 }
1679
1680 // gr_setylabel() -- set name for y-axis
1681 void
gr_setylabel(const char * ylab)1682 gr_setylabel(const char *ylab)
1683 {
1684 strncpy(_grYAxisLabel, ylab, LEN_lab);
1685 }
1686
1687 // gr_setynumberformat() -- set format for numbers on y-axis
1688 void
gr_setynumberformat(const char * yformat)1689 gr_setynumberformat(const char *yformat)
1690 {
1691 strncpy(_grNumFormat_y, yformat, LEN_num);
1692 }
1693
1694 // gr_setypagesize_cm() -- set page size for y
1695 void
gr_setypagesize_cm(double y_cm)1696 gr_setypagesize_cm(double y_cm)
1697 {
1698 if (y_cm <= 0.0) {
1699 double default_value = 27.94;
1700 sprintf(grTempString,
1701 "(gr_setypagesize_cm): USED %g INSTEAD OF 'y_cm'=%g",
1702 default_value, y_cm);
1703 warning(grTempString);
1704 _grPageHeight_cm = default_value;
1705 } else {
1706 _grPageHeight_cm = y_cm;
1707 }
1708 }
1709
1710 // gr_setyscale() -- set the scale for y
1711 void
gr_setyscale(double yb_cm,double yt_cm,double yb,double yt)1712 gr_setyscale(double yb_cm, double yt_cm, double yb, double yt)
1713 {
1714 // XREF -- axis transform
1715 /* set up scale */
1716 _gryb_pt = yb_cm * PT_PER_CM;
1717 switch (_grTransform_y) {
1718 case gr_axis_LINEAR:
1719 _gryb = yb;
1720 _grCmPerUser_y = (yt_cm - yb_cm) / (yt - yb);
1721 #if 1
1722 _ll_y_pt = yb_cm * PT_PER_CM;
1723 _ur_y_pt = yt_cm * PT_PER_CM;
1724 _ll_y_us = yb;
1725 _ur_y_us = yt;
1726 #endif
1727 break;
1728 case gr_axis_LOG:
1729 if (yb <= 0.0 || yt <= 0.0) {
1730 gr_Error("Log axis requires y values to exceed 0.\n");
1731 }
1732 _gryb = yb;
1733 _grCmPerUser_y = (yt_cm - yb_cm) / log10(yt / yb);
1734 break;
1735 default:
1736 gr_Error("unknown y transform"); // impossible to get here
1737 break;
1738 }
1739 _grPtPerUser_y = _grCmPerUser_y * PT_PER_CM;
1740 /* test for weirdness */
1741 double error_test = GRI_ABS(_gryb_pt / PT_PER_CM - yt_cm);
1742 if (error_test < 0.1) {
1743 sprintf(grTempString,
1744 "(gr_setyscale): _gryb_pt/PT_PER_CM=%g VERY CLOSE TO yt_cm=%g",
1745 _gryb_pt / PT_PER_CM, yt_cm);
1746 warning(grTempString);
1747 }
1748 if (error_test == 0.0) {
1749 sprintf(grTempString,
1750 "(gr_setyscale): _gryb_pt/PT_PER_CM=%g == yt_cm=%g",
1751 _gryb_pt / PT_PER_CM, yt_cm);
1752 warning(grTempString);
1753 }
1754 } /* end of gr_setyscale() */
1755
1756 /*
1757 * gr_setysubdivisions() -- set # subdivisions on y-axis SYNOPSIS void
1758 * gr_setysubdivisions(int num) DESCRIPTION: Corresponds to
1759 * gr_setxsubdivisions().
1760 */
1761 void
gr_setysubdivisions(int num)1762 gr_setysubdivisions(int num)
1763 {
1764 if (num > 0 && num < 100)
1765 _grNumSubDiv_y = num;
1766 else
1767 _grNumSubDiv_y = 1;
1768 }
1769
1770 // gr_setytransform() -- set transform for y-mapping
1771 void
gr_setytransform(gr_axis_properties ystyle)1772 gr_setytransform(gr_axis_properties ystyle)
1773 {
1774 _grTransform_y = ystyle;
1775 }
1776
1777 // gr_showpage() -- print this page, and start a new one
1778 void
gr_showpage()1779 gr_showpage()
1780 {
1781 fprintf(_grPS, PS_showpage);
1782 handle_landscape_scale(_grPS);
1783 which_page++;
1784 fprintf(_grPS, "grestore\n%%%%Page: %d %d\ngsave\n", which_page, which_page);
1785 #if 0 // as per PSG change, 98-oct-1
1786 fprintf(_grPS, "/Helvetica findfont 12.00 sc sf\n"); // need some font (guess this one)!
1787 #endif
1788 }
1789
1790 // Convert user units to cm on page,, using gr_usertopt
1791 void
gr_usertocm(double x,double y,double * x_cm,double * y_cm)1792 gr_usertocm(double x, double y, double *x_cm, double *y_cm)
1793 {
1794 gr_usertopt(x, y, x_cm, y_cm);
1795 *x_cm /= PT_PER_CM;
1796 *y_cm /= PT_PER_CM;
1797 }
1798 double
gr_usertocm_x(double x,double y)1799 gr_usertocm_x(double x, double y)
1800 {
1801 double x_cm, y_cm;
1802 gr_usertopt(x, y, &x_cm, &y_cm);
1803 return (x_cm / PT_PER_CM);
1804 }
1805 double
gr_usertocm_y(double x,double y)1806 gr_usertocm_y(double x, double y)
1807 {
1808 double x_cm, y_cm;
1809 gr_usertopt(x, y, &x_cm, &y_cm);
1810 return (y_cm / PT_PER_CM);
1811 }
1812
1813 /*
1814 * gr_usertopt() -- convert user units to points on the page. This is a base
1815 * routine used by all drawing routines.
1816 *
1817 * Adding projections: change this, also gr_cmtouser(), also
1818 * show_axesCmd()/show.c.
1819 *
1820 *
1821 * Known projections:
1822 *
1823 * LINEAR: scale to page units, then plot.
1824 *
1825 * LOG: log transform, scale to page units, then plot.
1826 *
1827 */
1828 void
gr_usertopt(double x,double y,double * x_pt,double * y_pt)1829 gr_usertopt(double x, double y, double *x_pt, double *y_pt)
1830 {
1831 // XREF -- axis transform
1832 double xx, yy;
1833 switch (_grTransform_x) {
1834 case gr_axis_LINEAR:
1835 #if NEW_TRANSFORM
1836 *x_pt = interpolate_linear(x, _ll_x_us, _ll_x_pt, _ur_x_us, _ur_x_pt);
1837 #else
1838 *x_pt = _grxl_pt + (x - _grxl) * _grPtPerUser_x;
1839 #endif
1840 break;
1841 case gr_axis_LOG:
1842 if (x <= 0.0) {
1843 sprintf(_grTempString, "can't use negative x (%g) with LOG axis.", x);
1844 gr_Error(_grTempString);
1845 } else {
1846 xx = log10(x / _grxl);
1847 *x_pt = _grxl_pt + xx * _grPtPerUser_x;
1848 }
1849 break;
1850 default:
1851 gr_Error("unknown x transform"); // impossible to get here
1852 }
1853 switch (_grTransform_y) {
1854 case gr_axis_LINEAR:
1855 #if NEW_TRANSFORM
1856 *y_pt = interpolate_linear(y, _ll_y_us, _ll_y_pt, _ur_y_us, _ur_y_pt);
1857 #else
1858 *y_pt = _gryb_pt + (y - _gryb) * _grPtPerUser_y;
1859 #endif
1860 break;
1861 case gr_axis_LOG:
1862 if (y <= 0.0) {
1863 sprintf(_grTempString, "can't use negative y (%g) with LOG axis.", y);
1864 gr_Error(_grTempString);
1865 } else {
1866 yy = log10(y / _gryb);
1867 *y_pt = _gryb_pt + yy * _grPtPerUser_y;
1868 }
1869 break;
1870 default:
1871 gr_Error("unknown y transform");
1872 }
1873 }
1874
1875 void
gr_set_clip_ps_off()1876 gr_set_clip_ps_off()
1877 {
1878 if (_clipping_postscript) {
1879 fprintf(_grPS, "S Q %% turn clipping off\n");
1880 check_psfile();
1881 _clipping_postscript = false;
1882 }
1883 }
1884
1885 void
gr_set_clip_ps_rect(double ll_x_pt,double ll_y_pt,double ur_x_pt,double ur_y_pt)1886 gr_set_clip_ps_rect(double ll_x_pt, double ll_y_pt, double ur_x_pt, double ur_y_pt)
1887 {
1888 extern output_file_type _output_file_type;
1889 if (_output_file_type == svg) {
1890 warning("cannot yet clip in svg files");
1891 } else if (_output_file_type == postscript) {
1892 if (_clipping_postscript) {
1893 fprintf(_grPS, "S Q %% turn existing clipping off since user forgot to\n");
1894 check_psfile();
1895 }
1896 _clip_ps_xleft = SMALLER_ONE(ll_x_pt, ur_x_pt);
1897 _clip_ps_ybottom = SMALLER_ONE(ll_y_pt, ur_y_pt);
1898 _clip_ps_xright = LARGER_ONE(ll_x_pt, ur_x_pt);
1899 _clip_ps_ytop = LARGER_ONE(ll_y_pt, ur_y_pt);
1900
1901 /*DEK*/ //printf("DEBUG: am setting clip to xl=%f xr=%f yb=%f yt=%f\n", _clip_ps_xleft, _clip_ps_xright, _clip_ps_ybottom, _clip_ps_ytop);
1902
1903 /*DEK*/
1904 // why do I not have "Q" to finish it off?
1905 // why do I not have "W*" to finish it off?
1906 /*DEK*/
1907 fprintf(_grPS, "q n\n");
1908 fprintf(_grPS, "%f %f moveto\n", _clip_ps_xleft, _clip_ps_ybottom);
1909 fprintf(_grPS, "%f %f lineto\n", _clip_ps_xright, _clip_ps_ybottom);
1910 fprintf(_grPS, "%f %f lineto\n", _clip_ps_xright, _clip_ps_ytop);
1911 fprintf(_grPS, "%f %f lineto\n", _clip_ps_xleft, _clip_ps_ytop);
1912 fprintf(_grPS, "h W\n");
1913 fprintf(_grPS, "n %% turn clipping on\n");
1914 check_psfile();
1915 _clipping_is_postscript_rect = true;
1916 //printf("%s:%d set RECT ps clip\n",__FILE__,__LINE__);
1917 _clipping_postscript = true;
1918 }
1919 }
1920
1921 void
gr_set_clip_ps_curve(const double * xc,const double * yc,unsigned int len)1922 gr_set_clip_ps_curve(const double *xc, const double *yc, unsigned int len)
1923 {
1924 if (_clipping_postscript) {
1925 fprintf(_grPS, "S Q %% `set clip to curve' first must turn remnant clipping off\n");
1926 check_psfile();
1927 }
1928 fprintf(_grPS, "q n %% `set clip to curve' setting clipping on\n");
1929 bool need_moveto = true;
1930 for (unsigned int i = 0; i < len; i++) {
1931 if (!gr_missingx(double(*xc)) && !gr_missingy(double(*yc))) {
1932 double xpt, ypt;
1933 gr_usertopt(*xc, *yc, &xpt, &ypt);
1934 if (need_moveto)
1935 fprintf(_grPS, "%f %f moveto\n", xpt, ypt);
1936 else
1937 fprintf(_grPS, "%f %f lineto\n", xpt, ypt);
1938 need_moveto = false;
1939 } else {
1940 need_moveto = true;
1941 }
1942 xc++;
1943 yc++;
1944 }
1945 fprintf(_grPS, "h W\n");
1946 fprintf(_grPS, "n %% turn clipping on\n");
1947 check_psfile();
1948 //printf("%s:%d set CURVE ps clip\n",__FILE__,__LINE__);
1949 _clipping_is_postscript_rect = false;
1950 _clipping_postscript = true;
1951 //_clipData = -1; // KEEP??
1952 }
1953