The R Project SVN R

Rev

Rev 24735 | Rev 27236 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 r 1
/*
1160 maechler 2
 *  R : A Computer Language for Statistical Data Analysis
2 r 3
 *  Copyright (C) 1995, 1996, 1997 Robert Gentleman and Ross Ihaka
7527 maechler 4
 *  Copyright (C) 1998-2000	The R Development Core Team
2 r 5
 *
6
 *  This source code module:
2028 ihaka 7
 *  Copyright (C) 1997, 1998 Paul Murrell and Ross Ihaka
2 r 8
 *
9
 *  This program is free software; you can redistribute it and/or modify
10
 *  it under the terms of the GNU General Public License as published by
11
 *  the Free Software Foundation; either version 2 of the License, or
12
 *  (at your option) any later version.
13
 *
14
 *  This program is distributed in the hope that it will be useful,
15
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 *  GNU General Public License for more details.
18
 *
19
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program; if not, write to the Free Software
5458 ripley 21
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
2 r 22
 */
23
 
5187 hornik 24
#ifdef HAVE_CONFIG_H
7701 hornik 25
#include <config.h>
5187 hornik 26
#endif
27
 
5233 hornik 28
#include <ctype.h>
29
 
11499 ripley 30
#include <Defn.h>
31
#include <Rmath.h>
32
#include <Graphics.h>
1926 ihaka 33
 
5233 hornik 34
 
2028 ihaka 35
/* The R graphics device */
2 r 36
 
19875 murrell 37
/* These are a bit unthreadable aren't they?
38
 * Better to pass them all down, but that's a heck of a lot of 
39
 * changes to make to the function call argument lists!!
40
 */
41
static GEDevDesc *MathDevice;
2028 ihaka 42
static unsigned int BoxColor;
43
static unsigned int TextColor;
19875 murrell 44
/* The value of cex passed into GEMathText
45
 */
2028 ihaka 46
static double BaseCex = 1;
19875 murrell 47
static double MathGamma;
21062 murrell 48
static char *MathFontFamily;
19875 murrell 49
/* The value of font passed into GEMathText
50
 * May be modified by plotmath code -- it is plotmath code's 
51
 * responsibility to save and restore temporary changes.
52
 */
53
static int MathFont;
21062 murrell 54
static double MathLineHeight;
19875 murrell 55
/* A temporary value of cex that may be modified by plotmath code.
56
 */
57
static double MathCex;
58
static double MathPs;
59
static GEUnit MetricUnit = GE_INCHES;
581 paul 60
 
2028 ihaka 61
/* Font Definitions */
2 r 62
 
2028 ihaka 63
typedef enum {
3475 pd 64
    PlainFont	   = 1,
65
    BoldFont	   = 2,
66
    ItalicFont	   = 3,
2028 ihaka 67
    BoldItalicFont = 4,
6098 pd 68
    SymbolFont	   = 5
2028 ihaka 69
} FontType;
70
 
2122 maechler 71
/*
2028 ihaka 72
 *  Italic Correction Factor
73
 *
74
 *  The correction for a character is computed as ItalicFactor
75
 *  times the height (above the baseline) of the character's
76
 *  bounding box.
77
 *
78
 */
79
 
80
static double ItalicFactor = 0.15;
81
 
82
/* Drawing basics */
83
 
84
static double ReferenceX;
85
static double ReferenceY;
86
static double CurrentX;
87
static double CurrentY;
88
static double CurrentAngle;
89
static double CosAngle;
90
static double SinAngle;
91
 
92
/* Convert CurrentX and CurrentY from */
93
/* 0 angle to and CurrentAngle */
94
 
95
static double ConvertedX()
2 r 96
{
2028 ihaka 97
    double rotatedX = ReferenceX +
98
	(CurrentX - ReferenceX) * CosAngle -
99
	(CurrentY - ReferenceY) * SinAngle;
19875 murrell 100
    return toDeviceX(rotatedX, MetricUnit, MathDevice);
2 r 101
}
102
 
2028 ihaka 103
static double ConvertedY()
104
{
105
    double rotatedY = ReferenceY +
106
	(CurrentY - ReferenceY) * CosAngle +
107
	(CurrentX - ReferenceX) * SinAngle;
19875 murrell 108
    return toDeviceY(rotatedY, MetricUnit, MathDevice);
2028 ihaka 109
}
2 r 110
 
3244 ihaka 111
static void PMoveAcross(double xamount)
2 r 112
{
2028 ihaka 113
    CurrentX += xamount;
2 r 114
}
115
 
3244 ihaka 116
static void PMoveUp(double yamount)
2028 ihaka 117
{
118
    CurrentY += yamount;
119
}
2 r 120
 
3244 ihaka 121
static void PMoveTo(double x, double y)
2 r 122
{
2028 ihaka 123
    CurrentX = x;
124
    CurrentY = y;
2 r 125
}
126
 
2028 ihaka 127
/* Basic Font Properties */
128
 
129
static double xHeight()
2 r 130
{
2028 ihaka 131
    double height, depth, width;
19875 murrell 132
    GEMetricInfo('x', 
133
		MathFont, MathCex, MathPs,
134
		&height, &depth, &width, MathDevice);
135
    return fromDeviceHeight(height, MetricUnit, MathDevice);
2028 ihaka 136
}
2 r 137
 
2028 ihaka 138
static double XHeight()
139
{
140
    double height, depth, width;
19875 murrell 141
    GEMetricInfo('X', 
142
		MathFont, MathCex, MathPs,
143
		&height, &depth, &width, MathDevice);
144
    return fromDeviceHeight(height, MetricUnit, MathDevice);
2 r 145
}
146
 
2028 ihaka 147
static double AxisHeight()
2 r 148
{
2028 ihaka 149
    double height, depth, width;
19875 murrell 150
    GEMetricInfo('+', 
151
		MathFont, MathCex, MathPs,
152
		&height, &depth, &width, MathDevice);
153
    return fromDeviceHeight(0.5 * height, MetricUnit, MathDevice);
2 r 154
}
155
 
2028 ihaka 156
static double Quad()
2 r 157
{
2028 ihaka 158
    double height, depth, width;
19875 murrell 159
    GEMetricInfo('M', 
160
		MathFont, MathCex, MathPs,
161
		&height, &depth, &width, MathDevice);
162
    return fromDeviceHeight(width, MetricUnit, MathDevice);
2028 ihaka 163
}
164
 
2105 ihaka 165
/* The height of digits */
166
static double FigHeight()
167
{
168
    double height, depth, width;
19875 murrell 169
    GEMetricInfo('0', 
170
		MathFont, MathCex, MathPs,
171
		&height, &depth, &width, MathDevice);
172
    return fromDeviceHeight(height, MetricUnit, MathDevice);
2105 ihaka 173
}
174
 
175
/* Depth of lower case descenders */
176
static double DescDepth()
177
{
178
    double height, depth, width;
19875 murrell 179
    GEMetricInfo('g', 
180
		MathFont, MathCex, MathPs,
181
		&height, &depth, &width, MathDevice);
182
    return fromDeviceHeight(depth, MetricUnit, MathDevice);
2105 ihaka 183
}
184
 
185
/* Thickness of rules */
186
static double RuleThickness()
187
{
188
    return 0.015;
189
}
190
 
2028 ihaka 191
static double ThinSpace()
192
{
193
    double height, depth, width;
194
    static double OneSixth = 0.16666666666666666666;
19875 murrell 195
    GEMetricInfo('M', 
196
		MathFont, MathCex, MathPs,
197
		&height, &depth, &width, MathDevice);
198
    return fromDeviceHeight(OneSixth * width, MetricUnit, MathDevice);
2028 ihaka 199
}
200
 
201
static double MediumSpace()
202
{
203
    double height, depth, width;
204
    static double TwoNinths = 0.22222222222222222222;
19875 murrell 205
    GEMetricInfo('M', 
206
		MathFont, MathCex, MathPs,
207
		&height, &depth, &width, MathDevice);
208
    return fromDeviceHeight(TwoNinths * width, MetricUnit, MathDevice);
2028 ihaka 209
}
210
 
211
static double ThickSpace()
212
{
213
    double height, depth, width;
214
    static double FiveEighteenths = 0.27777777777777777777;
19875 murrell 215
    GEMetricInfo('M', 
216
		MathFont, MathCex, MathPs,
217
		&height, &depth, &width, MathDevice);
218
    return fromDeviceHeight(FiveEighteenths * width, MetricUnit, MathDevice);
2028 ihaka 219
}
220
 
221
static double MuSpace()
222
{
223
    double height, depth, width;
224
    static double OneEighteenth = 0.05555555555555555555;
19875 murrell 225
    GEMetricInfo('M', 
226
		MathFont, MathCex, MathPs,
227
		&height, &depth, &width, MathDevice);
228
    return fromDeviceHeight(OneEighteenth * width, MetricUnit, MathDevice);
2028 ihaka 229
}
230
 
231
 
232
/*
233
 *  Mathematics Layout Parameters
234
 *
2123 maechler 235
 *  The TeXBook, Appendix G, Page 447.
2028 ihaka 236
 *
2105 ihaka 237
 *  These values are based on an inspection of TeX metafont files
238
 *  together with some visual simplification.
2028 ihaka 239
 *
2105 ihaka 240
 *  Note : The values are ``optimised'' for PostScript.
241
 *
2028 ihaka 242
 */
243
 
244
typedef enum {
3475 pd 245
    sigma2,  sigma5,  sigma6,  sigma8,	sigma9,	 sigma10, sigma11,
2105 ihaka 246
    sigma12, sigma13, sigma14, sigma15, sigma16, sigma17, sigma18,
247
    sigma19, sigma20, sigma21, sigma22, xi8, xi9, xi10, xi11, xi12, xi13
2028 ihaka 248
}
249
TEXPAR;
250
 
3475 pd 251
#define SUBS	       0.7
2028 ihaka 252
 
253
static double TeX(TEXPAR which)
254
{
255
    switch(which) {
256
    case sigma2:  /* space */
257
    case sigma5:  /* x_height */
258
	return xHeight();
259
 
260
    case sigma6:  /* quad */
261
	return Quad();
262
 
263
    case sigma8:  /* num1 */
2105 ihaka 264
	return AxisHeight()
265
	    + 3.51 * RuleThickness()
3475 pd 266
	    + 0.15 * XHeight()		/* 54/36 * 0.1 */
2105 ihaka 267
	    + SUBS * DescDepth();
2028 ihaka 268
    case sigma9:  /* num2 */
2105 ihaka 269
	return AxisHeight()
270
	    + 1.51 * RuleThickness()
3475 pd 271
	    + 0.08333333 * XHeight();	/* 30/36 * 0.1 */
2028 ihaka 272
    case sigma10: /* num3 */
2105 ihaka 273
	return AxisHeight()
274
	    + 1.51 * RuleThickness()
3475 pd 275
	    + 0.1333333 * XHeight();	/* 48/36 * 0.1 */
2028 ihaka 276
    case sigma11: /* denom1 */
3475 pd 277
	return	- AxisHeight()
2105 ihaka 278
	    + 3.51 * RuleThickness()
279
	    + SUBS * FigHeight()
3475 pd 280
	    + 0.344444 * XHeight();	/* 124/36 * 0.1 */
2028 ihaka 281
    case sigma12: /* denom2 */
3475 pd 282
	return	- AxisHeight()
2105 ihaka 283
	    + 1.51 * RuleThickness()
284
	    + SUBS * FigHeight()
3475 pd 285
	    + 0.08333333 * XHeight();	/* 30/36 * 0.1 */
2028 ihaka 286
 
287
    case sigma13: /* sup1 */
2105 ihaka 288
	return 0.95 * xHeight();
2028 ihaka 289
    case sigma14: /* sup2 */
2105 ihaka 290
	return 0.825 * xHeight();
2028 ihaka 291
    case sigma15: /* sup3 */
2105 ihaka 292
	return 0.7 * xHeight();
2028 ihaka 293
 
294
    case sigma16: /* sub1 */
2105 ihaka 295
	return 0.35 * xHeight();
2028 ihaka 296
    case sigma17: /* sub2 */
2105 ihaka 297
	return 0.45 * XHeight();
2028 ihaka 298
 
299
    case sigma18: /* sup_drop */
2105 ihaka 300
	return 0.3861111 * XHeight();
2028 ihaka 301
 
302
    case sigma19: /* sub_drop */
2105 ihaka 303
	return 0.05 * XHeight();
2028 ihaka 304
 
305
    case sigma20: /* delim1 */
2105 ihaka 306
	return 2.39 * XHeight();
2028 ihaka 307
    case sigma21: /* delim2 */
2105 ihaka 308
	return 1.01 *XHeight();
2028 ihaka 309
 
310
    case sigma22: /* axis_height */
311
	return AxisHeight();
312
 
3475 pd 313
    case xi8:	  /* default_rule_thickness */
2105 ihaka 314
	return RuleThickness();
2028 ihaka 315
 
3475 pd 316
    case xi9:	  /* big_op_spacing1 */
317
    case xi10:	  /* big_op_spacing2 */
318
    case xi11:	  /* big_op_spacing3 */
319
    case xi12:	  /* big_op_spacing4 */
320
    case xi13:	  /* big_op_spacing5 */
2028 ihaka 321
	return 0.15 * XHeight();
2692 maechler 322
    default:/* never happens (enum type) */
5731 ripley 323
	error("invalid `which' in TeX()!"); return 0;/*-Wall*/
2028 ihaka 324
    }
325
}
326
 
327
/*
328
 *  TeX Math Styles
329
 *
330
 *  The TeXBook, Appendix G, Page 441.
331
 *
332
 */
333
 
334
typedef enum {
335
    STYLE_SS1 = 1,
336
    STYLE_SS  = 2,
337
    STYLE_S1  = 3,
338
    STYLE_S   = 4,
339
    STYLE_T1  = 5,
340
    STYLE_T   = 6,
341
    STYLE_D1  = 7,
342
    STYLE_D   = 8
343
} STYLE;
344
 
345
static STYLE CurrentStyle;
346
 
347
static STYLE GetStyle()
348
{
349
    return CurrentStyle;
350
}
351
 
352
static void SetStyle(STYLE newstyle)
353
{
2122 maechler 354
    switch (newstyle) {
2028 ihaka 355
    case STYLE_D:
356
    case STYLE_T:
357
    case STYLE_D1:
358
    case STYLE_T1:
19875 murrell 359
	MathCex = 1.0 * BaseCex;
2028 ihaka 360
	break;
361
    case STYLE_S:
362
    case STYLE_S1:
19875 murrell 363
	MathCex = 0.7 * BaseCex;
2028 ihaka 364
	break;
365
    case STYLE_SS:
366
    case STYLE_SS1:
19875 murrell 367
	MathCex = 0.5 * BaseCex;
2028 ihaka 368
	break;
369
    default:
5731 ripley 370
	error("invalid math style encountered");
2028 ihaka 371
    }
372
    CurrentStyle = newstyle;
373
}
374
 
375
static void SetPrimeStyle(STYLE style)
376
{
377
    switch (style) {
378
    case STYLE_D:
379
    case STYLE_D1:
380
	SetStyle(STYLE_D1);
381
	break;
382
    case STYLE_T:
383
    case STYLE_T1:
384
	SetStyle(STYLE_T1);
385
	break;
386
    case STYLE_S:
387
    case STYLE_S1:
388
	SetStyle(STYLE_S1);
389
	break;
390
    case STYLE_SS:
391
    case STYLE_SS1:
392
	SetStyle(STYLE_SS1);
393
	break;
394
    }
395
}
396
 
397
static void SetSupStyle(STYLE style)
398
{
399
    switch (style) {
400
    case STYLE_D:
401
    case STYLE_T:
402
	SetStyle(STYLE_S);
403
	break;
404
    case STYLE_D1:
405
    case STYLE_T1:
406
	SetStyle(STYLE_S1);
407
	break;
408
    case STYLE_S:
409
    case STYLE_SS:
410
	SetStyle(STYLE_SS);
411
	break;
412
    case STYLE_S1:
413
    case STYLE_SS1:
414
	SetStyle(STYLE_SS1);
415
	break;
416
    }
417
}
418
 
419
static void SetSubStyle(STYLE style)
420
{
421
    switch (style) {
422
    case STYLE_D:
423
    case STYLE_T:
424
    case STYLE_D1:
425
    case STYLE_T1:
426
	SetStyle(STYLE_S1);
427
	break;
428
    case STYLE_S:
429
    case STYLE_SS:
430
    case STYLE_S1:
431
    case STYLE_SS1:
432
	SetStyle(STYLE_SS1);
433
	break;
434
    }
435
}
436
 
437
static void SetNumStyle(STYLE style)
438
{
439
    switch (style) {
440
    case STYLE_D:
441
	SetStyle(STYLE_T);
442
	break;
443
    case STYLE_D1:
444
	SetStyle(STYLE_T1);
445
	break;
446
    default:
447
	SetSupStyle(style);
448
    }
449
}
450
 
451
static void SetDenomStyle(STYLE style)
452
{
453
    if (style > STYLE_T)
454
	SetStyle(STYLE_T1);
1839 ihaka 455
    else
2028 ihaka 456
	SetSubStyle(style);
2 r 457
}
458
 
2028 ihaka 459
static int IsCompactStyle(STYLE style)
2 r 460
{
2028 ihaka 461
    switch (style) {
462
    case STYLE_D1:
463
    case STYLE_T1:
464
    case STYLE_S1:
465
    case STYLE_SS1:
466
	return 1;
467
    default:
468
	return 0;
469
    }
2 r 470
}
471
 
2028 ihaka 472
 
473
#ifdef max
474
#undef max
475
#endif
476
/* Return maximum of two doubles. */
477
static double max(double x, double y)
2 r 478
{
2028 ihaka 479
    if (x > y) return x;
480
    else return y;
2 r 481
}
482
 
