1 /*
2 Gri - A language for scientific graphics programming
3 Copyright (C) 2010 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 DEBUG_LABELLING 1
21 //#define DEBUG_LABELS 1
22 #include <string>
23 #include <string.h>
24 #if !defined(IS_MINGW32)
25 #include <strings.h>
26 #else
27 #define index strrchr
28 #endif
29
30 #include <math.h>
31 #include <stdlib.h>
32 #include "gr.hh"
33 #include "GriPath.hh"
34 #include "extern.hh"
35
36 #if defined(__DECCXX) || defined(OS_IS_BEOS)
37 extern "C" char *index(const char *s, int c);
38 #endif
39
40 #define TIC_RATIO 0.5 /* (Length small tic) / (large tic) */
41 extern char _grXAxisLabel[];
42 extern char _grYAxisLabel[];
43 extern char _grNumFormat_x[];
44 extern char _grNumFormat_y[];
45 extern char _grTempString[];
46 extern int _grAxisStyle_x;
47 extern int _grAxisStyle_y;
48 extern gr_axis_properties _grTransform_x;
49 extern gr_axis_properties _grTransform_y;
50 extern int _grNeedBegin;
51 extern int _grNumSubDiv_x;
52 extern int _grNumSubDiv_y;
53 extern double _grCmPerUser_x;
54 extern double _grCmPerUser_y;
55 extern double _grTicSize_cm;
56
57 #define round_down_to_10(x) (pow (10.0, floor (log10 ( (x) ))))
58 #define round_to_1(x) (floor (0.5 + (x) ))
59
60 /* Some twiddles */
61 #define AXIS_TWIDDLE 0.005 /* .05mm overhang on axis */
62 #define SMALLNUM (1.0e-3) /* twiddle axis range */
63 #define SMALLERNUM (1.0e-4) /* twiddle axis range */
64 #define SMALLFONTSIZE (1.0e-3) /* too small to draw */
65 /*
66 * local functions
67 */
68 static bool next_tic(double *next, double labelling, bool gave_labelling, double present, double final, double inc, gr_axis_properties axis_type, bool increasing);
69
70 #if 0
71 static int num_decimals(char *s);
72 static int create_labels(double y, double yb, double yinc, double yt, double smallinc, char *label[], int *num_label);
73 #endif
74
75
76 // Calculate next tic location on axis. Note that if the axis starts off
77 // ragged, this will assign to *next the first multiple of "inc". Returns
78 // true if more axis to do
79 static bool
next_tic(double * next,double labelling,bool gave_labelling,double present,double final,double inc,gr_axis_properties axis_type,bool increasing)80 next_tic(double *next, double labelling, bool gave_labelling, double present, double final, double inc, gr_axis_properties axis_type, bool increasing)
81 {
82 #if defined(DEBUG_LABELLING)
83 printf("%s:%d next_tic(...,labelling=%f gave_labelling=%s present=%f final=%f inc=%f increasing=%s _x_gave_labelling=%s _y_gave_labelling=%s\n", __FILE__,__LINE__,labelling,gave_labelling?"true":"false",present,final,inc,increasing?"true":"false", _x_gave_labelling?"true":"false", _y_gave_labelling?"true":"false");
84 #endif
85 double order_of_mag, mantissa;
86 // Check to see if already ran out of axis.
87 if (present >= final && increasing == true)
88 return false;
89 if (present <= final && increasing == false)
90 return false;
91 // Determine next tic to draw to, treating linear/log separately.
92 switch (axis_type) {
93 case gr_axis_LINEAR:
94 //*next = inc * (1.0 + floor((SMALLERNUM * inc + (present - labelling)) / inc));
95 if (gave_labelling) {
96 *next = labelling + inc * (1 + floor(SMALLERNUM + (present - labelling) / inc));
97 #if defined(DEBUG_LABELLING)
98 printf("%s:%d next=%f present=%f inc=%f\n %f %f %f %f\n",
99 __FILE__, __LINE__, *next, present, inc,
100 inc * (1.001 + floor((present - labelling) / inc)),
101 (1 + floor((present - labelling) / inc)),
102 (floor((present - labelling) / inc)),
103 (present - labelling) / inc);
104 #endif
105 } else {
106 *next = inc * (1.0 + floor((SMALLERNUM * inc + present) / inc));
107 }
108 break;
109 case gr_axis_LOG:
110 if (present <= 0.0) {
111 err("zero or negative on log axis (internal error).");
112 return false;
113 }
114 order_of_mag = round_down_to_10(present);
115 mantissa = present / order_of_mag;
116 if (increasing)
117 mantissa = ceil(mantissa + SMALLNUM);
118 else {
119 if (mantissa <= 1.0)
120 mantissa = 0.9;
121 else
122 mantissa = floor(mantissa - SMALLNUM);
123 }
124 *next = order_of_mag * mantissa;
125 break;
126 default:
127 gr_Error("unknown axis type (internal error)");
128 }
129 // Set flag if this will overrun axis.
130 if (increasing == true)
131 return (*next <= final) ? true : false;
132 else
133 return (*next >= final) ? true : false;
134 }
135
136 /*
137 * gr_drawxyaxes DESCRIPTION: Draws an x-y axis frame
138 */
139 void
gr_drawxyaxes(double xl,double xinc,double xr,double yb,double yinc,double yt)140 gr_drawxyaxes(double xl, double xinc, double xr, double yb, double yinc, double yt)
141 {
142 double old_fontsize_pt = gr_currentfontsize_pt();
143 gr_drawxaxis(yb, xl, xinc, xr, xl, gr_axis_BOTTOM);
144 gr_drawyaxis(xl, yb, yinc, yt, yb, gr_axis_LEFT);
145 gr_setfontsize_pt(0.0);
146 gr_drawxaxis(yt, xl, xinc, xr, xl, gr_axis_TOP);
147 gr_drawyaxis(xr, yb, yinc, yt, yb, gr_axis_RIGHT);
148 gr_setfontsize_pt(old_fontsize_pt);
149 }
150
151 // DESCRIPTION: The axis extends from 'xl' to 'xr',with numbers placed at
152 // intervals of 'xinc'.
153 // If 'side'==BOTTOM/TOP this is an axis designed to appear at the
154 // bottom/top of a plotting region (ie, the numbers are below/above).
155 void
gr_drawxaxis(double y,double xl,double xinc,double xr,double xlabelling,gr_axis_properties side)156 gr_drawxaxis(double y, double xl, double xinc, double xr, double xlabelling, gr_axis_properties side)
157 {
158 //printf("gr_drawxaxis(..., xl=%f xlabelling=%f same=%d\n", xl, xlabelling, xl==xlabelling);
159 bool user_gave_labels = (_x_labels.size() != 0);
160 #ifdef DEBUG_LABELS
161 if (user_gave_labels) {
162 printf("DEBUG: %s:%d x axis should have labels [", __FILE__,__LINE__);
163 for (unsigned int i = 0; i < _x_labels.size(); i++)
164 printf("'%s' ", _x_labels[i].c_str());
165 printf("] at positions [");
166 for (unsigned int i = 0; i < _x_label_positions.size(); i++)
167 printf("%f ", _x_label_positions[i]);
168 printf("]\n");
169 }
170 #endif
171 GriString label;
172 std::string slabel;
173 extern char _xtype_map;
174 double CapHeight = gr_currentCapHeight_cm();
175 double angle = 0.0; // angle of axis tics, labels, etc
176 #ifdef DEBUG_LABELS
177 printf("DEBUG: %s:%d at top of gr_drawxaxis(), angle is %f\n", __FILE__, __LINE__, angle);
178 #endif
179 bool increasing = ((xr > xl) ? true : false);
180 double tic, tic_sml; // length of tic marks
181 double xcm, ycm; // tmp
182 double offset; // for numbers
183 double present, next, final = xr, smallinc = 0.0;
184 int decade_between_labels; // for log axes
185 double tmp1, tmp2;
186 GriPath axis_path;
187 // XREF -- axis transform
188 // Calculate size of large and small tic marks.
189 extern bool _grTicsPointIn;
190 tic = ((side == gr_axis_LEFT && _grTicsPointIn == false)
191 || (side == gr_axis_RIGHT && _grTicsPointIn == true))
192 ? -_grTicSize_cm : _grTicSize_cm;
193 if (_grTransform_x == gr_axis_LOG && _xsubdiv < 0)
194 tic_sml = 0.0;
195 else
196 tic_sml = TIC_RATIO * tic;
197 // Calculate offset = space for numbers.
198 offset = 0.0;
199 int old_linecap = _griState.line_cap();
200 int old_linejoin = _griState.line_join();
201 _griState.set_line_cap(0);
202 _griState.set_line_join(0);
203 switch (_grTransform_x) {
204 case gr_axis_LINEAR:
205 case gr_axis_LOG:
206 if (side == gr_axis_BOTTOM) {
207 if (strlen(_grNumFormat_x))
208 offset -= 1.75 * CapHeight;
209 offset -= ((_grTicsPointIn == false) ? _grTicSize_cm : 0.0);
210 } else {
211 if (strlen(_grNumFormat_x))
212 offset += 0.75 * CapHeight;
213 offset += ((_grTicsPointIn == false) ? _grTicSize_cm : 0.0);
214 }
215 break;
216 default:
217 gr_Error("unknown axis type (internal error)");
218 }
219 // Draw axis, moving from tic to tic. Tics are advanced by smallinc for
220 // linear axes and by something appropriate for log axes. Whenever the
221 // current location should be a big tic, such a tic is drawn along with a
222 // label.
223 double xl_cm, y_cm;
224 int this_pass=0, pass_max=5000;
225 switch (_grTransform_x) {
226 case gr_axis_LINEAR:
227 smallinc = xinc / _grNumSubDiv_x;
228 // Twiddle axes to extend a bit beyond the requested
229 // region, to prevent rounding problems.
230 present = xl - xinc / 1.0e3;
231 final = xr + xinc / 1.0e3;
232 // Draw x-axis, moving from tic to tic. Tics are advanced by
233 // smallinc for linear axes and by something appropriate for log
234 // axes. Whenever the current location should be a big tic, such a
235 // tic is drawn along with a label.
236 gr_usertocm(xl, y, &xl_cm, &y_cm);
237 axis_path.push_back(xl_cm, y_cm, 'm');
238 #if defined(DEBUG_LABELLING)
239 printf("%s:%d _x_gave_labelling=%d xlabelling=%f present=%f\n", __FILE__, __LINE__, _x_gave_labelling, xlabelling, present);
240 #endif
241 while (next_tic(&next, xlabelling, _x_gave_labelling, present, final, smallinc, _grTransform_x, increasing)) {
242 if (this_pass++ > pass_max) {
243 extern bool _x_gave_labelling;
244 if (_x_gave_labelling) {
245 gr_Error("cannot draw x axis (internal error: problem dealing with 'labelling' keyword)");
246 } else {
247 gr_Error("cannot draw x axis (internal error)");
248 }
249 return;
250 }
251 // Determine angle of x-axis tics, for non-rectangular axes
252 switch (_grTransform_x) {
253 case gr_axis_LINEAR:
254 case gr_axis_LOG:
255 angle = atan2(1.0, 0.0); // angle for tics
256 break;
257 default:
258 gr_Error("unknown axis type (internal error)");
259 break;
260 }
261 gr_usertocm(next, y, &xcm, &ycm);
262 axis_path.push_back(xcm, ycm, 'l');
263 // Detect large tics on x axis
264 if ((_x_gave_labelling && gr_multiple(next - xlabelling, xinc, 0.01 * smallinc))
265 || (!_x_gave_labelling && gr_multiple(next, xinc, 0.01 * smallinc))) {
266 #if defined(DEBUG_LABELLING)
267 printf("%s:%d next=%f\n", __FILE__, __LINE__, next);
268 #endif
269 // draw large tic
270 axis_path.push_back(xcm + tic * cos(angle), ycm + tic * sin(angle), 'l');
271 if (gr_currentfontsize_pt() > SMALLFONTSIZE) {
272 if (_xtype_map != ' ') {
273 // It's a map, so figure the deg/min/seconds;
274 // over-ride any existing format
275 int hour, min, sec;
276 if (gr_multiple(next, 1.0, 1.0e-6)) {
277 hour = (int)floor(1.0e-4 + fabs(next));
278 if (next >= 0.0)
279 sprintf(_grTempString,"%d$\\circ$%c",hour,_xtype_map);
280 else
281 sprintf(_grTempString,"-%d$\\circ$%c",hour,_xtype_map);
282 } else if (gr_multiple(next, 1.0 / 60.0, 1.0e-7)) {
283 hour = (int)floor(1.0e-4 + fabs(next));
284 min = (int)floor(1e-5 + 60.0 * (fabs(next) - hour));
285 if (next >= 0.0)
286 sprintf(_grTempString,"%d$\\circ$%d'%c",hour,min,_xtype_map);
287 else
288 sprintf(_grTempString,"-%d$\\circ$%d'%c",hour,min,_xtype_map);
289 } else if (gr_multiple(next, 1.0 / 3600.0, 1.0e-8)) {
290 hour = (int)floor(1.0e-4 + fabs(next));
291 min = (int)floor(1e-5 + 60.0 * (fabs(next) - hour));
292 sec = (int)floor(1e-5 + 3600.0 * (fabs(next) - hour - min / 60.0));
293 if (next >= 0.0)
294 sprintf(_grTempString, "%d$\\circ$%d'%d\"%c",hour,min,sec,_xtype_map);
295 else
296 sprintf(_grTempString, "-%d$\\circ$%d'%d\"%c",hour,min,sec,_xtype_map);
297 } else {
298 sprintf(_grTempString,"%f$\\circ$%c",next,_xtype_map);
299 }
300 } else if (strlen(_grNumFormat_x)) {
301 sprintf(_grTempString, _grNumFormat_x, next);
302 if (get_flag("emulate_gre")) {
303 char *e = index(_grTempString, int('E'));
304 if (e != NULL) {
305 std::string gs(_grTempString);
306 size_t chop;
307 if (STRING_NPOS != (chop = gs.find("E+0"))) {
308 gs.replace(chop, 3, "$\\times10^{");
309 gs.append("}$");
310 } else if (STRING_NPOS != (chop = gs.find("E-0"))) {
311 gs.replace(chop, 3, "$\\times10^{-");
312 gs.append("}$");
313 } else if (STRING_NPOS != (chop = gs.find("E+"))) {
314 gs.replace(chop, 2, "$\\times10^{");
315 gs.append("}$");
316 } else if (STRING_NPOS != (chop = gs.find("E-"))) {
317 gs.replace(chop, 2, "$\\times10^{-");
318 gs.append("}$");
319 } else if (STRING_NPOS != (chop = gs.find("E"))) {
320 gs.replace(chop, 1, "$\\times10^{");
321 gs.append("}$");
322 }
323 strcpy(_grTempString, gs.c_str());
324 }
325 }
326 } else {
327 *_grTempString = '\0';
328 }
329 angle = 0;
330 #ifdef DEBUG_LABELS
331 printf("DEBUG: %s:%d after the loop, angle is %f\n", __FILE__, __LINE__, angle);
332 #endif
333 if (!user_gave_labels) {
334 slabel.assign(_grTempString);
335 fix_negative_zero(slabel);
336 label.fromSTR(slabel.c_str());
337 label.draw(xcm - offset * sin(angle),
338 ycm + offset * cos(angle),
339 TEXT_CENTERED,
340 DEG_PER_RAD * angle);
341 }
342 }
343 } else {
344 // Small tic
345 axis_path.push_back(xcm + tic_sml * cos(angle), ycm + tic_sml * sin(angle), 'l');
346 }
347 axis_path.push_back(gr_usertocm_x(next, y), gr_usertocm_y(next, y), 'l');
348 present = next;
349 #if defined(DEBUG_LABELLING)
350 printf("%s:%d bottom of loop; present=%f\n", __FILE__, __LINE__, present);
351 #endif
352 }
353 if (user_gave_labels) {
354 angle = 0;
355 for (unsigned int i = 0; i < _x_labels.size(); i++) {
356 if (BETWEEN(xl, xr, _x_label_positions[i])) {
357 label.fromSTR(_x_labels[i].c_str()); // BUG: should interpolate into this string
358 gr_usertocm(_x_label_positions[i], y, &xcm, &ycm);
359 #ifdef DEBUG_LABELS
360 printf("DEBUG: %s:%d drawing %d-th label '%s' at x=%f angle=%f\n",__FILE__,__LINE__,i,_x_labels[i].c_str(),_x_label_positions[i],angle);
361 #endif
362 label.draw(xcm - offset * sin(angle),
363 ycm + offset * cos(angle),
364 TEXT_CENTERED,
365 DEG_PER_RAD * angle);
366 } else {
367 #ifdef DEBUG_LABELS
368 printf("DEBUG: %s:%d SKIPPING %d-th label '%s' since x=%f\n",__FILE__,__LINE__,i,_x_labels[i].c_str(),_x_label_positions[i]);
369 #endif
370 }
371 }
372 }
373 // Finish by drawing to end of axis (in case there was no tic there).
374 axis_path.push_back(gr_usertocm_x(final, y), gr_usertocm_y(final, y), 'l');
375 axis_path.stroke(units_cm, _griState.linewidth_axis());
376 break;
377 case gr_axis_LOG:
378 decade_between_labels = (int) floor(0.5 + xinc);
379 gr_usertocm(xl, y, &xcm, &ycm);
380 gr_cmtouser(xcm - AXIS_TWIDDLE, ycm, &tmp1, &tmp2);
381 present = tmp1;
382 axis_path.push_back(present, y, 'm');
383 #if defined(DEBUG_LABELLING)
384 printf("%s:%d _x_gave_labelling=%d xlabelling=%f present=%f\n", __FILE__, __LINE__, _x_gave_labelling, xlabelling, present);
385 #endif
386 while (next_tic(&next, xl, _x_gave_labelling, present, final, smallinc, _grTransform_x, increasing)) {
387 double tmp, next_log;
388 double xuser, yuser;
389 axis_path.push_back(next, y, 'l');
390 next_log = log10(next);
391 tmp = next_log - floor(next_log);
392 gr_usertocm(next, y, &xcm, &ycm);
393 if (-0.01 < tmp && tmp < 0.01) {
394 // large tic & number
395 gr_cmtouser(xcm, ycm+tic, &xuser, &yuser);
396 axis_path.push_back(xuser, yuser, 'l');
397 gr_cmtouser(xcm, ycm+offset, &xuser, &yuser);
398 tmp = next_log - decade_between_labels * floor(next_log / decade_between_labels);
399 if (!user_gave_labels
400 && gr_currentfontsize_pt() > SMALLFONTSIZE
401 && -0.01 < tmp / xinc
402 && tmp / xinc < 0.01
403 && strlen(_grNumFormat_x)) {
404 // Draw "1" as a special case
405 if (0.99 < next && next < 1.01)
406 sprintf(_grTempString, "1");
407 else
408 sprintf(_grTempString, "$10^{%.0f}$", log10(next));
409 slabel.assign(_grTempString);
410 fix_negative_zero(slabel);
411 label.fromSTR(slabel.c_str());
412 label.draw(xcm, ycm + offset, TEXT_CENTERED, 0.0);
413 }
414 } else {
415 // small tic
416 gr_cmtouser(xcm, ycm+tic_sml,&xuser, &yuser);
417 axis_path.push_back(xuser, yuser, 'l');
418 }
419 axis_path.push_back(next, y, 'm');
420 present = next;
421 }
422 if (user_gave_labels) {
423 angle = 0;
424 for (unsigned int i = 0; i < _x_labels.size(); i++) {
425 if (BETWEEN(xl, xr, _x_label_positions[i])) {
426 label.fromSTR(_x_labels[i].c_str()); // BUG: should interpolate into this string
427 gr_usertocm(_x_label_positions[i], y, &xcm, &ycm);
428 #ifdef DEBUG_LABELS
429 printf("DEBUG: %s:%d drawing %d-th label '%s' at x=%f angle=%f\n",__FILE__,__LINE__,i,_x_labels[i].c_str(),_x_label_positions[i],angle);
430 #endif
431 label.draw(xcm - offset * sin(angle), ycm + offset * cos(angle), TEXT_CENTERED, DEG_PER_RAD * angle);
432 } else {
433 #ifdef DEBUG_LABELS
434 //printf("DEBUG: %s:%d SKIPPING %d-th label '%s' since x=%f\n",__FILE__,__LINE__,i,_x_labels[i].c_str(),_x_label_positions[i]);
435 #endif
436 }
437 }
438 }
439 // Finish by drawing to end of axis (in case there was no tic there).
440 axis_path.push_back(final, y, 'l');
441 axis_path.stroke(units_user, _griState.linewidth_axis());
442 break;
443 default:
444 gr_Error("unknown axis type (internal error)");
445 }
446 // Draw axis title.
447 if (gr_currentfontsize_pt() > SMALLFONTSIZE) {
448 label.fromSTR(_grXAxisLabel);
449 switch (_grTransform_x) {
450 case gr_axis_LINEAR:
451 if (side == gr_axis_TOP) {
452 double xcm, ycm;
453 gr_usertocm(0.5 * (xl + final), y, &xcm, &ycm);
454 label.draw(xcm,
455 ycm + offset + 1.75 * CapHeight,
456 TEXT_CENTERED,
457 0.0);
458 } else {
459 double xcm, ycm;
460 gr_usertocm(0.5 * (xl + final), y, &xcm, &ycm);
461 label.draw(xcm,
462 ycm + offset - 1.75 * CapHeight,
463 TEXT_CENTERED,
464 0.0);
465 }
466 break;
467 case gr_axis_LOG:
468 if (side == gr_axis_TOP) {
469 double xcm, ycm;
470 gr_usertocm(sqrt(xl * final), y, &xcm, &ycm);
471 label.draw(xcm,
472 ycm + offset + (1.75 + 0.75) * CapHeight,
473 TEXT_CENTERED,
474 0.0);
475 } else {
476 double xcm, ycm;
477 gr_usertocm(sqrt(xl * final), y, &xcm, &ycm);
478 label.draw(xcm,
479 ycm + offset - 1.75 * CapHeight,
480 TEXT_CENTERED,
481 0.0);
482 }
483 break;
484 default:
485 gr_Error("unknown axis type (internal error)");
486 }
487 }
488 _griState.set_line_cap(old_linecap);
489 _griState.set_line_join(old_linejoin);
490 }
491
492 #define FACTOR 1.35 // Kludge to scale fonts up
493 // Draw a y axis
494 void
gr_drawyaxis(double x,double yb,double yinc,double yt,double ylabelling,gr_axis_properties side)495 gr_drawyaxis(double x, double yb, double yinc, double yt, double ylabelling, gr_axis_properties side)
496 {
497 #if 1 // 2.9.x
498 bool user_gave_labels = (_y_labels.size() != 0);
499 #endif // 2.9.x
500 GriString label;
501 std::string slabel;
502 extern char _ytype_map;
503 double CapHeight = gr_currentCapHeight_cm();
504 double angle = 0.0; // angle of axis tics, labels, etc
505 bool increasing = ((yt > yb) ? true : false);
506 double tic, tic_sml; // length of tic marks
507 double xcm, ycm; // used to step along axis
508 double xcm2, ycm2; // tmp, allowed to mess with
509 double labelx_cm, labely_cm; // where tic label will go
510 double offset; // for numbers
511 double present, next, final = yt, smallinc = 0.0;
512 int decade_between_labels; // for log axes
513 double max_num_width_cm = 0.0; // use for positioning label
514 double tmp0, tmp1, tmp2;
515 GriPath axis_path;
516 // Calculate size of large and small tic marks.
517 extern bool _grTicsPointIn;
518 tic = ((side == gr_axis_LEFT && _grTicsPointIn == true)
519 || (side == gr_axis_RIGHT && _grTicsPointIn == false))
520 ? _grTicSize_cm : -_grTicSize_cm;
521 if (_grTransform_y == gr_axis_LOG && _ysubdiv < 0)
522 tic_sml = 0.0;
523 else
524 tic_sml = TIC_RATIO * tic;
525 // Calculate offset = space for numbers.
526 if (side == gr_axis_LEFT) {
527 if (_grTicsPointIn == true) {
528 offset = -0.5 * FACTOR * CapHeight;
529 } else {
530 offset = -0.5 * FACTOR * CapHeight - _grTicSize_cm;
531 }
532 } else {
533 if (_grTicsPointIn == true) {
534 offset = 0.5 * FACTOR * CapHeight;
535 } else {
536 offset = 0.5 * FACTOR * CapHeight + _grTicSize_cm;
537 }
538 }
539 int old_linecap = _griState.line_cap();
540 int old_linejoin = _griState.line_join();
541 _griState.set_line_cap(0);
542 _griState.set_line_join(0);
543 // Draw y-axis, moving from tic to tic. Tics are advanced by smallinc
544 // for linear axes and by something appropriate for log axes. Whenever
545 // the current location should be a big tic, such a tic is drawn along
546 // with a label.
547 int this_pass=0, pass_max=5000;
548 switch (_grTransform_y) {
549 case gr_axis_LINEAR:
550 smallinc = yinc / _grNumSubDiv_y;
551 present = yb - yinc / 1.0e3;
552 final = yt + yinc / 1.0e3;
553 axis_path.push_back(gr_usertocm_x(x, yb), gr_usertocm_y(x, yb), 'm');
554 #if defined(DEBUG_LABELLING)
555 printf("%s:%d _y_gave_labelling=%d ylabelling=%f present=%f\n", __FILE__, __LINE__, _y_gave_labelling, ylabelling, present);
556 #endif
557 while (next_tic(&next, ylabelling, _y_gave_labelling, present, final, smallinc, _grTransform_y, increasing)) {
558 axis_path.push_back(gr_usertocm_x(x, next), gr_usertocm_y(x, next), 'l');
559 gr_usertocm(x, next, &xcm, &ycm);
560 angle = 0.0;
561 // Detect large tics on y axis
562 if ((_y_gave_labelling && gr_multiple(next - ylabelling, yinc, 0.01 * smallinc))
563 || (!_y_gave_labelling && gr_multiple(next, yinc, 0.01 * smallinc))) {
564 double tmpx, tmpy;
565 gr_cmtouser(xcm + tic * cos(angle), ycm + tic * sin(angle), &tmpx, &tmpy);
566 axis_path.push_back(xcm + tic * cos(angle), ycm + tic * sin(angle), 'l');
567 labelx_cm = xcm + offset * cos(angle);
568 labely_cm = ycm + offset * sin(angle) - 0.5 * CapHeight;
569 if (gr_currentfontsize_pt() > SMALLFONTSIZE) {
570 if (_ytype_map != ' ') {
571 // It's a map, so figure the deg/min/seconds;
572 // over-ride any existing format
573 int hour, min, sec;
574 if (gr_multiple(next, 1.0, 1.0e-6)) {
575 hour = (int)floor(1.0e-4 + fabs(next));
576 if (next >= 0.0)
577 sprintf(_grTempString,"%d$\\circ$%c",hour,_ytype_map);
578 else
579 sprintf(_grTempString,"-%d$\\circ$%c",hour,_ytype_map);
580 } else if (gr_multiple(next, 1.0 / 60.0, 1.0e-7)) {
581 hour = (int)floor(1.0e-4 + fabs(next));
582 min = (int)floor(1e-5 + 60.0 * (fabs(next) - hour));
583 if (next >= 0.0)
584 sprintf(_grTempString,"%d$\\circ$%d'%c",hour,min,_ytype_map);
585 else
586 sprintf(_grTempString,"-%d$\\circ$%d'%c",hour,min,_ytype_map);
587 } else if (gr_multiple(next, 1.0 / 3600.0, 1.0e-8)) {
588 hour = (int)floor(1.0e-4 + fabs(next));
589 min = (int)floor(1e-5 + 60.0 * (fabs(next) - hour));
590 sec = (int)floor(1e-5 + 3600.0 * (fabs(next) - hour - min / 60.0));
591 if (next >= 0.0)
592 sprintf(_grTempString, "%d$\\circ$%d'%d\"%c",hour,min,sec,_ytype_map);
593 else
594 sprintf(_grTempString, "-%d$\\circ$%d'%d\"%c",hour,min,sec,_ytype_map);
595 } else {
596 sprintf(_grTempString,"%f$\\circ$%c",next,_ytype_map);
597 }
598 } else if (strlen(_grNumFormat_y)) {
599 if (get_flag("emulate_gre")) {
600 sprintf(_grTempString, _grNumFormat_y, next);
601 char *e = index(_grTempString, int('E'));
602 if (e != NULL) {
603 std::string gs(_grTempString);
604 size_t chop;
605 if (STRING_NPOS != (chop = gs.find("E+0"))) {
606 gs.replace(chop, 3, "$\\times10^{");
607 gs.append("}$");
608 } else if (STRING_NPOS != (chop = gs.find("E-0"))) {
609 gs.replace(chop, 3, "$\\times10^{-");
610 gs.append("}$");
611 } else if (STRING_NPOS != (chop = gs.find("E+"))) {
612 gs.replace(chop, 2, "$\\times10^{");
613 gs.append("}$");
614 } else if (STRING_NPOS != (chop = gs.find("E-"))) {
615 gs.replace(chop, 2, "$\\times10^{-");
616 gs.append("}$");
617 } else if (STRING_NPOS != (chop = gs.find("E"))) {
618 gs.replace(chop, 1, "$\\times10^{");
619 gs.append("}$");
620 }
621 strcpy(_grTempString, gs.c_str());
622 }
623 } else {
624 sprintf(_grTempString, _grNumFormat_y, next);
625 }
626 } else {
627 *_grTempString = '\0';
628 }
629 if (!user_gave_labels) { // 2.9.x
630 slabel.assign(_grTempString);
631 fix_negative_zero(slabel);
632 label.fromSTR(slabel.c_str());
633 if (side == gr_axis_LEFT)
634 label.draw(labelx_cm, labely_cm, TEXT_RJUST, angle * DEG_PER_RAD);
635 else
636 label.draw(labelx_cm, labely_cm, TEXT_LJUST, angle * DEG_PER_RAD);
637 }
638 // Keep track of maximum width of axis numbers, so that
639 // axis label can be offset right amount.
640 gr_stringwidth(_grTempString, &tmp0, &tmp1, &tmp2);
641 if (tmp0 > max_num_width_cm)
642 max_num_width_cm = tmp0;
643 }
644 } else {
645 // Small tic
646 axis_path.push_back(xcm + tic_sml * cos(angle), ycm + tic_sml * sin(angle), 'l');
647 }
648 axis_path.push_back(gr_usertocm_x(x, next), gr_usertocm_y(x, next), 'l');
649 present = next;
650 }
651 #if 1 // 2.9.x
652 if (user_gave_labels) {
653 //printf("labels...\n");
654 for (unsigned int i = 0; i < _y_labels.size(); i++) {
655 label.fromSTR(_y_labels[i].c_str()); // BUG: should interpolate into this string
656 gr_usertocm(x, _y_label_positions[i], &xcm, &ycm);
657 labelx_cm = xcm + offset * cos(angle);
658 labely_cm = ycm + offset * sin(angle) - 0.5 * CapHeight;
659 //printf("%f %f %f %f\n", ycm, offset*sin(angle),CapHeight, labely_cm);
660 if (side == gr_axis_LEFT)
661 label.draw(labelx_cm, labely_cm, TEXT_RJUST, DEG_PER_RAD * angle);
662 else
663 label.draw(labelx_cm, labely_cm, TEXT_LJUST, DEG_PER_RAD * angle);
664 gr_stringwidth(_y_labels[i].c_str(), &tmp0, &tmp1, &tmp2);
665 if (tmp0 > max_num_width_cm)
666 max_num_width_cm = tmp0;
667 }
668 }
669 #endif
670 // Finish by drawing to end of axis (in case there was no tic there).
671 axis_path.push_back(gr_usertocm_x(x, yt), gr_usertocm_y(x, yt), 'l');
672 axis_path.stroke(units_cm, _griState.linewidth_axis());
673 break;
674 case gr_axis_LOG:
675 decade_between_labels = (int) floor(0.5 + yinc);
676 gr_usertocm(x, yb, &xcm, &ycm);
677 gr_cmtouser(xcm, ycm - AXIS_TWIDDLE, &tmp1, &tmp2);
678 present = tmp2;
679 axis_path.push_back(x, present, 'm');
680 if (_y_gave_labelling) {
681 err("cannot use 'labelling' parameter for logarithmic axis");
682 return;
683 }
684 #if defined(DEBUG_LABELLING)
685 printf("%s:%d _y_gave_labelling=%d ylabelling=%f present=%f\n", __FILE__, __LINE__, _y_gave_labelling, ylabelling, present);
686 #endif
687 while (next_tic(&next, yb, _y_gave_labelling, present, final, smallinc, _grTransform_y, increasing)) {
688 if (this_pass++ > pass_max) {
689 extern bool _y_gave_labelling;
690 if (_y_gave_labelling) {
691 gr_Error("cannot draw y axis (internal error: cannot use 'labelling' keyword)");
692 } else {
693 gr_Error("cannot draw y axis (internal error)");
694 }
695 return;
696 }
697 double tmp, next_log;
698 axis_path.push_back(x, next, 'l');
699 next_log = log10(next);
700 tmp = next_log - floor(next_log);
701 gr_usertocm(x, next, &xcm2, &ycm2); // NOTE: not using (xcm,ycm)
702 if (-0.01 < tmp && tmp < 0.01) {
703 // large tic & number
704 double xuser, yuser;
705 gr_cmtouser(xcm2 + tic, ycm2, &xuser, &yuser);
706 axis_path.push_back(xuser, yuser, 'l');
707 gr_cmtouser(xcm2 + tic, ycm2 - 0.5 * FACTOR * CapHeight, &xuser, &yuser);
708 tmp = next_log - decade_between_labels * floor(next_log / decade_between_labels);
709 if (!user_gave_labels
710 && gr_currentfontsize_pt() > SMALLFONTSIZE
711 && -0.01 < tmp / yinc
712 && tmp / yinc< 0.01
713 && strlen(_grNumFormat_y)) {
714 // Draw "1" as a special case
715 if (0.99 < next && next < 1.01)
716 sprintf(_grTempString, "1");
717 else
718 sprintf(_grTempString, "$10^{%.0f}$", log10(next));
719 slabel.assign(_grTempString);
720 fix_negative_zero(slabel);
721 label.fromSTR(slabel.c_str());
722 if (side == gr_axis_LEFT)
723 label.draw(xcm2 + offset, ycm2 - 0.5 * CapHeight, TEXT_RJUST, 0.0);
724 else
725 label.draw(xcm2 + offset, ycm2 - 0.5 * CapHeight, TEXT_LJUST, 0.0);
726 // Keep track of maximum width of axis numbers, so that
727 // axis label can be offset right amount.
728 gr_stringwidth(_grTempString, &tmp0, &tmp1, &tmp2);
729 if (tmp0 > max_num_width_cm)
730 max_num_width_cm = tmp0;
731 }
732 } else {
733 // small tic
734 double xuser, yuser;
735 gr_cmtouser(xcm2 + tic_sml, ycm2, &xuser, &yuser);
736 axis_path.push_back(xuser, yuser, 'l');
737 }
738 axis_path.push_back(x, next, 'l');
739 present = next;
740 }
741 if (user_gave_labels) {
742 angle = 0;
743 for (unsigned int i = 0; i < _y_labels.size(); i++) {
744 if (BETWEEN(yb, yt, _y_label_positions[i])) {
745 label.fromSTR(_y_labels[i].c_str()); // BUG: should interpolate into this string
746 gr_usertocm(x, _y_label_positions[i], &xcm, &ycm);
747 labelx_cm = xcm + offset * cos(angle);
748 labely_cm = ycm + offset * sin(angle) - 0.5 * CapHeight;
749
750 #ifdef DEBUG_LABELS
751 printf("DEBUG: %s:%d drawing %d-th label '%s' at y=%f angle=%f\n",__FILE__,__LINE__,i,_y_labels[i].c_str(),_y_label_positions[i],angle);
752 #endif
753 if (side == gr_axis_LEFT)
754 label.draw(labelx_cm, labely_cm, TEXT_RJUST, DEG_PER_RAD * angle);
755 else
756 label.draw(labelx_cm, labely_cm, TEXT_LJUST, DEG_PER_RAD * angle);
757 } else {
758 #ifdef DEBUG_LABELS
759 //printf("DEBUG: %s:%d SKIPPING %d-th label '%s' since x=%f\n",__FILE__,__LINE__,i,_y_labels[i].c_str(),_y_label_positions[i]);
760 #endif
761 }
762 }
763 }
764 // Finish by drawing to end of axis (in case there was no tic there).
765 axis_path.push_back(x, final, 'l');
766 axis_path.stroke(units_user, _griState.linewidth_axis());
767 break;
768 default:
769 gr_Error("unknown axis type (internal error)");
770 }
771 // write label, rotated if necessary
772 if (gr_currentfontsize_pt() > SMALLFONTSIZE) {
773 // Start to calculate what x to put label at; this makes xcm be on
774 // axis, so will have to shift depending on orientation of label.
775 // Note: will now re-use 'angle' to mean angle of y axis
776
777 if (_grTransform_y == gr_axis_LOG) {
778 double x_cm, xx_cm, y_cm, yy_cm;
779 gr_usertocm(x, sqrt(yb * yt), &x_cm, &y_cm);
780 gr_usertocm(x, 0.001 + sqrt(yb * yt), &xx_cm, &yy_cm);
781 angle = fabs(DEG_PER_RAD * atan2(yy_cm - y_cm, xx_cm - x_cm)); // abs() ensures from bottom to top
782 } else {
783 double x_cm, xx_cm, y_cm, yy_cm;
784 gr_usertocm(x, 0.5 * (yb + yt), &x_cm, &y_cm);
785 gr_usertocm(x, 0.01 * yinc + 0.5 * (yb + yt), &xx_cm, &yy_cm);
786 angle = DEG_PER_RAD * atan2(yy_cm - y_cm, xx_cm - x_cm);
787 }
788 xcm = 0.5 * (gr_usertocm_x(x, yb) + gr_usertocm_x(x, yt));
789 ycm = 0.5 * (gr_usertocm_y(x, yb) + gr_usertocm_y(x, yt));
790 // Need at least max_num_width_cm, i.e., widest numeric label, plus
791 // a little space (check against above).
792 max_num_width_cm += FACTOR * CapHeight;
793 // Need space for tics too
794 max_num_width_cm += (_grTicsPointIn == true ? 0.0 : _grTicSize_cm);
795 // Do by cases -- inelegant but flexible to change
796 label.fromSTR(_grYAxisLabel);
797 switch (_grAxisStyle_y) {
798 default:
799 case 0: // label parallel to axis
800 if (side == gr_axis_LEFT) {
801 label.draw(xcm - max_num_width_cm,
802 ycm,
803 TEXT_CENTERED, angle);
804 } else {
805 label.draw(xcm + max_num_width_cm,
806 ycm,
807 TEXT_CENTERED, angle - 180);
808 }
809 break;
810 case 1: // horizontal label
811 if (side == gr_axis_LEFT) {
812 label.draw(xcm - max_num_width_cm,
813 ycm - 0.5 * CapHeight,
814 TEXT_RJUST, 90.0 - angle);
815 } else {
816 label.draw(xcm + max_num_width_cm,
817 ycm - 0.5 * CapHeight,
818 TEXT_LJUST, 90.0 - angle);
819 }
820 break;
821 }
822 }
823 _griState.set_line_cap(old_linecap);
824 _griState.set_line_join(old_linejoin);
825 }
826
827 #if 0
828 /* UNUSED -- Delete later BUG ?? */
829 #define NUM_LABEL 100
830 /*
831 * create_labels - make clean labels for linear axes (clean by making same
832 * number of decimals)
833 */
834 static int
835 create_labels(double y, double yb, double yinc, double yt, double smallinc,
836 char *label[NUM_LABEL],
837 int *num_label)
838 {
839 int i, max_decimals = 0;
840 double zero = fabs(yt - yb) / 1.0e5; /* store a number
841 * effectively 0 */
842 *num_label = 0;
843 do {
844 if (gr_multiple(y, yinc, 0.01 * smallinc)) {
845 if (fabs(y) < zero)
846 sprintf(_grTempString, _grNumFormat_y, 0.0);
847 else
848 sprintf(_grTempString, _grNumFormat_y, y);
849 /* reserve 10 extra characters for 0s which might be appended */
850 GET_STORAGE(label[*num_label],
851 char,
852 10 + strlen(_grTempString));
853 strcpy(label[*num_label], _grTempString);
854 (*num_label)++;
855 }
856 y += smallinc;
857 } while ((*num_label < NUM_LABEL)
858 && ((yinc > 0.0 && y <= yt)
859 || (yinc < 0.0 && y >= yt)));
860 if (*num_label >= NUM_LABEL) {
861 fprintf(stderr, "Internal error (graxes.c): %d > NUM_LABEL labels\n",
862 *num_label);
863 gri_exit(1);
864 }
865 for (i = 0; i < *num_label; i++) {
866 int j;
867 if (max_decimals < (j = num_decimals(label[i])))
868 max_decimals = j;
869 }
870 for (i = 0; i < *num_label; i++) {
871 int n = num_decimals(label[i]);
872 int l = strlen(label[i]);
873 if (!n) /* don't meddle if e/E/d/D present */
874 continue;
875 if (max_decimals > 0) {
876 if (n < max_decimals) {
877 int j;
878 if (n == 0) {
879 *(label[i] + l++) = '.';
880 n = 1;
881 }
882 for (j = max_decimals - n; j < max_decimals; j++)
883 *(label[i] + l++) = '0';
884 }
885 *(label[i] + l) = '\0';
886 }
887 }
888 return 1;
889 }
890 #endif
891
892 #if 0
893 /*
894 * num_decimals(s) -- return number of places to right of decimal, but return
895 * 0 if there is an e/E/d/D in the number (in which case don't meddle
896 */
897 static int
898 num_decimals(char *s)
899 {
900 char * cp;
901 int j = 0, jMax = strlen(s);
902 /*
903 * First search for e/E/d/D
904 */
905 cp = s;
906 do
907 if (*cp == 'd' || *cp == 'D' || *cp == 'e' || *cp == 'E')
908 return 0;
909 while (*++cp != '\0');
910 /*
911 * It doesn't have e/E/d/D
912 */
913 cp = s + strlen(s) - 1;
914 do
915 if (*cp == '.')
916 break;
917 while (cp--, ++j < jMax);
918 if (j >= jMax)
919 j = 0;
920 return j;
921 }
922 #endif
923