The R Project SVN R

Rev

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