2028 ihaka 483
 
484
/* Bounding Boxes */
485
/* These including italic corrections and an */
486
/* indication of whether the nucleus was simple. */
487
 
488
typedef struct {
489
    double height;
490
    double depth;
491
    double width;
492
    double italic;
493
    int simple;
494
} BBOX;
495
 
496
 
497
#define bboxHeight(bbox) bbox.height
498
#define bboxDepth(bbox) bbox.depth
499
#define bboxWidth(bbox) bbox.width
500
#define bboxItalic(bbox) bbox.italic
501
#define bboxSimple(bbox) bbox.simple
502
 
503
 
504
static BBOX MakeBBox(double height, double depth, double width)
2 r 505
{
2028 ihaka 506
    BBOX bbox;
507
    bboxHeight(bbox) = height;
508
    bboxDepth(bbox)  = depth;
509
    bboxWidth(bbox)  = width;
510
    bboxItalic(bbox) = 0;
511
    bboxSimple(bbox) = 0;
512
    return bbox;
2 r 513
}
514
 
2028 ihaka 515
static BBOX NullBBox()
2 r 516
{
2028 ihaka 517
    BBOX bbox;
518
    bboxHeight(bbox) = 0;
519
    bboxDepth(bbox)  = 0;
520
    bboxWidth(bbox)  = 0;
521
    bboxItalic(bbox) = 0;
522
    bboxSimple(bbox) = 0;
523
    return bbox;
2 r 524
}
525
 
2028 ihaka 526
static BBOX ShiftBBox(BBOX bbox1, double shiftV)
2 r 527
{
2028 ihaka 528
    bboxHeight(bbox1) = bboxHeight(bbox1) + shiftV;
529
    bboxDepth(bbox1)  = bboxDepth(bbox1) - shiftV;
530
    bboxWidth(bbox1)  = bboxWidth(bbox1);
531
    bboxItalic(bbox1) = bboxItalic(bbox1);
532
    bboxSimple(bbox1) = bboxSimple(bbox1);
533
    return bbox1;
2 r 534
}
535
 
2028 ihaka 536
static BBOX EnlargeBBox(BBOX bbox, double deltaHeight, double deltaDepth,
537
		      double deltaWidth)
2 r 538
{
2028 ihaka 539
    bboxHeight(bbox) += deltaHeight;
540
    bboxDepth(bbox)  += deltaDepth;
541
    bboxWidth(bbox)  += deltaWidth;
542
    return bbox;
2 r 543
}
544
 
2028 ihaka 545
static BBOX CombineBBoxes(BBOX bbox1, BBOX bbox2)
546
{
547
    bboxHeight(bbox1) = max(bboxHeight(bbox1), bboxHeight(bbox2));
548
    bboxDepth(bbox1)  = max(bboxDepth(bbox1), bboxDepth(bbox2));
549
    bboxWidth(bbox1)  = bboxWidth(bbox1) + bboxWidth(bbox2);
550
    bboxItalic(bbox1) = bboxItalic(bbox2);
551
    bboxSimple(bbox1) = bboxSimple(bbox2);
552
    return bbox1;
553
}
554
 
555
static BBOX CombineAlignedBBoxes(BBOX bbox1, BBOX bbox2)
556
{
557
    bboxHeight(bbox1) = max(bboxHeight(bbox1), bboxHeight(bbox2));
558
    bboxDepth(bbox1)  = max(bboxDepth(bbox1), bboxDepth(bbox2));
559
    bboxWidth(bbox1)  = max(bboxWidth(bbox1), bboxWidth(bbox2));
560
    bboxItalic(bbox1) = 0;
561
    bboxSimple(bbox1) = 0;
562
    return bbox1;
563
}
564
 
565
static BBOX CombineOffsetBBoxes(BBOX bbox1, int italic1,
566
				BBOX bbox2, int italic2,
567
				double xoffset,
568
				double yoffset)
569
{
570
    double width1 = bboxWidth(bbox1) + (italic1 ? bboxItalic(bbox1) : 0);
571
    double width2 = bboxWidth(bbox2) + (italic2 ? bboxItalic(bbox2) : 0);
572
    bboxWidth(bbox1) = max(width1, width2 + xoffset);
573
    bboxHeight(bbox1) = max(bboxHeight(bbox1), bboxHeight(bbox2) + yoffset);
574
    bboxDepth(bbox1) = max(bboxDepth(bbox1), bboxDepth(bbox2) - yoffset);
575
    bboxItalic(bbox1) = 0;
576
    bboxSimple(bbox1) = 0;
577
    return bbox1;
578
}
579
 
580
static double CenterShift(BBOX bbox)
581
{
582
    return 0.5 * (bboxHeight(bbox) - bboxDepth(bbox));
583
}
584
 
3475 pd 585
#ifdef NOT_used_currently/*-- out 'def'	 (-Wall) --*/
2028 ihaka 586
static BBOX DrawBBox(BBOX bbox, double xoffset, double yoffset)
587
{
588
    double xsaved = CurrentX;
589
    double ysaved = CurrentY;
590
    double x[5], y[5];
591
    CurrentX += xoffset;
592
    CurrentY += yoffset;
3244 ihaka 593
    PMoveUp(-bboxDepth(bbox));
2028 ihaka 594
    x[4] = x[0] = ConvertedX();
595
    y[4] = y[0] = ConvertedY();
3244 ihaka 596
    PMoveAcross(bboxWidth(bbox));
2028 ihaka 597
    x[1] = ConvertedX();
598
    y[1] = ConvertedY();
3244 ihaka 599
    PMoveUp(bboxHeight(bbox) + bboxDepth(bbox));
2028 ihaka 600
    x[2] = ConvertedX();
601
    y[2] = ConvertedY();
3244 ihaka 602
    PMoveAcross(-bboxWidth(bbox));
2028 ihaka 603
    x[3] = ConvertedX();
604
    y[3] = ConvertedY();
19875 murrell 605
    GEPolyline(5, x, y, BoxColor, MathGamma, LTY_SOLID, 1, INCHES, MathDevice);
3244 ihaka 606
    PMoveTo(xsaved, ysaved);
2028 ihaka 607
    return bbox;
608
}
2124 maechler 609
#endif
2028 ihaka 610
 
611
typedef struct {
612
    char *name;
613
    int code;
614
} SymTab;
615
 
616
/* Determine a match between symbol name and string. */
617
 
618
static int NameMatch(SEXP expr, char *aString)
619
{
6994 pd 620
    if (!isSymbol(expr)) return 0;
2028 ihaka 621
    return !strcmp(CHAR(PRINTNAME(expr)), aString);
622
}
623
 
624
static int StringMatch(SEXP expr, char *aString)
625
{
10172 luke 626
    return !strcmp(CHAR(STRING_ELT(expr, 0)), aString);
2028 ihaka 627
}
628
/* Code to determine the ascii code corresponding */
629
/* to an element of a mathematical expression. */
630
 
3475 pd 631
#define A_HAT		  94
632
#define A_TILDE		 126
2028 ihaka 633
 
3475 pd 634
#define S_SPACE		  32
635
#define S_PARENLEFT	  40
636
#define S_PARENRIGHT	  41
637
#define S_ASTERISKMATH	  42
638
#define S_COMMA		  44
639
#define S_SLASH		  47
640
#define S_RADICALEX	  96
641
#define S_FRACTION	 164
642
#define S_ELLIPSIS	 188
643
#define S_INTERSECTION	 199
644
#define S_UNION		 200
645
#define S_PRODUCT	 213
646
#define S_RADICAL	 214
647
#define S_SUM		 229
648
#define S_INTEGRAL	 242
649
#define S_BRACKETLEFTTP	 233
650
#define S_BRACKETLEFTBT	 235
2028 ihaka 651
#define S_BRACKETRIGHTTP 249
652
#define S_BRACKETRIGHTBT 251
653
 
3475 pd 654
#define N_LIM		1001
655
#define N_LIMINF	1002
656
#define N_LIMSUP	1003
657
#define N_INF		1004
658
#define N_SUP		1005
659
#define N_MIN		1006
660
#define N_MAX		1007
2028 ihaka 661
 
662
 
663
/* The Full Adobe Symbol Font */
664
 
7081 pd 665
static SymTab
1926 ihaka 666
SymbolTable[] = {
3865 pd 667
    { "space",		 32 },
668
    { "exclam",		 33 },
669
    { "universal",	 34 },
670
    { "numbersign",	 35 },
671
    { "existential",	 36 },
672
    { "percent",	 37 },
673
    { "ampersand",	 38 },
674
    { "suchthat",	 39 },
675
    { "parenleft",	 40 },
676
    { "parenright",	 41 },
677
    { "asteriskmath",	 42 },
678
    { "plus",		 43 },
679
    { "comma",		 44 },
680
    { "minus",		 45 },
681
    { "period",		 46 },
682
    { "slash",		 47 },
683
    { "0",		 48 },
684
    { "1",		 49 },
685
    { "2",		 50 },
686
    { "3",		 51 },
687
    { "4",		 52 },
688
    { "5",		 53 },
689
    { "6",		 54 },
690
    { "7",		 55 },
691
    { "8",		 56 },
692
    { "9",		 57 },
693
    { "colon",		 58 },
694
    { "semicolon",	 59 },
695
    { "less",		 60 },
696
    { "equal",		 61 },
697
    { "greater",	 62 },
698
    { "question",	 63 },
699
    { "congruent",	 64 },
2028 ihaka 700
 
7081 pd 701
    { "Alpha",/* 0101= */65 }, /* Upper Case Greek Characters */
3865 pd 702
    { "Beta",		 66 },
703
    { "Chi",		 67 },
704
    { "Delta",		 68 },
705
    { "Epsilon",	 69 },
706
    { "Phi",		 70 },
707
    { "Gamma",		 71 },
708
    { "Eta",		 72 },
709
    { "Iota",		 73 },
710
    { "theta1",		 74 },
711
    { "Kappa",		 75 },
712
    { "Lambda",		 76 },
713
    { "Mu",		 77 },
714
    { "Nu",		 78 },
715
    { "Omicron",	 79 },
716
    { "Pi",		 80 },
717
    { "Theta",		 81 },
718
    { "Rho",		 82 },
719
    { "Sigma",		 83 },
720
    { "Tau",		 84 },
721
    { "Upsilon",	 85 },
722
    { "sigma1",		 86 },
723
    { "Omega",		 87 },
724
    { "Xi",		 88 },
725
    { "Psi",		 89 },
7081 pd 726
    { "Zeta",/* 0132 = */90 },
2 r 727
 
3865 pd 728
    { "bracketleft",	 91 },	/* Miscellaneous Special Characters */
729
    { "therefore",	 92 },
730
    { "bracketright",	 93 },
731
    { "perpendicular",	 94 },
732
    { "underscore",	 95 },
733
    { "radicalex",	 96 },
2028 ihaka 734
 
7081 pd 735
    { "alpha",/* 0141= */97 },	/* Lower Case Greek Characters */
3865 pd 736
    { "beta",		 98 },
737
    { "chi",		 99 },
738
    { "delta",		100 },
739
    { "epsilon",	101 },
740
    { "phi",		102 },
741
    { "gamma",		103 },
742
    { "eta",		104 },
743
    { "iota",		105 },
744
    { "phi1",		106 },
745
    { "kappa",		107 },
746
    { "lambda",		108 },
747
    { "mu",		109 },
748
    { "nu",		110 },
749
    { "omicron",	111 },
750
    { "pi",		112 },
751
    { "theta",		113 },
752
    { "rho",		114 },
753
    { "sigma",		115 },
754
    { "tau",		116 },
755
    { "upsilon",	117 },
756
    { "omega1",		118 },
757
    { "omega",		119 },
758
    { "xi",		120 },
759
    { "psi",		121 },
7081 pd 760
    { "zeta",/* 0172= */122 },
2 r 761
 
3865 pd 762
    { "braceleft",	123 },	/* Miscellaneous Special Characters */
763
    { "bar",		124 },
764
    { "braceright",	125 },
765
    { "similar",	126 },
2028 ihaka 766
 
3865 pd 767
    { "Upsilon1",	161 },	/* Lone Greek */
768
    { "minute",		162 },
769
    { "lessequal",	163 },
770
    { "fraction",	164 },
771
    { "infinity",	165 },
772
    { "florin",		166 },
773
    { "club",		167 },
774
    { "diamond",	168 },
775
    { "heart",		169 },
776
    { "spade",		170 },
777
    { "arrowboth",	171 },
778
    { "arrowleft",	172 },
779
    { "arrowup",	173 },
780
    { "arrowright",	174 },
781
    { "arrowdown",	175 },
782
    { "degree",		176 },
783
    { "plusminus",	177 },
784
    { "second",		178 },
785
    { "greaterequal",	179 },
786
    { "multiply",	180 },
787
    { "proportional",	181 },
788
    { "partialdiff",	182 },
789
    { "bullet",		183 },
790
    { "divide",		184 },
791
    { "notequal",	185 },
792
    { "equivalence",	186 },
793
    { "approxequal",	187 },
794
    { "ellipsis",	188 },
795
    { "arrowvertex",	189 },
796
    { "arrowhorizex",	190 },
797
    { "carriagereturn", 191 },
798
    { "aleph",		192 },
799
    { "Ifraktur",	193 },
800
    { "Rfraktur",	194 },
801
    { "weierstrass",	195 },
802
    { "circlemultiply", 196 },
803
    { "circleplus",	197 },
804
    { "emptyset",	198 },
7081 pd 805
    { "intersection",	199 },/* = 0307 */
806
    { "union",		200 },/* = 0310 */
3865 pd 807
    { "propersuperset", 201 },
808
    { "reflexsuperset", 202 },
809
    { "notsubset",	203 },
810
    { "propersubset",	204 },
811
    { "reflexsubset",	205 },
812
    { "element",	206 },
813
    { "notelement",	207 },
814
    { "angle",		208 },
815
    { "gradient",	209 },
816
    { "registerserif",	210 },
817
    { "copyrightserif", 211 },
818
    { "trademarkserif", 212 },
819
    { "product",	213 },
820
    { "radical",	214 },
821
    { "dotmath",	215 },
822
    { "logicaland",	217 },
823
    { "logicalor",	218 },
824
    { "arrowdblboth",	219 },
825
    { "arrowdblleft",	220 },
826
    { "arrowdblup",	221 },
827
    { "arrowdblright",	222 },
828
    { "arrowdbldown",	223 },
829
    { "lozenge",	224 },
830
    { "angleleft",	225 },
831
    { "registersans",	226 },
832
    { "copyrightsans",	227 },
833
    { "trademarksans",	228 },
834
    { "summation",	229 },
835
    { "parenlefttp",	230 },
836
    { "parenleftex",	231 },
837
    { "parenleftbt",	232 },
838
    { "bracketlefttp",	233 },
839
    { "bracketleftex",	234 },
840
    { "bracketleftbt",	235 },
841
    { "bracelefttp",	236 },
842
    { "braceleftmid",	237 },
843
    { "braceleftbt",	238 },
844
    { "braceex",	239 },
845
    { "angleright",	241 },
846
    { "integral",	242 },
847
    { "integraltp",	243 },
848
    { "integralex",	244 },
849
    { "integralbt",	245 },
850
    { "parenrighttp",	246 },
851
    { "parenrightex",	247 },
852
    { "parenrightbt",	248 },
853
    { "bracketrighttp", 249 },
854
    { "bracketrightex", 250 },
855
    { "bracketrightbt", 251 },
856
    { "bracerighttp",	252 },
857
    { "bracerightmid",	253 },
858
    { "bracerightbt",	254 },
2 r 859
 
3865 pd 860
    { NULL,		  0 },
2 r 861
};
862
 
2028 ihaka 863
static int SymbolCode(SEXP expr)
2 r 864
{
1839 ihaka 865
    int i;
1926 ihaka 866
    for (i = 0; SymbolTable[i].code; i++)
2028 ihaka 867
	if (NameMatch(expr, SymbolTable[i].name))
1926 ihaka 868
	    return SymbolTable[i].code;
1839 ihaka 869
    return 0;
870
}
2 r 871
 
2028 ihaka 872
static int TranslatedSymbol(SEXP expr)
1839 ihaka 873
{
2028 ihaka 874
    int code = SymbolCode(expr);
3475 pd 875
    if ((0101 <= code && code <= 0132)	||   /* Greek */
876
	(0141 <= code && code <= 0172)	||   /* Greek */
7081 pd 877
	code == 0241			||   /* Upsilon1 */
3475 pd 878
	code == 0242			||   /* minute */
879
	code == 0245			||   /* infinity */
880
	code == 0260			||   /* degree */
881
	code == 0262			||   /* second */
19598 ripley 882
	code == 0266                    ||   /* partialdiff */
2028 ihaka 883
	0)
884
	return code;
885
    else
886
	return 0;
2 r 887
}
888
 
1839 ihaka 889
/* Code to determine the nature of an expression. */
2 r 890
 
2028 ihaka 891
static int FormulaExpression(SEXP expr)
2 r 892
{
1839 ihaka 893
    return (TYPEOF(expr) == LANGSXP);
2 r 894
}
895
 
2028 ihaka 896
static int NameAtom(SEXP expr)
2 r 897
{
1839 ihaka 898
    return (TYPEOF(expr) == SYMSXP);
2 r 899
}
900
 
2028 ihaka 901
static int NumberAtom(SEXP expr)
2 r 902
{
1839 ihaka 903
    return ((TYPEOF(expr) == REALSXP) ||
904
	    (TYPEOF(expr) == INTSXP)  ||
905
	    (TYPEOF(expr) == CPLXSXP));
2 r 906
}
907
 
2028 ihaka 908
static int StringAtom(SEXP expr)
2 r 909
{
1839 ihaka 910
    return (TYPEOF(expr) == STRSXP);
2 r 911
}
912
 
