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