The R Project SVN R

Rev

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