3475 pd 913
#ifdef NOT_used_currently/*-- out 'def'	 (-Wall) --*/
2028 ihaka 914
static int symbolAtom(SEXP expr)
1926 ihaka 915
{
916
    int i;
2028 ihaka 917
    if (NameAtom(expr)) {
918
	for (i = 0; SymbolTable[i].code; i++)
919
	    if (NameMatch(expr, SymbolTable[i].name))
920
		return 1;
921
    }
1926 ihaka 922
    return 0;
2 r 923
}
2124 maechler 924
#endif
2028 ihaka 925
/* Code to determine a font from the */
926
/* nature of the expression */
2 r 927
 
3475 pd 928
#ifdef NOT_used_currently/*-- out 'def'	 (-Wall) --*/
2028 ihaka 929
static FontType CurrentFont = 3;
2124 maechler 930
#endif
2028 ihaka 931
static FontType GetFont()
2 r 932
{
19875 murrell 933
    return MathFont;
2 r 934
}
935
 
2028 ihaka 936
static FontType SetFont(FontType font)
2 r 937
{
19875 murrell 938
    FontType prevfont = MathFont;
939
    MathFont = font;
2028 ihaka 940
    return prevfont;
2 r 941
}
942
 
2028 ihaka 943
static int UsingItalics()
2 r 944
{
19875 murrell 945
    return (MathFont == ItalicFont ||
946
	    MathFont == BoldItalicFont);
2 r 947
}
948
 
2028 ihaka 949
static BBOX GlyphBBox(int chr)
2 r 950
{
2028 ihaka 951
    BBOX bbox;
952
    double height, depth, width;
19875 murrell 953
    GEMetricInfo(chr, 
954
		MathFont, MathCex, MathPs,
955
		&height, &depth, &width, MathDevice);
956
    bboxHeight(bbox) = fromDeviceHeight(height, MetricUnit, MathDevice);
957
    bboxDepth(bbox)  = fromDeviceHeight(depth, MetricUnit, MathDevice);
958
    bboxWidth(bbox)  = fromDeviceHeight(width, MetricUnit, MathDevice);
2028 ihaka 959
    bboxItalic(bbox) = 0;
960
    bboxSimple(bbox) = 1;
961
    return bbox;
2 r 962
}
963
 
2028 ihaka 964
static BBOX RenderElement(SEXP, int);
965
static BBOX RenderOffsetElement(SEXP, double, double, int);
966
static BBOX RenderExpression(SEXP, int);
967
static BBOX RenderSymbolChar(int, int);
2 r 968
 
969
 
3475 pd 970
/*  Code to Generate Bounding Boxes and Draw Formulae.	*/
2 r 971
 
2028 ihaka 972
static BBOX RenderItalicCorr(BBOX bbox, int draw)
2 r 973
{
2028 ihaka 974
    if (bboxItalic(bbox) > 0) {
975
	if (draw)
3244 ihaka 976
	    PMoveAcross(bboxItalic(bbox));
2028 ihaka 977
	bboxWidth(bbox) += bboxItalic(bbox);
978
	bboxItalic(bbox) = 0;
979
    }
980
    return bbox;
2 r 981
}
982
 
2028 ihaka 983
static BBOX RenderGap(double gap, int draw)
2 r 984
{
2028 ihaka 985
    if (draw)
3244 ihaka 986
	PMoveAcross(gap);
2028 ihaka 987
    return MakeBBox(0, 0, gap);
2 r 988
}
989
 
2028 ihaka 990
/* Draw a Symbol from the Special Font */
2 r 991
 
2028 ihaka 992
static BBOX RenderSymbolChar(int ascii, int draw)
2 r 993
{
2028 ihaka 994
    FontType prev;
995
    BBOX bbox;
996
    char asciiStr[2];
997
    if (ascii == A_HAT || ascii == A_TILDE)
998
	prev = SetFont(PlainFont);
999
    else
1000
	prev = SetFont(SymbolFont);
1001
    bbox = GlyphBBox(ascii);
1002
    if (draw) {
1003
	asciiStr[0] = ascii;
1004
	asciiStr[1] = '\0';
19875 murrell 1005
	GEText(ConvertedX(), ConvertedY(), asciiStr, 
21062 murrell 1006
	       0.0, 0.0, CurrentAngle, 
1007
	       TextColor, MathGamma, 
1008
	       MathFontFamily, MathFont, MathLineHeight, MathCex, MathPs,
1009
	       MathDevice);
3244 ihaka 1010
	PMoveAcross(bboxWidth(bbox));
2028 ihaka 1011
    }
1012
    SetFont(prev);
1013
    return bbox;
2 r 1014
}
1015
 
2028 ihaka 1016
/* Draw a Symbol String in "Math Mode */
1017
/* This code inserts italic corrections after */
1018
/* every character. */
2 r 1019
 
2028 ihaka 1020
static BBOX RenderSymbolStr(char *str, int draw)
2 r 1021
{
2028 ihaka 1022
    char chr[2];
1023
    BBOX glyphBBox;
1024
    BBOX resultBBox = NullBBox();
1025
    double lastItalicCorr = 0;
1026
    FontType prevfont = GetFont();
1027
    FontType font = prevfont;
1028
    chr[1] = '\0';
1029
    if (str) {
1030
	char *s = str;
1031
	while (*s) {
6098 pd 1032
	    if (isdigit((int)*s) && font != PlainFont) {
2028 ihaka 1033
		font = PlainFont;
1034
		SetFont(PlainFont);
1035
	    }
1036
	    else if (font != prevfont) {
1037
		font = prevfont;
1038
		SetFont(prevfont);
1039
	    }
1040
	    glyphBBox = GlyphBBox(*s);
1041
	    if (UsingItalics())
1042
		bboxItalic(glyphBBox) = ItalicFactor * bboxHeight(glyphBBox);
1043
	    else
1044
		bboxItalic(glyphBBox) = 0;
1045
	    if (draw) {
1046
		chr[0] = *s;
3244 ihaka 1047
		PMoveAcross(lastItalicCorr);
19875 murrell 1048
		GEText(ConvertedX(), ConvertedY(), chr,
21062 murrell 1049
		       0.0, 0.0, CurrentAngle, 
1050
		       TextColor, MathGamma, 
1051
		       MathFontFamily, MathFont, MathLineHeight, 
1052
		       MathCex, MathPs,
1053
		       MathDevice);
3244 ihaka 1054
		PMoveAcross(bboxWidth(glyphBBox));
2028 ihaka 1055
	    }
1056
	    bboxWidth(resultBBox) += lastItalicCorr;
1057
	    resultBBox = CombineBBoxes(resultBBox, glyphBBox);
1058
	    lastItalicCorr = bboxItalic(glyphBBox);
1059
	    s++;
1060
	}
1061
	if (font != prevfont)
1062
	    SetFont(prevfont);
1063
    }
1064
    bboxSimple(resultBBox) = 1;
1065
    return resultBBox;
2 r 1066
}
1067
 
2028 ihaka 1068
/* Code for Character String Atoms. */
2 r 1069
 
2028 ihaka 1070
static BBOX RenderChar(int ascii, int draw)
2 r 1071
{
2028 ihaka 1072
    BBOX bbox;
1073
    char asciiStr[2];
1074
    bbox = GlyphBBox(ascii);
1075
    if (draw) {
1076
	asciiStr[0] = ascii;
1077
	asciiStr[1] = '\0';
19875 murrell 1078
	GEText(ConvertedX(), ConvertedY(), asciiStr,
21062 murrell 1079
	       0.0, 0.0, CurrentAngle, 
1080
	       TextColor, MathGamma, 
1081
	       MathFontFamily, MathFont, MathLineHeight, MathCex, MathPs,
1082
	       MathDevice);
3244 ihaka 1083
	PMoveAcross(bboxWidth(bbox));
2028 ihaka 1084
    }
1085
    return bbox;
2 r 1086
}
1087
 
2028 ihaka 1088
static BBOX RenderStr(char *str, int draw)
2 r 1089
{
2028 ihaka 1090
    BBOX glyphBBox;
1091
    BBOX resultBBox = NullBBox();
1092
    if (str) {
1093
	char *s = str;
1094
	while (*s) {
1095
	    glyphBBox = GlyphBBox(*s);
1096
	    resultBBox = CombineBBoxes(resultBBox, glyphBBox);
1097
	    s++;
1098
	}
1099
	if (draw) {
19875 murrell 1100
	    GEText(ConvertedX(), ConvertedY(), str,
21062 murrell 1101
		   0.0, 0.0, CurrentAngle, 
1102
		   TextColor, MathGamma, 
1103
		   MathFontFamily, MathFont, MathLineHeight, 
1104
		   MathCex, MathPs,
1105
		   MathDevice);
3244 ihaka 1106
	    PMoveAcross(bboxWidth(resultBBox));
2028 ihaka 1107
	}
1108
	if (UsingItalics())
1109
	    bboxItalic(resultBBox) = ItalicFactor * bboxHeight(glyphBBox);
1110
	else
1111
	    bboxItalic(resultBBox) = 0;
1112
    }
1113
    bboxSimple(resultBBox) = 1;
1114
    return resultBBox;
2 r 1115
}
1116
 
1117
 
2028 ihaka 1118
/* Code for Symbol Font Atoms */
2 r 1119
 
2028 ihaka 1120
static BBOX RenderSymbol(SEXP expr, int draw)
2 r 1121
{
2028 ihaka 1122
    int code;
2124 maechler 1123
    if ((code = TranslatedSymbol(expr)))
2028 ihaka 1124
	return RenderSymbolChar(code, draw);
1125
    else
1126
	return RenderSymbolStr(CHAR(PRINTNAME(expr)), draw);
2 r 1127
}
1128
 
2028 ihaka 1129
static BBOX RenderSymbolString(SEXP expr, int draw)
2 r 1130
{
2028 ihaka 1131
    int code;
2124 maechler 1132
    if ((code = TranslatedSymbol(expr)))
2028 ihaka 1133
	return RenderSymbolChar(code, draw);
1134
    else
1135
	return RenderStr(CHAR(PRINTNAME(expr)), draw);
2 r 1136
}
1137
 
1138
 
2028 ihaka 1139
/* Code for Numeric Atoms */
2 r 1140
 
2028 ihaka 1141
static BBOX RenderNumber(SEXP expr, int draw)
2 r 1142
{
2028 ihaka 1143
    BBOX bbox;
1144
    FontType prevfont = SetFont(PlainFont);
1145
    bbox = RenderStr(CHAR(asChar(expr)), draw);
1146
    SetFont(prevfont);
1147
    return bbox;
2 r 1148
}
1149
 
2028 ihaka 1150
/* Code for String Atoms */
2 r 1151
 
2028 ihaka 1152
static BBOX RenderString(SEXP expr, int draw)
2 r 1153
{
10172 luke 1154
    return RenderStr(CHAR(STRING_ELT(expr, 0)), draw);
2 r 1155
}
1156
 
2028 ihaka 1157
/* Code for Ellipsis (ldots, cdots, ...) */
2 r 1158
 
2028 ihaka 1159
static int DotsAtom(SEXP expr)
2 r 1160
{
2028 ihaka 1161
    if (NameMatch(expr, "cdots") ||
3475 pd 1162
	NameMatch(expr, "...")	 ||
2028 ihaka 1163
	NameMatch(expr, "ldots"))
3475 pd 1164
	    return 1;
2028 ihaka 1165
    return 0;
2 r 1166
}
1167
 
2028 ihaka 1168
static BBOX RenderDots(SEXP expr, int draw)
2 r 1169
{
2028 ihaka 1170
    BBOX bbox = RenderSymbolChar(S_ELLIPSIS, 0);
1171
    if (NameMatch(expr, "cdots") || NameMatch(expr, "...")) {
1172
	double shift = AxisHeight() - 0.5 * bboxHeight(bbox);
1173
	if (draw) {
3244 ihaka 1174
	    PMoveUp(shift);
2028 ihaka 1175
	    RenderSymbolChar(S_ELLIPSIS, 1);
3244 ihaka 1176
	    PMoveUp(-shift);
2028 ihaka 1177
	}
1178
	return ShiftBBox(bbox, shift);
1179
    }
1180
    else {
1181
	if (draw)
1182
	    RenderSymbolChar(S_ELLIPSIS, 1);
1183
	return bbox;
1184
    }
2 r 1185
}
1186
 
2028 ihaka 1187
/*----------------------------------------------------------------------
1188
 *
1189
 *  Code for Atoms
1190
 *
1191
 */
2 r 1192
 
2028 ihaka 1193
static BBOX RenderAtom(SEXP expr, int draw)
2 r 1194
{
2028 ihaka 1195
    if (NameAtom(expr)) {
1196
	if (DotsAtom(expr))
1197
	    return RenderDots(expr, draw);
1198
	else
1199
	    return RenderSymbol(expr, draw);
1200
    }
1201
    else if (NumberAtom(expr))
1202
	return RenderNumber(expr, draw);
1203
    else if (StringAtom(expr))
1204
	return RenderString(expr, draw);
3865 pd 1205
 
1206
    return NullBBox();		/* -Wall */
2 r 1207
}
1208
 
1209
 
2028 ihaka 1210
/*----------------------------------------------------------------------
1211
 *
1212
 *  Code for Binary / Unary Operators  (~, +, -, ... )
1213
 *
1214
 *  Note that there are unary and binary ~ s.
1215
 *
1216
 */
2 r 1217
 
2028 ihaka 1218
static int SpaceAtom(SEXP expr)
2 r 1219
{
2028 ihaka 1220
    return NameAtom(expr) && NameMatch(expr, "~");
2 r 1221
}
1222
 
1223
 
2028 ihaka 1224
static BBOX RenderSpace(SEXP expr, int draw)
2 r 1225
{
2124 maechler 1226
 
2028 ihaka 1227
    BBOX opBBox, arg1BBox, arg2BBox;
1228
    int nexpr = length(expr);
2 r 1229
 
2028 ihaka 1230
    if (nexpr == 2) {
1231
	opBBox = RenderSymbolChar(' ', draw);
1232
	arg1BBox = RenderElement(CADR(expr), draw);
1233
	return CombineBBoxes(opBBox, arg1BBox);
1234
    }
1235
    else if (nexpr == 3) {
1236
	arg1BBox = RenderElement(CADR(expr), draw);
1237
	opBBox = RenderSymbolChar(' ', draw);
1238
	arg2BBox = RenderElement(CADDR(expr), draw);
1239
	opBBox = CombineBBoxes(arg1BBox, opBBox);
1240
	opBBox = CombineBBoxes(opBBox, arg2BBox);
1241
	return opBBox;
1242
    }
1839 ihaka 1243
    else
5731 ripley 1244
	error("invalid mathematical annotation");
3865 pd 1245
 
1246
    return NullBBox();		/* -Wall */
2 r 1247
}
1248
 
2028 ihaka 1249
static SymTab BinTable[] = {
3865 pd 1250
    { "*",		 052 },	/* Binary Operators */
1251
    { "+",		 053 },
1252
    { "-",		 055 },
1253
    { "/",		 057 },
1254
    { ":",		 072 },
1255
    { "%+-%",		0261 },
1256
    { "%*%",		0264 },
1257
    { "%/%",		0270 },
1258
    { "%intersection%", 0307 },
1259
    { "%union%",	0310 },
1260
    { NULL,		   0 }
2028 ihaka 1261
};
2 r 1262
 
2028 ihaka 1263
static int BinAtom(SEXP expr)
2 r 1264
{
2028 ihaka 1265
    int i;
7081 pd 1266
 
2028 ihaka 1267
    for (i = 0; BinTable[i].code; i++)
1268
	if (NameMatch(expr, BinTable[i].name))
1269
	    return BinTable[i].code;
1270
    return 0;
2 r 1271
}
1272
 
2028 ihaka 1273
#define SLASH2
2 r 1274
 
2028 ihaka 1275
static BBOX RenderSlash(int draw)
2 r 1276
{
2028 ihaka 1277
#ifdef SLASH0
1278
    /* The Default Font Character */
1279
    return RenderSymbolChar(S_SLASH, draw);
2 r 1280
#endif
2028 ihaka 1281
#ifdef SLASH1
1282
    /* Symbol Magnify Version */
19875 murrell 1283
    double savecex = MathCex;
2028 ihaka 1284
    BBOX bbox;
1285
    double height1, height2;
1286
    height1 = bboxHeight(RenderSymbolChar(S_SLASH, 0));
19875 murrell 1287
    MathCex = 1.2 * MathCex;
2028 ihaka 1288
    height2 = bboxHeight(RenderSymbolChar(S_SLASH, 0));
1289
    if (draw)
3244 ihaka 1290
	PMoveUp(- 0.5 * (height2 - height1));
2028 ihaka 1291
    bbox = RenderSymbolChar(S_SLASH, draw);
1292
    if (draw)
3244 ihaka 1293
	PMoveUp(0.5 * (height2 - height1));
19875 murrell 1294
    MathCex = savecex;
2028 ihaka 1295
    return bbox;
2 r 1296
#endif
2028 ihaka 1297
#ifdef SLASH2
1298
    /* Line Drawing Version */
1299
    double x[2], y[2];
1300
    double depth = 0.5 * TeX(sigma22);
1301
    double height = XHeight() + 0.5 * TeX(sigma22);
1302
    double width = 0.5 * xHeight();
1303
    if (draw) {
3244 ihaka 1304
	PMoveAcross(0.5 * width);
1305
	PMoveUp(-depth);
2028 ihaka 1306
	x[0] = ConvertedX();
1307
	y[0] = ConvertedY();
3244 ihaka 1308
	PMoveAcross(width);
1309
	PMoveUp(depth + height);
2028 ihaka 1310
	x[1] = ConvertedX();
1311
	y[1] = ConvertedY();
3244 ihaka 1312
	PMoveUp(-height);
19875 murrell 1313
	GEPolyline(2, x, y,
1314
		  TextColor, MathGamma, LTY_SOLID, 1, MathDevice);
3244 ihaka 1315
	PMoveAcross(0.5 * width);
2028 ihaka 1316
    }
1317
    return MakeBBox(height, depth, 2 * width);
2 r 1318
#endif
2028 ihaka 1319
#ifdef SLASH3
1320
    /* Offset Overprinting - A Failure! */
1321
    BBOX slashBBox = RenderSymbolChar(S_SLASH, 0);
1322
    BBOX ansBBox;
1323
    double height = bboxHeight(slashBBox);
1324
    double depth = bboxDepth(slashBBox);
1325
    double width = bboxWidth(slashBBox);
1326
    double slope = (height + depth) / slope;
1327
    double delta = TeX(sigma22);
1328
    if (draw)
3244 ihaka 1329
	PMoveUp(-delta);
2028 ihaka 1330
    ansBBox = ShiftBBox(RenderSymbolChar(S_SLASH, draw), -delta);
3244 ihaka 1331
    PMoveUp(2 * delta);
2028 ihaka 1332
    ansBBox = CombineBBoxes(ansBBox, RenderGap(2 * delta / slope, draw));
1333
    ansBBox = ShiftBBox(RenderSymbolChar(S_SLASH, draw), 2 * delta);
3244 ihaka 1334
    PMoveUp(-delta);
2028 ihaka 1335
    return ansBBox;
2 r 1336
#endif
1337
}
1338
 
