The R Project SVN R

Rev

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