The R Project SVN R

Rev

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