2028 ihaka 1339
static BBOX RenderBin(SEXP expr, int draw)
2 r 1340
{
2028 ihaka 1341
    int op = BinAtom(CAR(expr));
1342
    int nexpr = length(expr);
1343
    BBOX bbox;
1344
    double gap;
2 r 1345
 
2028 ihaka 1346
    if(nexpr == 3) {
1347
	if (op == S_ASTERISKMATH) {
1348
	    bbox = RenderElement(CADR(expr), draw);
1349
	    bbox = RenderItalicCorr(bbox, draw);
1350
	    return CombineBBoxes(bbox, RenderElement(CADDR(expr), draw));
1351
	}
1352
	else if (op == S_SLASH) {
1353
	    gap = 0;
1354
	    bbox = RenderElement(CADR(expr), draw);
1355
	    bbox = RenderItalicCorr(bbox, draw);
1356
	    bbox = CombineBBoxes(bbox, RenderGap(gap, draw));
1357
	    bbox = CombineBBoxes(bbox, RenderSlash(draw));
1358
	    bbox = CombineBBoxes(bbox, RenderGap(gap, draw));
1359
	    return CombineBBoxes(bbox, RenderElement(CADDR(expr), draw));
1360
	}
1361
	else {
2122 maechler 1362
	    gap = (CurrentStyle > STYLE_S) ? MediumSpace() : 0;
2028 ihaka 1363
	    bbox = RenderElement(CADR(expr), draw);
1364
	    bbox = RenderItalicCorr(bbox, draw);
1365
	    bbox = CombineBBoxes(bbox, RenderGap(gap, draw));
1366
	    bbox = CombineBBoxes(bbox, RenderSymbolChar(op, draw));
1367
	    bbox = CombineBBoxes(bbox, RenderGap(gap, draw));
1368
	    return CombineBBoxes(bbox, RenderElement(CADDR(expr), draw));
1369
	}
1839 ihaka 1370
    }
2028 ihaka 1371
    else if(nexpr == 2) {
1372
	gap = (CurrentStyle > STYLE_S) ? ThinSpace() : 0;
1373
	bbox = RenderSymbolChar(op, draw);
1374
	bbox = CombineBBoxes(bbox, RenderGap(gap, draw));
1375
	return CombineBBoxes(bbox, RenderElement(CADR(expr), draw));
1839 ihaka 1376
    }
3865 pd 1377
    else
5731 ripley 1378
	error("invalid mathematical annotation");
3865 pd 1379
 
1380
    return NullBBox();		/* -Wall */
1381
 
2 r 1382
}
1383
 
1384
 
2028 ihaka 1385
/*----------------------------------------------------------------------
1386
 *
1387
 *  Code for Subscript and Superscipt Expressions
1388
 *
1389
 *  Rules 18, 18a, ..., 18f of the TeXBook.
1390
 *
1391
 */
2 r 1392
 
2028 ihaka 1393
static int SuperAtom(SEXP expr)
2 r 1394
{
2028 ihaka 1395
    return NameAtom(expr) && NameMatch(expr, "^");
2 r 1396
}
1397
 
2028 ihaka 1398
static int SubAtom(SEXP expr)
2 r 1399
{
2028 ihaka 1400
    return NameAtom(expr) && NameMatch(expr, "[");
2 r 1401
}
1402
 
2028 ihaka 1403
/* Note : If all computations are correct */
1404
/* We do not need to save and restore the */
1405
/* current location here.  This is paranoia. */
1406
static BBOX RenderSub(SEXP expr, int draw)
2 r 1407
{
2028 ihaka 1408
    BBOX bodyBBox, subBBox;
1409
    SEXP body = CADR(expr);
1410
    SEXP sub = CADDR(expr);
1411
    STYLE style = GetStyle();
1412
    double savedX = CurrentX;
1413
    double savedY = CurrentY;
2124 maechler 1414
    double v, s5, s16;
2028 ihaka 1415
    bodyBBox = RenderElement(body, draw);
1416
    bodyBBox = RenderItalicCorr(bodyBBox, draw);
1417
    v = bboxSimple(bodyBBox) ? 0 : bboxDepth(bodyBBox) + TeX(sigma19);
1418
    s5 = TeX(sigma5);
1419
    s16 = TeX(sigma16);
1420
    SetSubStyle(style);
1421
    subBBox = RenderElement(sub, 0);
1422
    v = max(max(v, s16), bboxHeight(subBBox) - 0.8 * sigma5);
1423
    subBBox = RenderOffsetElement(sub, 0, -v, draw);
1424
    bodyBBox = CombineBBoxes(bodyBBox, subBBox);
1425
    SetStyle(style);
1426
    if (draw)
3244 ihaka 1427
	PMoveTo(savedX + bboxWidth(bodyBBox), savedY);
2028 ihaka 1428
    return bodyBBox;
2 r 1429
}
1430
 
2028 ihaka 1431
static BBOX RenderSup(SEXP expr, int draw)
2 r 1432
{
2028 ihaka 1433
    BBOX bodyBBox, subBBox, supBBox;
1434
    SEXP body = CADR(expr);
1435
    SEXP sup = CADDR(expr);
3865 pd 1436
    SEXP sub = R_NilValue;	/* -Wall */
2028 ihaka 1437
    STYLE style = GetStyle();
1438
    double savedX = CurrentX;
1439
    double savedY = CurrentY;
1440
    double theta, delta, width;
1441
    double u, p;
1442
    double v, s5, s17;
1443
    int haveSub;
1444
    if (FormulaExpression(body) && SubAtom(CAR(body))) {
1445
	sub = CADDR(body);
1446
	body = CADR(body);
1447
	haveSub = 1;
1448
    }
1449
    else haveSub = 0;
1450
    bodyBBox = RenderElement(body, draw);
1451
    delta = bboxItalic(bodyBBox);
1452
    bodyBBox = RenderItalicCorr(bodyBBox, draw);
1453
    width = bboxWidth(bodyBBox);
1454
    if (bboxSimple(bodyBBox)) {
1455
	u = 0;
1456
	v = 0;
1457
    }
1458
    else {
1459
	u = bboxHeight(bodyBBox) - TeX(sigma18);
1460
	v = bboxDepth(bodyBBox) + TeX(sigma19);
1461
    }
1462
    theta = TeX(xi8);
1463
    s5 = TeX(sigma5);
1464
    s17 = TeX(sigma17);
1465
    if (style == STYLE_D)
1466
	p = TeX(sigma13);
1467
    else if (IsCompactStyle(style))
1468
	p = TeX(sigma15);
1839 ihaka 1469
    else
2028 ihaka 1470
	p = TeX(sigma14);
1471
    SetSupStyle(style);
1472
    supBBox = RenderElement(sup, 0);
1473
    u = max(max(u, p), bboxDepth(supBBox) + 0.25 * s5);
2 r 1474
 
2028 ihaka 1475
    if (haveSub) {
1476
	SetSubStyle(style);
1477
	subBBox = RenderElement(sub, 0);
1478
	v = max(v, s17);
1479
	if ((u - bboxDepth(supBBox)) - (bboxHeight(subBBox) - v) < 4 * theta) {
1480
	    double psi = 0.8 * s5 - (u - bboxDepth(supBBox));
1481
	    if (psi > 0) {
1482
		u += psi;
1483
		v -= psi;
1484
	    }
1485
	}
1486
	if (draw)
3244 ihaka 1487
	    PMoveTo(savedX, savedY);
2028 ihaka 1488
	subBBox = RenderOffsetElement(sub, width, -v, draw);
1489
	if (draw)
3244 ihaka 1490
	    PMoveTo(savedX, savedY);
2028 ihaka 1491
	SetSupStyle(style);
1492
	supBBox = RenderOffsetElement(sup, width + delta, u, draw);
1493
	bodyBBox = CombineAlignedBBoxes(bodyBBox, subBBox);
1494
	bodyBBox = CombineAlignedBBoxes(bodyBBox, supBBox);
1495
    }
1496
    else {
1497
	supBBox = RenderOffsetElement(sup, 0, u, draw);
1498
	bodyBBox = CombineBBoxes(bodyBBox, supBBox);
1499
    }
1500
    if (draw)
3244 ihaka 1501
	PMoveTo(savedX + bboxWidth(bodyBBox), savedY);
2028 ihaka 1502
    SetStyle(style);
1503
    return bodyBBox;
2 r 1504
}
1505
 
1506
 
2028 ihaka 1507
/*----------------------------------------------------------------------
1508
 *
1509
 *  Code for Accented Expressions (widehat, bar, widetilde, ...)
1510
 *
1511
 */
2 r 1512
 
2028 ihaka 1513
#define ACCENT_GAP  0.2
1514
#define HAT_HEIGHT  0.3
2 r 1515
 
3475 pd 1516
#define NTILDE	    8
1517
#define DELTA	    0.05
2 r 1518
 
2028 ihaka 1519
static int WideTildeAtom(SEXP expr)
2 r 1520
{
2028 ihaka 1521
    return NameAtom(expr) && NameMatch(expr, "widetilde");
2 r 1522
}
1523
 
2028 ihaka 1524
static BBOX RenderWideTilde(SEXP expr, int draw)
2 r 1525
{
2028 ihaka 1526
    double savedX = CurrentX;
1527
    double savedY = CurrentY;
1528
    BBOX bbox = RenderElement(CADR(expr), draw);
1529
    double height = bboxHeight(bbox);
2124 maechler 1530
    /*double width = bboxWidth(bbox);*/
2028 ihaka 1531
    double totalwidth = bboxWidth(bbox) + bboxItalic(bbox);
1532
    double delta = totalwidth * (1 - 2 * DELTA) / NTILDE;
1533
    double start = DELTA * totalwidth;
2105 ihaka 1534
    double accentGap = ACCENT_GAP * XHeight();
1535
    double hatHeight = 0.5 * HAT_HEIGHT * XHeight();
2028 ihaka 1536
    double c = 8 * atan(1.0) / NTILDE;
1537
    double x[NTILDE + 3], y[NTILDE + 3];
1538
    double baseX, baseY, xval, yval;
1539
    int i;
2 r 1540
 
2028 ihaka 1541
    if (draw) {
1542
	baseX = savedX;
1543
	baseY = savedY + height + accentGap;
3244 ihaka 1544
	PMoveTo(baseX, baseY);
2028 ihaka 1545
	x[0] = ConvertedX();
1546
	y[0] = ConvertedY();
1547
	for (i = 0; i <= NTILDE; i++) {
1548
	    xval = start + i * delta;
1549
	    yval = 0.5 * hatHeight * (sin(c * i) + 1);
3244 ihaka 1550
	    PMoveTo(baseX + xval, baseY + yval);
2028 ihaka 1551
	    x[i + 1] = ConvertedX();
1552
	    y[i + 1] = ConvertedY();
1553
	}
3244 ihaka 1554
	PMoveTo(baseX + totalwidth, baseY + hatHeight);
2028 ihaka 1555
	x[NTILDE + 2] = ConvertedX();
1556
	y[NTILDE + 2] = ConvertedY();
19875 murrell 1557
	GEPolyline(NTILDE + 3, x, y, 
1558
		  TextColor, MathGamma, LTY_SOLID, 1, MathDevice);
3244 ihaka 1559
	PMoveTo(savedX + totalwidth, savedY);
2028 ihaka 1560
    }
1561
    return MakeBBox(height + accentGap + hatHeight,
1562
		    bboxDepth(bbox), totalwidth);
2 r 1563
}
1564
 
2028 ihaka 1565
static int WideHatAtom(SEXP expr)
2 r 1566
{
2028 ihaka 1567
    return NameAtom(expr) && NameMatch(expr, "widehat");
2 r 1568
}
1569
 
2028 ihaka 1570
static BBOX RenderWideHat(SEXP expr, int draw)
2 r 1571
{
2028 ihaka 1572
    double savedX = CurrentX;
1573
    double savedY = CurrentY;
1574
    BBOX bbox = RenderElement(CADR(expr), draw);
2105 ihaka 1575
    double accentGap = ACCENT_GAP * XHeight();
1576
    double hatHeight = HAT_HEIGHT * XHeight();
2028 ihaka 1577
    double totalwidth = bboxWidth(bbox) + bboxItalic(bbox);
1578
    double height = bboxHeight(bbox);
1579
    double width = bboxWidth(bbox);
1580
    double x[3], y[3];
2 r 1581
 
2028 ihaka 1582
    if (draw) {
3244 ihaka 1583
	PMoveTo(savedX, savedY + height + accentGap);
2028 ihaka 1584
	x[0] = ConvertedX();
1585
	y[0] = ConvertedY();
3244 ihaka 1586
	PMoveAcross(0.5 * totalwidth);
1587
	PMoveUp(hatHeight);
2028 ihaka 1588
	x[1] = ConvertedX();
1589
	y[1] = ConvertedY();
3244 ihaka 1590
	PMoveAcross(0.5 * totalwidth);
1591
	PMoveUp(-hatHeight);
2028 ihaka 1592
	x[2] = ConvertedX();
1593
	y[2] = ConvertedY();
19875 murrell 1594
	GEPolyline(3, x, y, 
1595
		  TextColor, MathGamma, LTY_SOLID, 1, MathDevice);
3244 ihaka 1596
	PMoveTo(savedX + width, savedY);
2028 ihaka 1597
    }
1598
    return EnlargeBBox(bbox, accentGap + hatHeight, 0, 0);
2 r 1599
}
1600
 
2028 ihaka 1601
static int BarAtom(SEXP expr)
2 r 1602
{
2028 ihaka 1603
    return NameAtom(expr) && NameMatch(expr, "bar");
2 r 1604
}
1605
 
2028 ihaka 1606
static BBOX RenderBar(SEXP expr, int draw)
2 r 1607
{
2028 ihaka 1608
    double savedX = CurrentX;
1609
    double savedY = CurrentY;
1610
    BBOX bbox = RenderElement(CADR(expr), draw);
2105 ihaka 1611
    double accentGap = ACCENT_GAP * XHeight();
2124 maechler 1612
    /*double hatHeight = HAT_HEIGHT * XHeight();*/
2028 ihaka 1613
    double height = bboxHeight(bbox);
1614
    double width = bboxWidth(bbox);
2105 ihaka 1615
    double offset = bboxItalic(bbox);
2028 ihaka 1616
    double x[2], y[2];
2 r 1617
 
2028 ihaka 1618
    if (draw) {
3244 ihaka 1619
	PMoveTo(savedX + offset, savedY + height + accentGap);
2028 ihaka 1620
	x[0] = ConvertedX();
1621
	y[0] = ConvertedY();
3244 ihaka 1622
	PMoveAcross(width);
2028 ihaka 1623
	x[1] = ConvertedX();
1624
	y[1] = ConvertedY();
19875 murrell 1625
	GEPolyline(2, x, y, 
1626
		  TextColor, MathGamma, LTY_SOLID, 1, MathDevice);
3244 ihaka 1627
	PMoveTo(savedX + width, savedY);
2028 ihaka 1628
    }
1629
    return EnlargeBBox(bbox, accentGap, 0, 0);
2 r 1630
}
1631
 
2028 ihaka 1632
static struct {
1633
    char *name;
1634
    int code;
2 r 1635
}
2028 ihaka 1636
AccentTable[] = {
3865 pd 1637
    { "hat",		 94 },
1638
    { "ring",		176 },
1639
    { "tilde",		126 },
19598 ripley 1640
    { "dot",            215 },
3865 pd 1641
    { NULL,		  0 },
2028 ihaka 1642
};
2122 maechler 1643
 
2028 ihaka 1644
static int AccentCode(SEXP expr)
2 r 1645
{
2028 ihaka 1646
    int i;
1647
    for (i = 0; AccentTable[i].code; i++)
1648
	if (NameMatch(expr, AccentTable[i].name))
1649
	    return AccentTable[i].code;
1650
    return 0;
2 r 1651
}
1652
 
2028 ihaka 1653
static int AccentAtom(SEXP expr)
2 r 1654
{
2105 ihaka 1655
    return NameAtom(expr) && (AccentCode(expr) != 0);
2 r 1656
}
1657
 
2028 ihaka 1658
static void InvalidAccent(SEXP expr)
2 r 1659
{
5731 ripley 1660
    errorcall(expr, "invalid accent");
2 r 1661
}
1662
 
2028 ihaka 1663
static BBOX RenderAccent(SEXP expr, int draw)
2 r 1664
{
2028 ihaka 1665
    SEXP body, accent;
1666
    double savedX = CurrentX;
1667
    double savedY = CurrentY;
1668
    BBOX bodyBBox, accentBBox;
2105 ihaka 1669
    double xoffset, yoffset, width, italic;
2028 ihaka 1670
    int code;
1671
    if (length(expr) != 2)
1672
	InvalidAccent(expr);
1673
    accent = CAR(expr);
1674
    body = CADR(expr);
2105 ihaka 1675
    code = AccentCode(accent);
1676
    if (code == 0)
2028 ihaka 1677
	InvalidAccent(expr);
2105 ihaka 1678
    bodyBBox = RenderElement(body, 0);
1679
    italic = bboxItalic(bodyBBox);
19598 ripley 1680
    if (code == 215) /* dotmath */
1681
	accentBBox = RenderSymbolChar(code, 0);
1682
    else
1683
	accentBBox = RenderChar(code, 0);
2105 ihaka 1684
    width = max(bboxWidth(bodyBBox) + bboxItalic(bodyBBox),
1685
		bboxWidth(accentBBox));
1686
    xoffset = 0.5 *(width - bboxWidth(bodyBBox));
1687
    bodyBBox = RenderGap(xoffset, draw);
1688
    bodyBBox = CombineBBoxes(bodyBBox, RenderElement(body, draw));
1689
    bodyBBox = CombineBBoxes(bodyBBox, RenderGap(xoffset, draw));
3244 ihaka 1690
    PMoveTo(savedX, savedY);
2105 ihaka 1691
    xoffset = 0.5 *(width - bboxWidth(accentBBox))
1692
	+ 0.9 * italic;
1693
    yoffset = bboxHeight(bodyBBox) + bboxDepth(accentBBox) + 0.1 * XHeight();
2028 ihaka 1694
    if (draw) {
3244 ihaka 1695
	PMoveTo(savedX + xoffset, savedY + yoffset);
19598 ripley 1696
	if (code == 215) /* dotmath */
1697
	    RenderSymbolChar(code, draw);
1698
	else
1699
	    RenderChar(code, draw);
2028 ihaka 1700
    }
1701
    bodyBBox = CombineOffsetBBoxes(bodyBBox, 0, accentBBox, 0,
1702
				   xoffset, yoffset);
3244 ihaka 1703
    PMoveTo(savedX + width, savedY);
2028 ihaka 1704
    return bodyBBox;
2 r 1705
}
1706
 
1707
 
2028 ihaka 1708
/*----------------------------------------------------------------------
1709
 *
1710
 *  Code for Fraction Expressions  (over, atop)
1711
 *
1712
 *  Rules 15, 15a, ..., 15e of the TeXBook
1713
 *
1714
 */
2 r 1715
 
2028 ihaka 1716
static void NumDenomVShift(BBOX numBBox, BBOX denomBBox,
1717
			     double *u, double *v)
2 r 1718
{
2028 ihaka 1719
    double a, delta, phi, theta;
1720
    a = TeX(sigma22);
1721
    theta = TeX(xi8);
1722
    if(CurrentStyle > STYLE_T) {
1723
	*u = TeX(sigma8);
1724
	*v = TeX(sigma11);
1725
	phi = 3 * theta;
1726
    }
1727
    else {
1728
	*u = TeX(sigma9);
1729
	*v = TeX(sigma12);
1730
	phi = theta;
1731
    }
1732
    delta = (*u - bboxDepth(numBBox)) - (a + 0.5 * theta);
1733
    if (delta < phi)
2105 ihaka 1734
	*u += (phi - delta) + theta;
2028 ihaka 1735
    delta = (a + 0.5 * theta) - (bboxHeight(denomBBox) - *v);
1736
    if (delta < phi)
2105 ihaka 1737
	*v += (phi - delta) + theta;
2 r 1738
}
1739
 
2028 ihaka 1740
static void NumDenomHShift(BBOX numBBox, BBOX denomBBox,
1741
			   double *numShift, double *denomShift)
2 r 1742
{
2028 ihaka 1743
    double numWidth = bboxWidth(numBBox);
1744
    double denomWidth = bboxWidth(denomBBox);
1745
    if (numWidth > denomWidth) {
1746
	*numShift = 0;
1747
	*denomShift = (numWidth - denomWidth) / 2;
1748
    }
1749
    else {
1750
	*numShift = (denomWidth - numWidth) / 2;
1751
	*denomShift = 0;
1752
    }
2 r 1753
}
1754
 
2028 ihaka 1755
static BBOX RenderFraction(SEXP expr, int rule, int draw)
2 r 1756
{
2028 ihaka 1757
    SEXP numerator = CADR(expr);
1758
    SEXP denominator = CADDR(expr);
2124 maechler 1759
    BBOX numBBox, denomBBox;
2028 ihaka 1760
    double nHShift, dHShift;
1761
    double nVShift, dVShift;
1762
    double width, x[2], y[2];
1763
    double savedX = CurrentX;
1764
    double savedY = CurrentY;
1765
    STYLE style;
2 r 1766
 
2028 ihaka 1767
    style = GetStyle();
1768
    SetNumStyle(style);
1769
    numBBox = RenderItalicCorr(RenderElement(numerator, 0), 0);
1770
    SetDenomStyle(style);
1771
    denomBBox = RenderItalicCorr(RenderElement(denominator, 0), 0);
1772
    SetStyle(style);
2 r 1773
 
2028 ihaka 1774
    width = max(bboxWidth(numBBox), bboxWidth(denomBBox));
1775
    NumDenomHShift(numBBox, denomBBox, &nHShift, &dHShift);
1776
    NumDenomVShift(numBBox, denomBBox, &nVShift, &dVShift);
2 r 1777
 
2028 ihaka 1778
    CurrentX = savedX;
1779
    CurrentY = savedY;
1780
    SetNumStyle(style);
1781
    numBBox = RenderOffsetElement(numerator, nHShift, nVShift, draw);
2 r 1782
 
2028 ihaka 1783
    CurrentX = savedX;
1784
    CurrentY = savedY;
1785
    SetDenomStyle(style);
1786
    denomBBox = RenderOffsetElement(denominator, dHShift, -dVShift, draw);
2 r 1787
 
2028 ihaka 1788
    SetStyle(style);
2 r 1789
 
2028 ihaka 1790
    if (draw) {
1791
	if (rule) {
1792
	    CurrentX = savedX;
1793
	    CurrentY = savedY;
3244 ihaka 1794
	    PMoveUp(AxisHeight());
2028 ihaka 1795
	    x[0] = ConvertedX();
1796
	    y[0] = ConvertedY();
3244 ihaka 1797
	    PMoveAcross(width);
2028 ihaka 1798
	    x[1] = ConvertedX();
1799
	    y[1] = ConvertedY();
19875 murrell 1800
	    GEPolyline(2, x, y, 
1801
		  TextColor, MathGamma, LTY_SOLID, 1, MathDevice);
3244 ihaka 1802
	    PMoveUp(-AxisHeight());
2028 ihaka 1803
	}
3244 ihaka 1804
	PMoveTo(savedX + width, savedY);
2028 ihaka 1805
    }
1806
    return CombineAlignedBBoxes(numBBox, denomBBox);
2 r 1807
}
1808
 
2028 ihaka 1809
static int OverAtom(SEXP expr)
2 r 1810
{
2105 ihaka 1811
    return NameAtom(expr) &&
3475 pd 1812
	(NameMatch(expr, "over") || NameMatch(expr, "frac"));
2 r 1813
}
1814
 
2028 ihaka 1815
static BBOX RenderOver(SEXP expr, int draw)
2 r 1816
{
2028 ihaka 1817
    return RenderFraction(expr, 1, draw);
2 r 1818
}
1819
 
2028 ihaka 1820
static int AtopAtom(SEXP expr)
2 r 1821
{
2028 ihaka 1822
    return NameAtom(expr) && NameMatch(expr, "atop");
2 r 1823
}
1824
 
2028 ihaka 1825
static BBOX RenderAtop(SEXP expr, int draw)
2 r 1826
{
2028 ihaka 1827
    return RenderFraction(expr, 0, draw);
2 r 1828
}
2105 ihaka 1829
 
2028 ihaka 1830
/*----------------------------------------------------------------------
1831
 *
1832
 *  Code for Grouped Expressions  (e.g. ( ... ))
1833
 *
1834
 *    group(ldelim, body, rdelim)
1835
 *
1836
 *    bgroup(ldelim, body, rdelim)
1837
 *
1838
 */
2 r 1839
 
2105 ihaka 1840
#define DelimSymbolMag 1.25
1841
 
2028 ihaka 1842
static int DelimCode(SEXP expr, SEXP head)
2 r 1843
{
2028 ihaka 1844
    int code = 0;
1845
    if (NameAtom(head)) {
1846
	if (NameMatch(head, "lfloor"))
1847
	    code = S_BRACKETLEFTBT;
1848
	else if (NameMatch(head, "rfloor"))
1849
	    code = S_BRACKETRIGHTBT;
1850
	if (NameMatch(head, "lceil"))
8061 murrell 1851
	    code = S_BRACKETLEFTTP;
2028 ihaka 1852
	else if (NameMatch(head, "rceil"))
8061 murrell 1853
	    code = S_BRACKETRIGHTTP;
2028 ihaka 1854
    }
1855
    else if (StringAtom(head) && length(head) > 0) {
1856
	if (StringMatch(head, "|"))
1857
	    code = '|';
1858
	else if (StringMatch(head, "||"))
1859
	    code = 2;
1860
	else if (StringMatch(head, "("))
1861
	    code = '(';
1862
	else if (StringMatch(head, ")"))
1863
	    code = ')';
1864
	else if (StringMatch(head, "["))
1865
	    code = '[';
1866
	else if (StringMatch(head, "]"))
1867
	    code = ']';
1868
	else if (StringMatch(head, "{"))
1869
	    code = '{';
1870
	else if (StringMatch(head, "}"))
1871
	    code = '}';
1872
	else if (StringMatch(head, "") || StringMatch(head, "."))
1873
	    code = '.';
1874
    }
1875
    if (code == 0)
5731 ripley 1876
	errorcall(expr, "invalid group delimiter");
2028 ihaka 1877
    return code;
1016 maechler 1878
}
2 r 1879
 
2105 ihaka 1880
static BBOX RenderDelimiter(int delim, int draw)
1881
{
1882
    BBOX bbox;
19875 murrell 1883
    double savecex = MathCex;
1884
    MathCex = DelimSymbolMag * MathCex;
2105 ihaka 1885
    bbox = RenderSymbolChar(delim, draw);
19875 murrell 1886
    MathCex = savecex;
2105 ihaka 1887
    return bbox;
1888
}
1889
 
2028 ihaka 1890
static int GroupAtom(SEXP expr)
2 r 1891
{
2028 ihaka 1892
    return NameAtom(expr) && NameMatch(expr, "group");
2 r 1893
}
1894
 
2028 ihaka 1895
static BBOX RenderGroup(SEXP expr, int draw)
2 r 1896
{
19875 murrell 1897
    double cexSaved = MathCex;
2028 ihaka 1898
    BBOX bbox;
1899
    int code;
1900
    if (length(expr) != 4)
5731 ripley 1901
	errorcall(expr, "invalid group specification");
2028 ihaka 1902
    bbox = NullBBox();
1903
    code = DelimCode(expr, CADR(expr));
19875 murrell 1904
    MathCex = DelimSymbolMag * MathCex;
2028 ihaka 1905
    if (code == 2) {
1906
	bbox = RenderSymbolChar('|', draw);
1907
	bbox = RenderSymbolChar('|', draw);
1908
    }
1909
    else if (code != '.')
1910
	bbox = RenderSymbolChar(code, draw);
19875 murrell 1911
    MathCex = cexSaved;
2028 ihaka 1912
    bbox = CombineBBoxes(bbox, RenderElement(CADDR(expr), draw));
1913
    bbox = RenderItalicCorr(bbox, draw);
1914
    code = DelimCode(expr, CADDDR(expr));
19875 murrell 1915
    MathCex = DelimSymbolMag * MathCex;
2028 ihaka 1916
    if (code == 2) {
8061 murrell 1917
	bbox = CombineBBoxes(bbox, RenderSymbolChar('|', draw));
1918
	bbox = CombineBBoxes(bbox, RenderSymbolChar('|', draw));
2028 ihaka 1919
    }
1920
    else if (code != '.')
8061 murrell 1921
	bbox = CombineBBoxes(bbox, RenderSymbolChar(code, draw));
19875 murrell 1922
    MathCex = cexSaved;
2028 ihaka 1923
    return bbox;
2 r 1924
}
1925
 
2028 ihaka 1926
static int BGroupAtom(SEXP expr)
2 r 1927
{
2028 ihaka 1928
    return NameAtom(expr) && NameMatch(expr, "bgroup");
2 r 1929
}
1930
 
2028 ihaka 1931
static BBOX RenderDelim(int which, double dist, int draw)
2 r 1932
{
2028 ihaka 1933
    double savedX = CurrentX;
1934
    double savedY = CurrentY;
1935
    FontType prev = SetFont(SymbolFont);
1936
    BBOX ansBBox, topBBox, botBBox, extBBox, midBBox;
1937
    int top, bot, ext, mid;
2105 ihaka 1938
    int i, n;
1939
    double topShift, botShift, extShift, midShift;
1940
    double ytop, ybot, extHeight, delta;
2028 ihaka 1941
    double axisHeight = TeX(sigma22);
2 r 1942
 
2028 ihaka 1943
    switch(which) {
26060 tlumley 1944
    case '.': 
1945
	SetFont(prev);
2028 ihaka 1946
	return NullBBox();
1947
	break;
1948
    case '|':
1949
    case 2:
1950
	top = 239; ext = 239; bot = 239; mid = 0;
1951
	break;
1952
    case '(':
1953
	top = 230; ext = 231; bot = 232; mid = 0;
1954
	break;
1955
    case ')':
1956
	top = 246; ext = 247; bot = 248; mid = 0;
1957
	break;
1958
    case '[':
1959
	top = 233; ext = 234; bot = 235; mid = 0;
1960
	break;
1961
    case ']':
1962
	top = 249; ext = 250; bot = 251; mid = 0;
1963
	break;
1964
    case '{':
1965
	top = 236; ext = 239; bot = 238; mid = 237;
2122 maechler 1966
	break;
2028 ihaka 1967
    case '}':
1968
	top = 252; ext = 239; bot = 254; mid = 253;
1969
	break;
1970
    default:
5731 ripley 1971
	error("group is incomplete");
3475 pd 1972
	return ansBBox;/*never reached*/
2028 ihaka 1973
    }
1974
    topBBox = GlyphBBox(top);
1975
    extBBox = GlyphBBox(ext);
1976
    botBBox = GlyphBBox(bot);
2105 ihaka 1977
    if (which == '{' || which == '}') {
1978
	if (1.2 * (bboxHeight(topBBox) + bboxDepth(topBBox)) > dist)
1979
	    dist = 1.2 * (bboxHeight(topBBox) + bboxDepth(botBBox));
1980
    }
1981
    else {
1982
	if (0.8 * (bboxHeight(topBBox) + bboxDepth(topBBox)) > dist)
1983
	    dist = 0.8 * (bboxHeight(topBBox) + bboxDepth(topBBox));
1984
    }
1985
    extHeight = bboxHeight(extBBox) + bboxDepth(extBBox);
2028 ihaka 1986
    topShift = dist - bboxHeight(topBBox) + axisHeight;
1987
    botShift = dist - bboxDepth(botBBox) - axisHeight;
2105 ihaka 1988
    extShift = 0.5 * (bboxHeight(extBBox) - bboxDepth(extBBox));
2028 ihaka 1989
    topBBox = ShiftBBox(topBBox, topShift);
1990
    botBBox = ShiftBBox(botBBox, -botShift);
1991
    ansBBox = CombineAlignedBBoxes(topBBox, botBBox);
2105 ihaka 1992
    if (which == '{' || which == '}') {
1993
	midBBox = GlyphBBox(mid);
1994
	midShift = axisHeight
1995
	    - 0.5 * (bboxHeight(midBBox) - bboxDepth(midBBox));
1996
	midBBox = ShiftBBox(midBBox, midShift);
1997
	ansBBox = CombineAlignedBBoxes(ansBBox, midBBox);
1998
	if (draw) {
3244 ihaka 1999
	    PMoveTo(savedX, savedY + topShift);
2105 ihaka 2000
	    RenderSymbolChar(top, draw);
3244 ihaka 2001
	    PMoveTo(savedX, savedY + midShift);
2122 maechler 2002
	    RenderSymbolChar(mid, draw);
3244 ihaka 2003
	    PMoveTo(savedX, savedY - botShift);
2105 ihaka 2004
	    RenderSymbolChar(bot, draw);
3244 ihaka 2005
	    PMoveTo(savedX + bboxWidth(ansBBox), savedY);
2105 ihaka 2006
	}
2028 ihaka 2007
    }
2105 ihaka 2008
    else {
2009
	if (draw) {
2010
	    /* draw the top and bottom elements */
3244 ihaka 2011
	    PMoveTo(savedX, savedY + topShift);
2105 ihaka 2012
	    RenderSymbolChar(top, draw);
3244 ihaka 2013
	    PMoveTo(savedX, savedY - botShift);
2105 ihaka 2014
	    RenderSymbolChar(bot, draw);
2015
	    /* now join with extenders */
2016
	    ytop = axisHeight + dist
2017
		- (bboxHeight(topBBox) + bboxDepth(topBBox));
2018
	    ybot = axisHeight - dist
2019
		+ (bboxHeight(botBBox) + bboxDepth(botBBox));
2020
	    n = ceil((ytop - ybot) / (0.99 * extHeight));
2021
	    if (n > 0) {
2022
		delta = (ytop - ybot) / n;
2023
		for (i = 0; i < n; i++) {
3244 ihaka 2024
		    PMoveTo(savedX, savedY + ybot + (i + 0.5) * delta - extShift);
2105 ihaka 2025
		    RenderSymbolChar(ext, draw);
2026
		}
2027
	    }
3244 ihaka 2028
	    PMoveTo(savedX + bboxWidth(ansBBox), savedY);
2122 maechler 2029
 
2105 ihaka 2030
	}
2031
    }
2028 ihaka 2032
    SetFont(prev);
2033
    return ansBBox;
2 r 2034
}
2035
 
2028 ihaka 2036
static BBOX RenderBGroup(SEXP expr, int draw)
2 r 2037
{
2028 ihaka 2038
    double dist;
2124 maechler 2039
    BBOX bbox;
2028 ihaka 2040
    double axisHeight = TeX(sigma22);
2041
    double extra = 0.2 * xHeight();
2042
    int delim1, delim2;
2043
    if (length(expr) != 4)
5731 ripley 2044
	errorcall(expr, "invalid group specification");
2028 ihaka 2045
    bbox = NullBBox();
2046
    delim1 = DelimCode(expr, CADR(expr));
2047
    delim2 = DelimCode(expr, CADDDR(expr));
2048
    bbox = RenderElement(CADDR(expr), 0);
2049
    dist = max(bboxHeight(bbox) - axisHeight, bboxDepth(bbox) + axisHeight);
2050
    bbox = RenderDelim(delim1, dist + extra, draw);
3475 pd 2051
    bbox = CombineBBoxes(bbox,	RenderElement(CADDR(expr), draw));
2028 ihaka 2052
    bbox = RenderItalicCorr(bbox, draw);
3475 pd 2053
    bbox = CombineBBoxes(bbox,	RenderDelim(delim2, dist + extra, draw));
2028 ihaka 2054
    return bbox;
2 r 2055
}
2056
 
2028 ihaka 2057
/*----------------------------------------------------------------------
2058
 *
2059
 *  Code for Parenthetic Expressions  (i.e. ( ... ))
2060
 *
2061
 */
2 r 2062
 
2028 ihaka 2063
static int ParenAtom(SEXP expr)
2 r 2064
{
2028 ihaka 2065
    return NameAtom(expr) && NameMatch(expr, "(");
2 r 2066
}
2067
 
2028 ihaka 2068
static BBOX RenderParen(SEXP expr, int draw)
2 r 2069
{
2028 ihaka 2070
    BBOX bbox;
2105 ihaka 2071
    bbox = RenderDelimiter(S_PARENLEFT, draw);
2028 ihaka 2072
    bbox = CombineBBoxes(bbox, RenderElement(CADR(expr), draw));
2073
    bbox = RenderItalicCorr(bbox, draw);
2105 ihaka 2074
    return CombineBBoxes(bbox, RenderDelimiter(S_PARENRIGHT, draw));
2 r 2075
}
2076
 
2028 ihaka 2077
/*----------------------------------------------------------------------
2078
 *
2079
 *  Code for Integral Operators.
2080
 *
2081
 */
2 r 2082
 
2028 ihaka 2083
static int IntAtom(SEXP expr)
2 r 2084
{
2028 ihaka 2085
    return NameAtom(expr) && NameMatch(expr, "integral");
2 r 2086
}
2087
 
1926 ihaka 2088
 
2028 ihaka 2089
static BBOX RenderIntSymbol(int draw)
1926 ihaka 2090
{
2028 ihaka 2091
    double savedX = CurrentX;
2092
    double savedY = CurrentY;
2105 ihaka 2093
    if (GetStyle() > STYLE_T) {
2094
	BBOX bbox1 = RenderSymbolChar(243, 0);
2095
	BBOX bbox2 = RenderSymbolChar(245, 0);
2096
	double shift;
2097
	shift = TeX(sigma22) + 0.99 * bboxDepth(bbox1);
3244 ihaka 2098
	PMoveUp(shift);
2105 ihaka 2099
	bbox1 = ShiftBBox(RenderSymbolChar(243, draw), shift);
2100
	CurrentX = savedX;
2101
	CurrentY = savedY;
2102
	shift = TeX(sigma22) - 0.99 * bboxHeight(bbox2);
3244 ihaka 2103
	PMoveUp(shift);
2105 ihaka 2104
	bbox2 = ShiftBBox(RenderSymbolChar(245, draw), shift);
2105
	if (draw)
3244 ihaka 2106
	    PMoveTo(savedX + max(bboxWidth(bbox1), bboxWidth(bbox2)), savedY);
2105 ihaka 2107
	else
3244 ihaka 2108
	    PMoveTo(savedX, savedY);
2105 ihaka 2109
	return CombineAlignedBBoxes(bbox1, bbox2);
2110
    }
2111
    else {
2112
	return RenderSymbolChar(0362, draw);
2113
    }
1926 ihaka 2114
}
2115
 
2028 ihaka 2116
static BBOX RenderInt(SEXP expr, int draw)
2 r 2117
{
2028 ihaka 2118
    BBOX opBBox, lowerBBox, upperBBox, bodyBBox;
2119
    int nexpr = length(expr);
2120
    STYLE style = GetStyle();
2121
    double savedX = CurrentX;
2122
    double savedY = CurrentY;
2123
    double hshift, vshift, width;
2 r 2124
 
2028 ihaka 2125
    opBBox = RenderIntSymbol(draw);
2126
    width = bboxWidth(opBBox);
2127
    CurrentX = savedX;
2128
    CurrentY = savedY;
2129
    if (nexpr > 2) {
2130
	hshift = 0.5 * width + ThinSpace();
2131
	SetSubStyle(style);
2132
	lowerBBox = RenderElement(CADDR(expr), 0);
2133
	vshift = bboxDepth(opBBox) + CenterShift(lowerBBox);
2134
	lowerBBox = RenderOffsetElement(CADDR(expr), hshift, -vshift, draw);
2135
	opBBox = CombineAlignedBBoxes(opBBox, lowerBBox);
2136
	SetStyle(style);
2137
	CurrentX = savedX;
2138
	CurrentY = savedY;
1839 ihaka 2139
    }
2028 ihaka 2140
    if (nexpr > 3) {
2141
	hshift = width + ThinSpace();
2142
	SetSupStyle(style);
2143
	upperBBox = RenderElement(CADDDR(expr), 0);
2144
	vshift = bboxHeight(opBBox) - CenterShift(upperBBox);
2145
	upperBBox = RenderOffsetElement(CADDDR(expr), hshift, vshift, draw);
2146
	opBBox = CombineAlignedBBoxes(opBBox, upperBBox);
2147
	SetStyle(style);
2148
	CurrentX = savedX;
2149
	CurrentY = savedY;
1839 ihaka 2150
    }
3244 ihaka 2151
    PMoveAcross(bboxWidth(opBBox));
2028 ihaka 2152
    if (nexpr > 1) {
2153
	bodyBBox = RenderElement(CADR(expr), draw);
2154
	opBBox = CombineBBoxes(opBBox, bodyBBox);
1839 ihaka 2155
    }
2028 ihaka 2156
    return opBBox;
2 r 2157
}
2158
 
2159
 
2028 ihaka 2160
/*----------------------------------------------------------------------
2161
 *
2162
 *  Code for Operator Expressions (sum, product, lim, inf, sup, ...)
2163
 *
2164
 */
2 r 2165
 
2028 ihaka 2166
#define OperatorSymbolMag  1.25
2 r 2167
 
2028 ihaka 2168
static SymTab OpTable[] = {
3865 pd 2169
    { "prod",		S_PRODUCT },
2170
    { "sum",		S_SUM },
2171
    { "union",		S_UNION },
2172
    { "intersect",	S_INTERSECTION },
2173
    { "lim",		N_LIM },
2174
    { "liminf",		N_LIMINF },
2175
    { "limsup",		N_LIMINF },
2176
    { "inf",		N_INF },
2177
    { "sup",		N_SUP },
2178
    { "min",		N_MIN },
2179
    { "max",		N_MAX },
2180
    { NULL,		0 }
2028 ihaka 2181
};
2 r 2182
 
2028 ihaka 2183
static int OpAtom(SEXP expr)
2 r 2184
{
2028 ihaka 2185
    int i;
2186
    for (i = 0; OpTable[i].code; i++)
2187
	if (NameMatch(expr, OpTable[i].name))
2188
	    return OpTable[i].code;
2189
    return 0;
2 r 2190
}
2191
 
2028 ihaka 2192
static BBOX RenderOpSymbol(SEXP op, int draw)
2 r 2193
{
2028 ihaka 2194
    BBOX bbox;
19875 murrell 2195
    double cexSaved = MathCex;
2124 maechler 2196
    /*double savedX = CurrentX;*/
2197
    /*double savedY = CurrentY;*/
2028 ihaka 2198
    double shift;
2105 ihaka 2199
    int display = (GetStyle() > STYLE_T);
2028 ihaka 2200
    int opId = OpAtom(op);
2 r 2201
 
8061 murrell 2202
    if (opId == S_SUM || opId == S_PRODUCT ||
2203
	opId == S_UNION || opId == S_INTERSECTION) {
2105 ihaka 2204
	if (display) {
19875 murrell 2205
	    MathCex = OperatorSymbolMag * MathCex;
2105 ihaka 2206
	    bbox = RenderSymbolChar(OpAtom(op), 0);
2207
	    shift = 0.5 * (bboxHeight(bbox) - bboxDepth(bbox)) - TeX(sigma22);
2208
	    if (draw) {
3244 ihaka 2209
		PMoveUp(-shift);
2105 ihaka 2210
		bbox = RenderSymbolChar(opId, 1);
3244 ihaka 2211
		PMoveUp(shift);
2105 ihaka 2212
	    }
19875 murrell 2213
	    MathCex = cexSaved;
2105 ihaka 2214
	    return ShiftBBox(bbox, -shift);
2028 ihaka 2215
	}
2105 ihaka 2216
	else return RenderSymbolChar(opId, draw);
2028 ihaka 2217
    }
1839 ihaka 2218
    else {
2028 ihaka 2219
	FontType prevfont = SetFont(PlainFont);
2220
	bbox = RenderStr(CHAR(PRINTNAME(op)), draw);
2221
	SetFont(prevfont);
2222
	return bbox;
1839 ihaka 2223
    }
2 r 2224
}
2225
 
2028 ihaka 2226
static BBOX RenderOp(SEXP expr, int draw)
2 r 2227
{
2028 ihaka 2228
    BBOX lowerBBox, upperBBox, bodyBBox;
2229
    double savedX = CurrentX;
2230
    double savedY = CurrentY;
2231
    int nexpr = length(expr);
2232
    STYLE style = GetStyle();
2233
    BBOX opBBox = RenderOpSymbol(CAR(expr), 0);
2234
    double width = bboxWidth(opBBox);
2235
    double hshift, lvshift, uvshift;
3865 pd 2236
    lvshift = uvshift = 0;	/* -Wall */
2028 ihaka 2237
    if (nexpr > 2) {
2238
	SetSubStyle(style);
2239
	lowerBBox = RenderElement(CADDR(expr), 0);
2240
	SetStyle(style);
2241
	width = max(width, bboxWidth(lowerBBox));
2242
	lvshift = max(TeX(xi10), TeX(xi12) - bboxHeight(lowerBBox));
2243
	lvshift = bboxDepth(opBBox) + bboxHeight(lowerBBox) + lvshift;
2244
    }
2245
    if (nexpr > 3) {
2246
	SetSupStyle(style);
2247
	upperBBox = RenderElement(CADDDR(expr), 0);
2248
	SetStyle(style);
2249
	width = max(width, bboxWidth(upperBBox));
2250
	uvshift = max(TeX(xi9), TeX(xi11) - bboxDepth(upperBBox));
2251
	uvshift = bboxHeight(opBBox) + bboxDepth(upperBBox) + uvshift;
2252
    }
2253
    hshift = 0.5 * (width - bboxWidth(opBBox));
2254
    opBBox = RenderGap(hshift, draw);
2255
    opBBox = CombineBBoxes(opBBox, RenderOpSymbol(CAR(expr), draw));
2256
    CurrentX = savedX;
2257
    CurrentY = savedY;
2258
    if (nexpr > 2) {
2259
	SetSubStyle(style);
2260
	hshift = 0.5 * (width - bboxWidth(lowerBBox));
2261
	lowerBBox = RenderOffsetElement(CADDR(expr), hshift, -lvshift, draw);
2262
	SetStyle(style);
2263
	opBBox = CombineAlignedBBoxes(opBBox, lowerBBox);
2264
	CurrentX = savedX;
2265
	CurrentY = savedY;
2266
    }
2267
    if (nexpr > 3) {
2268
	SetSupStyle(style);
2269
	hshift = 0.5 * (width - bboxWidth(upperBBox));
2270
	upperBBox = RenderOffsetElement(CADDDR(expr), hshift, uvshift, draw);
2271
	SetStyle(style);
2272
	opBBox = CombineAlignedBBoxes(opBBox, upperBBox);
2273
	CurrentX = savedX;
2274
	CurrentY = savedY;
2275
    }
2276
    opBBox = EnlargeBBox(opBBox, TeX(xi13), TeX(xi13), 0);
2277
    if (draw)
3244 ihaka 2278
	PMoveAcross(width);
2028 ihaka 2279
    opBBox = CombineBBoxes(opBBox, RenderGap(ThinSpace(), draw));
2280
    bodyBBox = RenderElement(CADR(expr), draw);
2281
    return CombineBBoxes(opBBox, bodyBBox);
2 r 2282
}
2283
 
2284
 
2028 ihaka 2285
/*----------------------------------------------------------------------
2286
 *
2287
 *  Code for radical expressions (root, sqrt)
2288
 *
2289
 *  Tunable parameteters :
2290
 *
3475 pd 2291
 *  RADICAL_GAP	   The gap between the nucleus and the radical extension.
2028 ihaka 2292
 *  RADICAL_SPACE  Extra space to the left and right of the nucleus.
2293
 *
2294
 */
2 r 2295
 
2028 ihaka 2296
#define RADICAL_GAP    0.4
2297
#define RADICAL_SPACE  0.2
2 r 2298
 
2028 ihaka 2299
static int RadicalAtom(SEXP expr)
2 r 2300
{
2028 ihaka 2301
    return NameAtom(expr) &&
2302
	(NameMatch(expr, "root") ||
2303
	 NameMatch(expr, "sqrt"));
2 r 2304
}
2305
 
2028 ihaka 2306
static BBOX RenderScript(SEXP expr, int draw)
2 r 2307
{
2028 ihaka 2308
    BBOX bbox;
2309
    STYLE style = GetStyle();
2310
    SetSupStyle(style);
2311
    bbox = RenderElement(expr, draw);
2312
    SetStyle(style);
2313
    return bbox;
2 r 2314
}
2315
 
2028 ihaka 2316
static BBOX RenderRadical(SEXP expr, int draw)
2 r 2317
{
1839 ihaka 2318
    SEXP body = CADR(expr);
2028 ihaka 2319
    SEXP order = CADDR(expr);
2124 maechler 2320
    BBOX bodyBBox, orderBBox;
2028 ihaka 2321
    double radWidth, radHeight, radDepth;
2322
    double leadWidth, leadHeight, twiddleHeight;
2323
    double hshift, vshift;
2324
    double radGap, radSpace, radTrail;
2325
    STYLE style = GetStyle();
2326
    double savedX = CurrentX;
2327
    double savedY = CurrentY;
2328
    double x[5], y[5];
2 r 2329
 
2028 ihaka 2330
    radGap = RADICAL_GAP * xHeight();
2331
    radSpace = RADICAL_SPACE * xHeight();
2332
    radTrail = MuSpace();
2333
    SetPrimeStyle(style);
2334
    bodyBBox = RenderElement(body, 0);
2335
    bodyBBox = RenderItalicCorr(bodyBBox, 0);
2 r 2336
 
2028 ihaka 2337
    radWidth = 0.6 *XHeight();
2338
    radHeight = bboxHeight(bodyBBox) + radGap;
2339
    radDepth = bboxDepth(bodyBBox);
2340
    twiddleHeight = CenterShift(bodyBBox);
2 r 2341
 
2028 ihaka 2342
    leadWidth = radWidth;
2343
    leadHeight = radHeight;
2344
    if (order != R_NilValue) {
2345
	SetSupStyle(style);
2346
	orderBBox = RenderScript(order, 0);
2347
	leadWidth = max(leadWidth, bboxWidth(orderBBox) + 0.4 * radWidth);
2348
	hshift = leadWidth - bboxWidth(orderBBox) - 0.4 * radWidth;
2349
	vshift = leadHeight - bboxHeight(orderBBox);
2350
	if (vshift - bboxDepth(orderBBox) < twiddleHeight + radGap)
2351
	    vshift = twiddleHeight + bboxDepth(orderBBox) + radGap;
2352
	if (draw) {
3244 ihaka 2353
	    PMoveTo(savedX + hshift, savedY + vshift);
2028 ihaka 2354
	    orderBBox = RenderScript(order, draw);
2355
	}
2356
	orderBBox = EnlargeBBox(orderBBox, vshift, 0, hshift);
1839 ihaka 2357
    }
2028 ihaka 2358
    else
2359
	orderBBox = NullBBox();
2360
    if (draw) {
3244 ihaka 2361
	PMoveTo(savedX + leadWidth - radWidth, savedY);
2362
	PMoveUp(0.8 * twiddleHeight);
2028 ihaka 2363
	x[0] = ConvertedX();
2364
	y[0] = ConvertedY();
3244 ihaka 2365
	PMoveUp(0.2 * twiddleHeight);
2366
	PMoveAcross(0.3 * radWidth);
2028 ihaka 2367
	x[1] = ConvertedX();
2368
	y[1] = ConvertedY();
3244 ihaka 2369
	PMoveUp(-(twiddleHeight + bboxDepth(bodyBBox)));
2370
	PMoveAcross(0.3 * radWidth);
2028 ihaka 2371
	x[2] = ConvertedX();
2372
	y[2] = ConvertedY();
3244 ihaka 2373
	PMoveUp(bboxDepth(bodyBBox) + bboxHeight(bodyBBox) + radGap);
2374
	PMoveAcross(0.4 * radWidth);
2028 ihaka 2375
	x[3] = ConvertedX();
2376
	y[3] = ConvertedY();
3244 ihaka 2377
	PMoveAcross(radSpace + bboxWidth(bodyBBox) + radTrail);
2028 ihaka 2378
	x[4] = ConvertedX();
2379
	y[4] = ConvertedY();
19875 murrell 2380
	GEPolyline(5, x, y, 
2381
		  TextColor, MathGamma, LTY_SOLID, 1, MathDevice);
3244 ihaka 2382
	PMoveTo(savedX, savedY);
2028 ihaka 2383
    }
2384
    orderBBox = CombineAlignedBBoxes(orderBBox,
2385
				     RenderGap(leadWidth + radSpace, draw));
2386
    SetPrimeStyle(style);
2387
    orderBBox = CombineBBoxes(orderBBox, RenderElement(body, draw));
2388
    orderBBox = CombineBBoxes(orderBBox, RenderGap(2 * radTrail, draw));
16224 maechler 2389
    orderBBox = EnlargeBBox(orderBBox, radGap, 0, 0);/* << fixes PR#1101 */
2028 ihaka 2390
    SetStyle(style);
2391
    return orderBBox;
2 r 2392
}
2393
 
2028 ihaka 2394
/*----------------------------------------------------------------------
2395
 *
2396
 *  Code for Absolute Value Expressions (abs)
2397
 *
2398
 */
2 r 2399
 
2028 ihaka 2400
static int AbsAtom(SEXP expr)
2 r 2401
{
2028 ihaka 2402
    return NameAtom(expr) && NameMatch(expr, "abs");
2 r 2403
}
2404
 
2028 ihaka 2405
static BBOX RenderAbs(SEXP expr, int draw)
2 r 2406
{
2028 ihaka 2407
    BBOX bbox = RenderElement(CADR(expr), 0);
2408
    double height = bboxHeight(bbox);
2409
    double depth = bboxDepth(bbox);
1839 ihaka 2410
    double x[2], y[2];
2 r 2411
 
2028 ihaka 2412
    bbox= RenderGap(MuSpace(), draw);
2413
    if (draw) {
3244 ihaka 2414
	PMoveUp(-depth);
2028 ihaka 2415
	x[0] = ConvertedX();
2416
	y[0] = ConvertedY();
3244 ihaka 2417
	PMoveUp(depth + height);
2028 ihaka 2418
	x[1] = ConvertedX();
2419
	y[1] = ConvertedY();
19875 murrell 2420
	GEPolyline(2, x, y, 
2421
		  TextColor, MathGamma, LTY_SOLID, 1, MathDevice);
3244 ihaka 2422
	PMoveUp(-height);
2028 ihaka 2423
    }
2424
    bbox = CombineBBoxes(bbox, RenderGap(MuSpace(), draw));
2425
    bbox = CombineBBoxes(bbox, RenderElement(CADR(expr), draw));
2426
    bbox = RenderItalicCorr(bbox, draw);
2427
    bbox = CombineBBoxes(bbox, RenderGap(MuSpace(), draw));
2428
    if (draw) {
3244 ihaka 2429
	PMoveUp(-depth);
2028 ihaka 2430
	x[0] = ConvertedX();
2431
	y[0] = ConvertedY();
3244 ihaka 2432
	PMoveUp(depth + height);
2028 ihaka 2433
	x[1] = ConvertedX();
2434
	y[1] = ConvertedY();
19875 murrell 2435
	GEPolyline(2, x, y, 
2436
		  TextColor, MathGamma, LTY_SOLID, 1, MathDevice);
3244 ihaka 2437
	PMoveUp(-height);
2028 ihaka 2438
    }
2439
    bbox = CombineBBoxes(bbox, RenderGap(MuSpace(), draw));
2440
    return bbox;
2 r 2441
}
2442
 
2028 ihaka 2443
/*----------------------------------------------------------------------
2444
 *
2445
 *  Code for Grouped Expressions (i.e. { ... } )
2446
 *
2447
 */
2 r 2448
 
2028 ihaka 2449
static int CurlyAtom(SEXP expr)
2 r 2450
{
2028 ihaka 2451
    return NameAtom(expr) &&
2452
	NameMatch(expr, "{");
2 r 2453
}
2454
 
2028 ihaka 2455
static BBOX RenderCurly(SEXP expr, int draw)
2 r 2456
{
2028 ihaka 2457
    return RenderElement(CADR(expr), draw);
2 r 2458
}
2459
 
2460
 
2028 ihaka 2461
/*----------------------------------------------------------------------
2462
 *
2463
 *  Code for Relation Expressions (i.e. ... ==, !=, ...)
2464
 *
2465
 */
2 r 2466
 
3475 pd 2467
				/* Binary Relationships */
7824 ripley 2468
static
2028 ihaka 2469
SymTab RelTable[] = {
3865 pd 2470
    { "<",		 60 },	/* less */
2471
    { "==",		 61 },	/* equal */
2472
    { ">",		 62 },	/* greater */
2473
    { "%=~%",		 64 },	/* congruent */
2474
    { "!=",		185 },	/* not equal */
2475
    { "<=",		163 },	/* less or equal */
2476
    { ">=",		179 },	/* greater or equal */
2477
    { "%==%",		186 },	/* equivalence */
2478
    { "%~~%",		187 },	/* approxequal */
8061 murrell 2479
    { "%prop%",         181 },  /* proportional to */
2 r 2480
 
3865 pd 2481
    { "%<->%",		171 },	/* Arrows */
2482
    { "%<-%",		172 },
2483
    { "%up%",		173 },
2484
    { "%->%",		174 },
2485
    { "%down%",		175 },
2486
    { "%<=>%",		219 },
2487
    { "%<=%",		220 },
2488
    { "%dblup%",	221 },
2489
    { "%=>%",		222 },
2490
    { "%dbldown%",	223 },
2 r 2491
 
3865 pd 2492
    { "%supset%",	201 },	/* Sets (TeX Names) */
2493
    { "%supseteq%",	202 },
2494
    { "%notsubset%",	203 },
2495
    { "%subset%",	204 },
2496
    { "%subseteq%",	205 },
2497
    { "%in%",		206 },
2498
    { "%notin%",	207 },
2 r 2499
 
3865 pd 2500
    { NULL,		  0 },
2028 ihaka 2501
};
1016 maechler 2502
 
2028 ihaka 2503
static int RelAtom(SEXP expr)
2 r 2504
{
2028 ihaka 2505
    int i;
2506
    for (i = 0; RelTable[i].code; i++)
2507
	if (NameMatch(expr, RelTable[i].name))
2508
	    return RelTable[i].code;
2509
    return 0;
2 r 2510
}
2511
 
2028 ihaka 2512
static BBOX RenderRel(SEXP expr, int draw)
2 r 2513
{
2028 ihaka 2514
    int op = RelAtom(CAR(expr));
2515
    int nexpr = length(expr);
2516
    BBOX bbox;
2517
    double gap;
2 r 2518
 
2028 ihaka 2519
    if(nexpr == 3) {
2122 maechler 2520
	gap = (CurrentStyle > STYLE_S) ? ThickSpace() : 0;
2028 ihaka 2521
	bbox = RenderElement(CADR(expr), draw);
2522
	bbox = RenderItalicCorr(bbox, draw);
2523
	bbox = CombineBBoxes(bbox, RenderGap(gap, draw));
2524
	bbox = CombineBBoxes(bbox, RenderSymbolChar(op, draw));
2525
	bbox = CombineBBoxes(bbox, RenderGap(gap, draw));
2526
	return CombineBBoxes(bbox, RenderElement(CADDR(expr), draw));
2 r 2527
    }
5731 ripley 2528
    else error("invalid mathematical annotation");
3865 pd 2529
 
2530
    return NullBBox();		/* -Wall */
2 r 2531
}
2532
 
1016 maechler 2533
 
2028 ihaka 2534
/*----------------------------------------------------------------------
2535
 *
2536
 *  Code for Boldface Expressions
2537
 *
2538
 */
2 r 2539
 
2028 ihaka 2540
static int BoldAtom(SEXP expr)
2 r 2541
{
2028 ihaka 2542
    return NameAtom(expr) &&
2543
	NameMatch(expr, "bold");
2 r 2544
}
2545
 
2028 ihaka 2546
static BBOX RenderBold(SEXP expr, int draw)
2 r 2547
{
2028 ihaka 2548
    BBOX bbox;
2549
    FontType prevfont = SetFont(BoldFont);
2550
    bbox = RenderElement(CADR(expr), draw);
2551
    SetFont(prevfont);
2552
    return bbox;
2 r 2553
}
2554
 
2028 ihaka 2555
/*----------------------------------------------------------------------
2556
 *
2557
 *  Code for Italic Expressions
2558
 *
2559
 */
2 r 2560
 
2028 ihaka 2561
static int ItalicAtom(SEXP expr)
2 r 2562
{
2028 ihaka 2563
    return NameAtom(expr) &&
2564
	(NameMatch(expr, "italic") || NameMatch(expr, "math"));
2 r 2565
}
2566
 
2028 ihaka 2567
static BBOX RenderItalic(SEXP expr, int draw)
2 r 2568
{
2028 ihaka 2569
    BBOX bbox;
2570
    FontType prevfont = SetFont(ItalicFont);
2571
    bbox = RenderElement(CADR(expr), draw);
2572
    SetFont(prevfont);
2573
    return bbox;
2 r 2574
}
2575
 
2028 ihaka 2576
/*----------------------------------------------------------------------
2577
 *
2578
 *  Code for Plain (i.e. Roman) Expressions
2579
 *
2580
 */
2 r 2581
 
2028 ihaka 2582
static int PlainAtom(SEXP expr)
2 r 2583
{
2028 ihaka 2584
    return NameAtom(expr) &&
2585
	NameMatch(expr, "plain");
2 r 2586
}
2587
 
2028 ihaka 2588
static BBOX RenderPlain(SEXP expr, int draw)
2 r 2589
{
2028 ihaka 2590
    BBOX bbox;
2591
    int prevfont = SetFont(PlainFont);
2592
    bbox = RenderElement(CADR(expr), draw);
2593
    SetFont(prevfont);
2594
    return bbox;
2 r 2595
}
2596
 
2028 ihaka 2597
/*----------------------------------------------------------------------
2598
 *
2599
 *  Code for Bold Italic Expressions
2600
 *
2601
 */
2 r 2602
 
2028 ihaka 2603
static int BoldItalicAtom(SEXP expr)
2 r 2604
{
2028 ihaka 2605
    return NameAtom(expr) &&
2606
	(NameMatch(expr, "bolditalic") || NameMatch(expr, "boldmath"));
2 r 2607
}
2608
 
2028 ihaka 2609
static BBOX RenderBoldItalic(SEXP expr, int draw)
2 r 2610
{
2028 ihaka 2611
    BBOX bbox;
2612
    int prevfont = SetFont(BoldItalicFont);
2613
    bbox = RenderElement(CADR(expr), draw);
2614
    SetFont(prevfont);
2615
    return bbox;
2 r 2616
}
2617
 
2028 ihaka 2618
/*----------------------------------------------------------------------
2619
 *
2620
 *  Code for Styles
2621
 *
2622
 */
2 r 2623
 
2028 ihaka 2624
static int StyleAtom(SEXP expr)
2 r 2625
{
2028 ihaka 2626
    return (NameAtom(expr) &&
2627
	    (NameMatch(expr, "displaystyle") ||
2628
	     NameMatch(expr, "textstyle")    ||
2629
	     NameMatch(expr, "scriptstyle")   ||
2630
	     NameMatch(expr, "scriptscriptstyle")));
2 r 2631
}
2632
 
2028 ihaka 2633
static BBOX RenderStyle(SEXP expr, int draw)
2 r 2634
{
2028 ihaka 2635
    STYLE prevstyle = GetStyle();
2636
    BBOX bbox;
2122 maechler 2637
    if (NameMatch(CAR(expr), "displaystyle"))
2028 ihaka 2638
	SetStyle(STYLE_D);
2122 maechler 2639
    else if (NameMatch(CAR(expr), "textstyle"))
2028 ihaka 2640
	SetStyle(STYLE_T);
2122 maechler 2641
    else if (NameMatch(CAR(expr), "scriptstyle"))
2028 ihaka 2642
	SetStyle(STYLE_S);
2122 maechler 2643
    else if (NameMatch(CAR(expr), "scriptscriptstyle"))
2028 ihaka 2644
	SetStyle(STYLE_SS);
2645
    bbox = RenderElement(CADR(expr), draw);
2646
    SetStyle(prevstyle);
2647
    return bbox;
2 r 2648
}
2649
 
2028 ihaka 2650
/*----------------------------------------------------------------------
2651
 *
2652
 *  Code for Phantom Expressions
2653
 *
2654
 */
2 r 2655
 
2028 ihaka 2656
static int PhantomAtom(SEXP expr)
2 r 2657
{
2028 ihaka 2658
    return (NameAtom(expr) &&
2659
	    (NameMatch(expr, "phantom") ||
2660
	     NameMatch(expr, "vphantom")));
2 r 2661
}
2662
 
2028 ihaka 2663
static BBOX RenderPhantom(SEXP expr, int draw)
2 r 2664
{
2028 ihaka 2665
    BBOX bbox = RenderElement(CADR(expr), 0);
2666
    if (NameMatch(CAR(expr), "vphantom")) {
2667
	bboxWidth(bbox) = 0;
2668
	bboxItalic(bbox) = 0;
2669
    }
11086 ripley 2670
    else RenderGap(bboxWidth(bbox), draw);
2028 ihaka 2671
    return bbox;
2 r 2672
}
2673
 
2028 ihaka 2674
/*----------------------------------------------------------------------
2675
 *
2676
 *  Code for Concatenate Expressions
2677
 *
2678
 */
2 r 2679
 
2028 ihaka 2680
static int ConcatenateAtom(SEXP expr)
2 r 2681
{
2028 ihaka 2682
    return NameAtom(expr) && NameMatch(expr, "paste");
2 r 2683
}
2684
 
2028 ihaka 2685
static BBOX RenderConcatenate(SEXP expr, int draw)
2 r 2686
{
3786 pd 2687
    BBOX bbox = NullBBox();
2028 ihaka 2688
    int i, n;
2 r 2689
 
2028 ihaka 2690
    expr = CDR(expr);
2691
    n = length(expr);
2 r 2692
 
2028 ihaka 2693
    for (i = 0; i < n; i++) {
2694
	bbox = CombineBBoxes(bbox, RenderElement(CAR(expr), draw));
2695
	if (i != n - 1)
2696
	    bbox = RenderItalicCorr(bbox, draw);
2697
	expr = CDR(expr);
2698
    }
2699
    return bbox;
2 r 2700
}
2701
 
2028 ihaka 2702
/*----------------------------------------------------------------------
2703
 *
2704
 *  Code for Comma-Separated Lists
2705
 *
2706
 */
2 r 2707
 
2028 ihaka 2708
static BBOX RenderCommaList(SEXP expr, int draw)
2 r 2709
{
2028 ihaka 2710
    BBOX bbox = NullBBox();
2711
    double small = 0.4 * ThinSpace();
2712
    int i, n;
2713
    n = length(expr);
2714
    for (i = 0; i < n; i++) {
2715
	if (NameAtom(CAR(expr)) && NameMatch(CAR(expr), "...")) {
2716
	    if (i > 0) {
2717
		bbox = CombineBBoxes(bbox, RenderSymbolChar(S_COMMA, draw));
2718
		bbox = CombineBBoxes(bbox, RenderSymbolChar(S_SPACE, draw));
2719
	    }
2720
	    bbox = CombineBBoxes(bbox, RenderSymbolChar(S_ELLIPSIS, draw));
2721
	    bbox = CombineBBoxes(bbox, RenderGap(small, draw));
2722
	}
2723
	else {
2724
	    if (i > 0) {
2725
		bbox = CombineBBoxes(bbox, RenderSymbolChar(S_COMMA, draw));
2726
		bbox = CombineBBoxes(bbox, RenderSymbolChar(S_SPACE, draw));
2727
	    }
2728
	    bbox = CombineBBoxes(bbox, RenderElement(CAR(expr), draw));
2729
	}
2730
	expr = CDR(expr);
2731
    }
2732
    return bbox;
2 r 2733
}
2734
 
2028 ihaka 2735
/*----------------------------------------------------------------------
2736
 *
2737
 *  Code for General Expressions
2738
 *
2739
 */
2 r 2740
 
2028 ihaka 2741
static BBOX RenderExpression(SEXP expr, int draw)
2 r 2742
{
2028 ihaka 2743
    BBOX bbox;
2744
    if (NameAtom(CAR(expr)))
2745
	bbox = RenderSymbolString(CAR(expr), draw);
2746
    else
2747
	bbox = RenderElement(CAR(expr), draw);
2748
    bbox = RenderItalicCorr(bbox, draw);
2105 ihaka 2749
    bbox = CombineBBoxes(bbox, RenderDelimiter(S_PARENLEFT, draw));
2028 ihaka 2750
    bbox = CombineBBoxes(bbox, RenderCommaList(CDR(expr), draw));
2751
    bbox = RenderItalicCorr(bbox, draw);
2105 ihaka 2752
    bbox = CombineBBoxes(bbox, RenderDelimiter(S_PARENRIGHT, draw));
2028 ihaka 2753
    return bbox;
2 r 2754
}
2755
 
2028 ihaka 2756
/*----------------------------------------------------------------------
2757
 *
2758
 *  Code for Comma Separated List Expressions
2759
 *
2760
 */
2 r 2761
 
2028 ihaka 2762
static int ListAtom(SEXP expr)
2 r 2763
{
2028 ihaka 2764
    return NameAtom(expr) && NameMatch(expr, "list");
2 r 2765
}
2766
 
2028 ihaka 2767
static BBOX RenderList(SEXP expr, int draw)
2 r 2768
{
2028 ihaka 2769
    return RenderCommaList(CDR(expr), draw);
2 r 2770
}
2771
 
1839 ihaka 2772
/* Dispatching procedure which determines nature of expression. */
2 r 2773
 
2028 ihaka 2774
 
2775
static BBOX RenderFormula(SEXP expr, int draw)
2 r 2776
{
1839 ihaka 2777
    SEXP head = CAR(expr);
2 r 2778
 
2028 ihaka 2779
    if (SpaceAtom(head))
2780
	return RenderSpace(expr, draw);
2781
    else if (BinAtom(head))
2782
	return RenderBin(expr, draw);
2783
    else if (SuperAtom(head))
2784
	return RenderSup(expr, draw);
2785
    else if (SubAtom(head))
2786
	return RenderSub(expr, draw);
2787
    else if (WideTildeAtom(head))
2788
	return RenderWideTilde(expr, draw);
2789
    else if (WideHatAtom(head))
2790
	return RenderWideHat(expr, draw);
2791
    else if (BarAtom(head))
2792
	return RenderBar(expr, draw);
2793
    else if (AccentAtom(head))
2794
	return RenderAccent(expr, draw);
2795
    else if (OverAtom(head))
2796
	return RenderOver(expr, draw);
2797
    else if (AtopAtom(head))
2798
	return RenderAtop(expr, draw);
2799
    else if (ParenAtom(head))
2800
	return RenderParen(expr, draw);
2801
    else if (BGroupAtom(head))
2802
	return RenderBGroup(expr, draw);
2803
    else if (GroupAtom(head))
2804
	return RenderGroup(expr, draw);
2805
    else if (IntAtom(head))
3475 pd 2806
	return RenderInt(expr, draw);
2028 ihaka 2807
    else if (OpAtom(head))
2808
	return RenderOp(expr, draw);
2809
    else if (RadicalAtom(head))
2810
	return RenderRadical(expr, draw);
2811
    else if (AbsAtom(head))
2812
	return RenderAbs(expr, draw);
2813
    else if (CurlyAtom(head))
2814
	return RenderCurly(expr, draw);
2815
    else if (RelAtom(head))
2816
	return RenderRel(expr, draw);
2817
    else if (BoldAtom(head))
2818
	return RenderBold(expr, draw);
2819
    else if (ItalicAtom(head))
2820
	return RenderItalic(expr, draw);
2821
    else if (PlainAtom(head))
2822
	return RenderPlain(expr, draw);
2823
    else if (BoldItalicAtom(head))
2824
	return RenderBoldItalic(expr, draw);
2825
    else if (StyleAtom(head))
2826
	return RenderStyle(expr, draw);
2827
    else if (PhantomAtom(head))
2828
	return RenderPhantom(expr, draw);
2829
    else if (ConcatenateAtom(head))
2830
	return RenderConcatenate(expr, draw);
2831
    else if (ListAtom(head))
2832
	return RenderList(expr, draw);
1839 ihaka 2833
    else
2028 ihaka 2834
	return RenderExpression(expr, draw);
2 r 2835
}
2836
 
2837
 
2028 ihaka 2838
/* Dispatch on whether atom (symbol, string, number, ...) */
2839
/* or formula (some sort of expression) */
2 r 2840
 
2028 ihaka 2841
static BBOX RenderElement(SEXP expr, int draw)
2842
{
2843
    if (FormulaExpression(expr))
2844
	return RenderFormula(expr, draw);
1839 ihaka 2845
    else
2028 ihaka 2846
	return RenderAtom(expr, draw);
2 r 2847
}
2848
 
2028 ihaka 2849
static BBOX RenderOffsetElement(SEXP expr, double x, double y, int draw)
2850
{
2851
    BBOX bbox;
2852
    double savedX = CurrentX;
2853
    double savedY = CurrentY;
2854
    if (draw) {
2855
	CurrentX += x;
2856
	CurrentY += y;
2857
    }
2858
    bbox = RenderElement(expr, draw);
2859
    bboxWidth(bbox) += x;
2860
    bboxHeight(bbox) += y;
2861
    bboxDepth(bbox) -= y;
2862
    CurrentX = savedX;
2863
    CurrentY = savedY;
2864
    return bbox;
2 r 2865
 
2866
}
2867
 
19875 murrell 2868
/* Functions forming the R API */
2869
 
2028 ihaka 2870
/* Calculate width of expression */
2871
/* BBOXes are in INCHES (see MetricUnit) */
2872
 
19875 murrell 2873
double GEExpressionWidth(SEXP expr, 
2874
			 int font, double cex, double ps,
2875
			 GEDevDesc *dd)
2 r 2876
{
2679 pd 2877
    BBOX bbox;
2878
    double width;
2879
    MathDevice = dd;
19875 murrell 2880
    BaseCex = cex;
2881
    MathFont = font;
2882
    MathCex = cex;
2883
    MathPs = ps;
2679 pd 2884
    CurrentStyle = STYLE_D;
2885
    SetFont(PlainFont);
2886
    bbox = RenderElement(expr, 0);
2887
    width  = bboxWidth(bbox);
20257 murrell 2888
    /* 
2889
     * NOTE that we do fabs() here in case the device
2890
     * runs right-to-left.
2891
     * This is so that these calculations match those
2892
     * for string widths and heights, where the width
2893
     * and height of text is positive no matter how
2894
     * the device drawing is oriented.
2895
     */
2896
    return fabs(toDeviceWidth(width, GE_INCHES, dd));
257 paul 2897
}
2898
 
19875 murrell 2899
double GEExpressionHeight(SEXP expr, 
2900
			  int font, double cex, double ps,
2901
			  GEDevDesc *dd)
257 paul 2902
{
2679 pd 2903
    BBOX bbox;
2904
    double height;
2905
    MathDevice = dd;
19875 murrell 2906
    BaseCex = cex;
2907
    MathFont = font;
2908
    MathCex = cex;
2909
    MathPs = ps;
2679 pd 2910
    CurrentStyle = STYLE_D;
2911
    SetFont(PlainFont);
2912
    bbox = RenderElement(expr, 0);
2913
    height = bboxHeight(bbox) + bboxDepth(bbox);
20257 murrell 2914
    /* NOTE that we do fabs() here in case the device
2915
     * draws top-to-bottom (like an X11 window).
2916
     * This is so that these calculations match those
2917
     * for string widths and heights, where the width
2918
     * and height of text is positive no matter how
2919
     * the device drawing is oriented.
2920
     */
2921
    return fabs(toDeviceHeight(height, GE_INCHES, dd));
257 paul 2922
}
2923
 
19875 murrell 2924
void GEMathText(double x, double y, SEXP expr,
2925
		double xc, double yc, double rot, 
2926
		int col, double gamma, int font, double cex, double ps,
2927
		GEDevDesc *dd)
2 r 2928
{
2028 ihaka 2929
    BBOX bbox;
6098 pd 2930
 
2931
#ifdef BUG61
2932
#else
2933
    /* IF font metric information is not available for device */
2934
    /* then bail out */
2935
    double ascent, descent, width;
19875 murrell 2936
    GEMetricInfo(0, 
2937
		font, cex, ps,
2938
		&ascent, &descent, &width, dd);
6098 pd 2939
    if ((ascent==0) && (descent==0) && (width==0))
6191 maechler 2940
	error("Metric information not yet available for this device");
6098 pd 2941
#endif
2942
 
2028 ihaka 2943
    MathDevice = dd;
19875 murrell 2944
    BaseCex = cex;
2028 ihaka 2945
    BoxColor = name2col("pink");
19875 murrell 2946
    TextColor = col;
2947
    MathGamma = gamma;
21062 murrell 2948
    /* 
2949
     * FIXME:  When fontfamily and lineheight are passed to GEMathText
2950
     * need to use those instead of "" and 1 below
2951
     */
2952
    MathFontFamily = "";
19875 murrell 2953
    MathFont = font;
21062 murrell 2954
    MathLineHeight = 1;
19875 murrell 2955
    MathCex = cex;
2956
    MathPs = ps;
2028 ihaka 2957
    CurrentStyle = STYLE_D;
2105 ihaka 2958
    SetFont(PlainFont);
2028 ihaka 2959
    bbox = RenderElement(expr, 0);
19875 murrell 2960
    ReferenceX = fromDeviceX(x, GE_INCHES, dd);
2961
    ReferenceY = fromDeviceY(y, GE_INCHES, dd);
5107 maechler 2962
    if (R_FINITE(xc))
2028 ihaka 2963
	CurrentX = ReferenceX - xc * bboxWidth(bbox);
2964
    else
18202 murrell 2965
	/* Paul 11/2/02
2966
	 * If xc == NA then should centre horizontally.
2967
	 * Used to left-adjust.
2968
	 */
2969
	CurrentX = ReferenceX - 0.5 * bboxWidth(bbox);
5107 maechler 2970
    if (R_FINITE(yc))
2028 ihaka 2971
	CurrentY = ReferenceY + bboxDepth(bbox)
2972
	    - yc * (bboxHeight(bbox) + bboxDepth(bbox));
2973
    else
18202 murrell 2974
	/* Paul 11/2/02
2975
	 * If xc == NA then should centre vertically.
2976
	 * Used to bottom-adjust.
2977
	 */
2978
	CurrentY = ReferenceY + bboxDepth(bbox)
2979
	    - 0.5 * (bboxHeight(bbox) + bboxDepth(bbox));
2028 ihaka 2980
    CurrentAngle = rot;
7527 maechler 2981
    rot *= M_PI_2 / 90 ;/* radians */
2122 maechler 2982
    CosAngle = cos(rot);
2983
    SinAngle = sin(rot);
2028 ihaka 2984
    RenderElement(expr, 1);
10886 maechler 2985
}/* GMathText */
2 r 2986
 
2987
 
19875 murrell 2988
/********************************
2989
 * Code below here ...
2990
 * ... should be moved to base.c and 
2991
 * ... is part of the base graphics API NOT the graphics engine API
2992
 ********************************
2993
 */
2994
double GExpressionWidth(SEXP expr, GUnit units, DevDesc *dd)
2995
{
2996
    double width = GEExpressionWidth(expr, 
2997
				     Rf_gpptr(dd)->font, Rf_gpptr(dd)->cex, 
2998
				     Rf_gpptr(dd)->ps, (GEDevDesc*) dd);
2999
    if (units == DEVICE)
3000
	return width;
3001
    else
3002
	return GConvertXUnits(width, DEVICE, units, dd);
3003
}
3004
 
3005
double GExpressionHeight(SEXP expr, GUnit units, DevDesc *dd)
3006
{
3007
    double height = GEExpressionHeight(expr, 
3008
				       Rf_gpptr(dd)->font, Rf_gpptr(dd)->cex, 
3009
				       Rf_gpptr(dd)->ps, (GEDevDesc*) dd);
3010
    if (units == DEVICE)
3011
	return height;
3012
    else
3013
	return GConvertYUnits(height, DEVICE, units, dd);
3014
}
3015
 
19876 murrell 3016
/* This is just here to satisfy the Rgraphics.h API.
3017
 * This allows new graphics API (GraphicsDevice.h, GraphicsEngine.h) 
3018
 * to be developed alongside.
3019
 * Could be removed if Rgraphics.h ever gets REPLACED by new API
3020
 * NOTE that base graphics code no longer calls this -- the base
3021
 * graphics system directly calls the graphics engine for mathematical
3022
 * annotation (GEMathText)
3023
 */
3024
void GMathText(double x, double y, int coords, SEXP expr,
3025
	       double xc, double yc, double rot, 
3026
	       DevDesc *dd)
3027
{
3028
    GConvert(&x, &y, coords, DEVICE, dd);
20197 murrell 3029
    GClip(dd);
19876 murrell 3030
    GEMathText(x, y, expr, xc, yc, rot, 
3031
	       Rf_gpptr(dd)->col, Rf_gpptr(dd)->gamma, Rf_gpptr(dd)->font,
3032
	       Rf_gpptr(dd)->cex, Rf_gpptr(dd)->ps, (GEDevDesc*) dd);
3033
}
3034
 
2028 ihaka 3035
void GMMathText(SEXP str, int side, double line, int outer,
3036
		double at, int las, DevDesc *dd)
2 r 3037
{
13400 hornik 3038
    int coords = 0, subcoords;
3039
    double xadj, yadj = 0, angle = 0;
2 r 3040
 
6098 pd 3041
#ifdef BUG61
3042
#else
3043
    /* IF font metric information is not available for device */
3044
    /* then bail out */
3045
    double ascent, descent, width;
3046
    GMetricInfo(0, &ascent, &descent, &width, DEVICE, dd);
3047
    if ((ascent==0) && (descent==0) && (width==0))
6191 maechler 3048
	error("Metric information not yet available for this device");
6098 pd 3049
#endif
3050
 
19875 murrell 3051
    MathDevice = (GEDevDesc*) dd;
581 paul 3052
 
19875 murrell 3053
    xadj = Rf_gpptr(dd)->adj;
12841 murrell 3054
 
3055
    /* This is MOSTLY the same as the same section of GMtext
3056
     * BUT it differs because it sets different values for yadj for
3057
     * different situations.
3058
     * Paul
3059
     */
3060
    if(outer) {
3061
	switch(side) {
3062
	case 1:	    coords = OMA1;	break;
3063
	case 2:	    coords = OMA2;	break;
3064
	case 3:	    coords = OMA3;	break;
3065
	case 4:	    coords = OMA4;	break;
2 r 3066
	}
12841 murrell 3067
	subcoords = NIC;
1839 ihaka 3068
    }
3069
    else {
12841 murrell 3070
	switch(side) {
3071
	case 1:	    coords = MAR1;	break;
3072
	case 2:	    coords = MAR2;	break;
3073
	case 3:	    coords = MAR3;	break;
3074
	case 4:	    coords = MAR4;	break;
2 r 3075
	}
12841 murrell 3076
	subcoords = USER;
1839 ihaka 3077
    }
17179 murrell 3078
    /* Note: I changed Rf_gpptr(dd)->yLineBias to 0.3 here. */
12841 murrell 3079
    /* Purely visual tuning. RI */
16224 maechler 3080
    /* Note: I removed the 0.3 fiddle here because mathematical
15168 pd 3081
     * annotation stuff can do "exact" centering.
3082
     * i.e., 0.3 fiddle is effectively replaced by yadj=0.5
3083
     */
12841 murrell 3084
    switch(side) {
3085
    case 1:
3086
	if(las == 2 || las == 3) {
3087
	    angle = 90;
3088
	    yadj = 0.5;
3089
	}
3090
	else {
18202 murrell 3091
	    /*	    line = line + 1 - Rf_gpptr(dd)->yLineBias;
3092
		    angle = 0;
3093
		    yadj = NA_REAL; */
3094
	    line = line + 1;
12841 murrell 3095
	    angle = 0;
18202 murrell 3096
	    yadj = 0;
12841 murrell 3097
	}
3098
	break;
3099
    case 2:
3100
	if(las == 1 || las == 2) {
3101
	    angle = 0;
3102
	    yadj = 0.5;
3103
	}
3104
	else {
18202 murrell 3105
	    /*	    line = line + Rf_gpptr(dd)->yLineBias;
3106
		    angle = 90;
3107
		    yadj = NA_REAL; */
12841 murrell 3108
	    angle = 90;
18202 murrell 3109
	    yadj = 0;
12841 murrell 3110
	}
3111
	break;
3112
    case 3:
3113
	if(las == 2 || las == 3) {
3114
	    angle = 90;
3115
	    yadj = 0.5;
3116
	}
3117
	else {
18202 murrell 3118
	    /*   line = line + Rf_gpptr(dd)->yLineBias;
3119
		 angle = 0;
3120
		 yadj = NA_REAL; */
12841 murrell 3121
	    angle = 0;
18202 murrell 3122
	    yadj = 0;
12841 murrell 3123
	}
3124
	break;
3125
    case 4:
3126
	if(las == 1 || las == 2) {
3127
	    angle = 0;
3128
	    yadj = 0.5;
3129
	}
3130
	else {
18202 murrell 3131
	    /*   line = line + 1 - Rf_gpptr(dd)->yLineBias;
3132
		 angle = 90;
3133
		 yadj = NA_REAL; */
3134
	    line = line + 1;
12841 murrell 3135
	    angle = 90;
18202 murrell 3136
	    yadj = 0;
12841 murrell 3137
	}
3138
	break;
3139
    }
19875 murrell 3140
    GConvert(&at, &line, coords, DEVICE, dd);
20197 murrell 3141
    /* The graphics engine (unlike graphics.c code) knows nothing about
3142
     * xpd settings (that is a base graphics system concept), hence
3143
     * the graphics engine does not call GClip.  So we must call GClip
3144
     * here to make sure that the current xpd setting is enforced
3145
     * on the device.
3146
     */
3147
    GClip(dd);
19875 murrell 3148
    GEMathText(at, line, str, xadj, yadj, angle, 
3149
	       Rf_gpptr(dd)->col, Rf_gpptr(dd)->gamma, Rf_gpptr(dd)->font,
3150
	       Rf_gpptr(dd)->cex, Rf_gpptr(dd)->ps, (GEDevDesc*) dd);
10886 maechler 3151
}/* GMMathText */