The R Project SVN R

Rev

Rev 1917 | Rev 2028 | 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:
6
 *  Copyright (C) 1997 Paul Murrell and Ross Ihaka
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"
1926 ihaka 26
 
2 r 27
#ifdef max
28
#undef max
29
#endif
30
 
581 paul 31
static DevDesc *mathDevice;
32
 
1839 ihaka 33
/* Return maximum of two doubles. */
2 r 34
 
35
static double max(double x, double y)
36
{
1839 ihaka 37
    if (x > y) return x;
38
    else return y;
2 r 39
}
40
 
1926 ihaka 41
/* Determine a match between symbol name and string. */
2 r 42
 
43
static int symbolMatch(SEXP expr, char *aString)
44
{
1839 ihaka 45
    return !strcmp(CHAR(PRINTNAME(expr)), aString);
2 r 46
}
47
 
1839 ihaka 48
/* Code to determine the ascii code corresponding */
49
/* to an element of a mathematical expression. */
2 r 50
 
51
static int hatAscii()
52
{
1839 ihaka 53
    return 94;
2 r 54
}
55
 
56
static int tildeAscii()
57
{
1839 ihaka 58
    return 126;
2 r 59
}
60
 
61
static int accentAscii(SEXP expr)
62
{
1839 ihaka 63
    int result = 0;
2 r 64
 
1839 ihaka 65
    if (symbolMatch(expr, "hat"))
66
	result = hatAscii();
67
    else if (symbolMatch(expr, "tilde"))
68
	result = tildeAscii();
69
    return result;
2 r 70
}
71
 
72
static int operatorAscii(SEXP expr)
73
{
1839 ihaka 74
    int result = 0;
75
    if (symbolMatch(expr, "sum"))
76
	result = 229;
77
    else if (symbolMatch(expr, "integral"))
78
	result = 242;
79
    else if (symbolMatch(expr, "product"))
80
	result = 213;
81
    return result;
2 r 82
}
83
 
84
static int integralAscii(int section)
85
{
1839 ihaka 86
    if (section == 1)
87
	return 243;
88
    else if (section == 2)
89
	return 244;
90
    else
91
	return 245;
2 r 92
}
93
 
94
static int groupOpenAscii()
95
{
1839 ihaka 96
    return 40;
2 r 97
}
98
 
99
static int groupCloseAscii()
100
{
1839 ihaka 101
    return 41;
2 r 102
}
103
 
104
static int commaAscii()
105
{
1839 ihaka 106
    return 44;
2 r 107
}
108
 
109
static int spaceAscii()
110
{
1839 ihaka 111
    return 32;
2 r 112
}
113
 
114
static int radicalAscii()
115
{
1839 ihaka 116
    return 214;
2 r 117
}
118
 
119
static int radicalExAscii()
120
{
1839 ihaka 121
    return 96;
2 r 122
}
123
 
124
static struct {
1839 ihaka 125
    char *name;
126
    int code;
127
}
1926 ihaka 128
SymbolTable[] = {
129
    "Alpha",          65,    /* Upper Case Greek Characters */
130
    "Beta",           66,
131
    "Chi",            67,
132
    "Delta",          68,
133
    "Epsilon",        69,
134
    "Phi",            70,
135
    "Gamma",          71,
136
    "Eta",            72,
137
    "Iota",           73,
138
    "Phi1",           74,
139
    "Kappa",          75,
140
    "Lambda",         76,
141
    "Mu",             77,
142
    "Nu",             78,
143
    "Omicron",        79,
144
    "Pi",             80,
145
    "Theta",          81,
146
    "Rho",            82,
147
    "Sigma",          83,
148
    "Tau",            84,
149
    "Upsilon",        85,
150
    "sigma1",         86,
151
    "Omega",          87,
152
    "Xi",             88,
153
    "Psi",            89,
154
    "Zeta",           90,
2 r 155
 
1926 ihaka 156
    "alpha",          97,    /* Lower Case Greek Characters */
157
    "beta",           98,
158
    "chi",            99,
159
    "delta",         100,
160
    "epsilon",       101,
161
    "phi",           102,
162
    "gamma",         103,
163
    "eta",           104,
164
    "iota",          105,
165
    "phi1",          106,
166
    "kappa",         107,
167
    "lambda",        108,
168
    "mu",            109,
169
    "nu",            110,
170
    "omicron",       111,
171
    "pi",            112,
172
    "theta",         113,
173
    "rho",           114,
174
    "sigma",         115,
175
    "tau",           116,
176
    "upsilon",       117,
177
    "omega1",        118,
178
    "omega",         119,
179
    "xi",            120,
180
    "psi",           121,
181
    "zeta",          122,
2 r 182
 
1926 ihaka 183
    "universal",      34,    /* Miscellaneous Special Characters */
184
    "existential",    36,
185
    "therefore",      92,
186
    "perpendicular",  94,
187
    "minute",        162,
188
    "infinity",      165,
189
    "club",          167,
190
    "diamond",       168,
191
    "heart",         169,
192
    "spade",         170,
193
    "degree",        176,
194
    "second",        178,
195
    "partialdiff",   182,
196
    "bullet",        183,
197
    "ellipsis",      188,
198
    "aleph",         192,
199
    "Ifraktur",      193,
200
    "Rfraktur",      194,
201
    "weierstrass",   195,
202
    "emptyset",      198,
203
    "angle",         208,
204
    "gradient",      209,
2 r 205
 
1926 ihaka 206
    NULL,              0,
2 r 207
};
208
 
1926 ihaka 209
static int symbolAscii(SEXP expr)
2 r 210
{
1839 ihaka 211
    int i;
1926 ihaka 212
    for (i = 0; SymbolTable[i].code; i++)
213
	if (symbolMatch(expr, SymbolTable[i].name))
214
	    return SymbolTable[i].code;
1839 ihaka 215
    return 0;
216
}
2 r 217
 
1926 ihaka 218
 
219
static struct {
220
    char *name;
221
    int code;
222
}
223
RelationTable[] = {
224
    "<",                 60,    /* Binary Relationships */
225
    "==",                61,
226
    ">",                 62,
227
    "%=~%",              64,
228
    "!=",               185,
229
    "<=",               163,
230
    "%+-%",             177,
231
    ">=",               179,
232
    "%*%",              180,
233
    "%/%",              184,
234
    "%==%",             186,
235
    "%~~%",             187,
236
    "&",                217,
237
    "&&",               217,
238
    "|",                218,
239
    "||",               218,
240
 
241
    "%<->%",            171,    /* Arrows */
242
    "%<-%",             172,
243
    "%arrowup%",        173,
244
    "%->%",             174,
245
    "%arrowdown%",      175,
246
    "%<=>%",            219,
247
    "%<=%",             220,
248
    "%arrowdblup%",     221,
249
    "%=>%",             222,
250
    "%arrowdbldown%",   223,
251
 
252
    "%congruent%",       64,    /* Long (PostScript) Names */
253
    "%plusminus%",      177,
254
    "%multiply%",       180,
255
    "%proportional%",   181,
256
    "%divide%",         184,
257
    "%equivalence%",    186,
258
    "%approxequal%",    187,
259
    "%intersection%",   199,
260
    "%union%",          200,
261
    "%propersuperset%", 201,
262
    "%reflexsuperset%", 202,
263
    "%notsubset%",      203,
264
    "%propersubset%",   204,
265
    "%reflexsubset%",   205,
266
    "%element%",        206,
267
    "%notelement%",     207,
268
    "logicaland%",      217,
269
    "logicalor%",       218,
270
 
271
    NULL,                0,
272
};
273
 
274
 
275
/* Added argument - ihaka */
276
static int relAscii(SEXP expr)
1839 ihaka 277
{
1926 ihaka 278
    int i;
279
    for (i = 0; RelationTable[i].code; i++)
280
	if (symbolMatch(expr, RelationTable[i].name))
281
	    return RelationTable[i].code;
282
    return 0;
2 r 283
}
284
 
1926 ihaka 285
 
1839 ihaka 286
/* Initialisation code for mathematical notation. */
2 r 287
 
288
static double ratioScale = 0.8;
289
static double scriptScale = 0.65;
290
static int ratioDepth = 0;
581 paul 291
static int metricUnit = INCHES;
2 r 292
 
293
 
1839 ihaka 294
/* Code to determine the nature of an expression. */
2 r 295
 
296
static int formulaExpression(SEXP expr)
297
{
1839 ihaka 298
    return (TYPEOF(expr) == LANGSXP);
2 r 299
}
300
 
301
static int symbolAtom(SEXP expr)
302
{
1839 ihaka 303
    return (TYPEOF(expr) == SYMSXP);
2 r 304
}
305
 
306
static int numberAtom(SEXP expr)
307
{
1839 ihaka 308
    return ((TYPEOF(expr) == REALSXP) ||
309
	    (TYPEOF(expr) == INTSXP)  ||
310
	    (TYPEOF(expr) == CPLXSXP));
2 r 311
}
312
 
313
static int stringAtom(SEXP expr)
314
{
1839 ihaka 315
    return (TYPEOF(expr) == STRSXP);
2 r 316
}
317
 
1926 ihaka 318
static int spaceAtom(SEXP expr)
319
{
320
    return symbolAtom(expr) && symbolMatch(expr, "~");
321
}
322
 
323
static struct {
324
    char *name;
325
    int code;
326
}
327
BinopTable[] = {
328
    "*",                42,    /* Binary Relationships */
329
    "+",                43,
330
    "-",                45,
331
    "/",                47,
332
    ":",                58,
333
    NULL,                0
334
};
335
 
2 r 336
static int binAtom(SEXP expr)
337
{
1926 ihaka 338
    int i;
339
    for (i = 0; BinopTable[i].code; i++)
340
	if (symbolMatch(expr, BinopTable[i].name))
341
	    return BinopTable[i].code;
342
    return 0;
2 r 343
}
344
 
345
static int relAtom(SEXP expr)
346
{
1926 ihaka 347
    int i;
348
    for (i = 0; RelationTable[i].code; i++)
349
	if (symbolMatch(expr, RelationTable[i].name))
350
	    return 1;
351
    return 0;
2 r 352
}
353
 
354
static int multiplicationOperator(SEXP expr)
355
{
1926 ihaka 356
    return binAtom(expr) == 42;
2 r 357
}
358
 
359
static int superAtom(SEXP expr)
360
{
1926 ihaka 361
    return symbolAtom(expr) && symbolMatch(expr, "^");
2 r 362
}
363
 
364
static int subAtom(SEXP expr)
365
{
1926 ihaka 366
    return symbolAtom(expr) && symbolMatch(expr, "[");
2 r 367
}
368
 
369
static int hatAtom(SEXP expr)
370
{
1839 ihaka 371
    return symbolMatch(expr, "hat");
2 r 372
}
373
 
374
static int barAtom(SEXP expr)
375
{
1839 ihaka 376
    return symbolMatch(expr, "bar");
2 r 377
}
378
 
379
static int accentAtom(SEXP expr)
380
{
1839 ihaka 381
    return symbolAtom(expr) &&
382
	(hatAtom(expr) || barAtom(expr) || symbolMatch(expr, "tilde"));
2 r 383
}
384
 
385
static int fractionAtom(SEXP expr)
386
{
1839 ihaka 387
    return symbolAtom(expr) &&
388
	(symbolMatch(expr, "over") || symbolMatch(expr, "frac"));
2 r 389
}
390
 
391
static int groupAtom(SEXP expr)
392
{
1926 ihaka 393
    return symbolAtom(expr) && symbolMatch(expr, "(");
2 r 394
}
395
 
396
static int operatorAtom(SEXP expr)
397
{
1839 ihaka 398
    return symbolAtom(expr) &&
399
	(symbolMatch(expr, "sum") ||
400
	 symbolMatch(expr, "integral") ||
401
	 symbolMatch(expr, "product"));
2 r 402
}
403
 
404
static int integralOperator(SEXP expr)
405
{
1839 ihaka 406
    return symbolAtom(expr) && symbolMatch(expr, "integral");
2 r 407
}
408
 
409
static int radicalAtom(SEXP expr)
410
{
1839 ihaka 411
    return symbolAtom(expr) &&
412
	(symbolMatch(expr, "root") || symbolMatch(expr, "sqrt"));
2 r 413
}
414
 
415
static int absAtom(SEXP expr)
416
{
1839 ihaka 417
    return symbolAtom(expr) && symbolMatch(expr, "abs");
2 r 418
}
419
 
420
static int curlyAtom(SEXP expr)
421
{
1839 ihaka 422
    return symbolAtom(expr) && symbolMatch(expr, "{");
2 r 423
}
424
 
425
static int boldAtom(SEXP expr)
426
{
1839 ihaka 427
    return symbolAtom(expr) && symbolMatch(expr, "bold");
2 r 428
}
429
 
430
static int italicAtom(SEXP expr)
431
{
1839 ihaka 432
    return symbolAtom(expr) && symbolMatch(expr, "italic");
2 r 433
}
434
 
435
static int plainAtom(SEXP expr)
436
{
1839 ihaka 437
    return symbolAtom(expr) && symbolMatch(expr, "plain");
2 r 438
}
439
 
440
static int boldItalicAtom(SEXP expr)
441
{
1839 ihaka 442
    return symbolAtom(expr) && symbolMatch(expr, "bolditalic");
2 r 443
}
444
 
445
static int italicExpression(SEXP expr)
446
{
1839 ihaka 447
    return formulaExpression(expr) &&
448
	(italicAtom(CAR(expr)) || boldItalicAtom(CAR(expr)));
2 r 449
}
450
 
451
static int nonItalicExpression(SEXP expr)
452
{
1839 ihaka 453
    return formulaExpression(expr) &&
454
	(boldAtom(CAR(expr)) || plainAtom(CAR(expr)));
2 r 455
}
456
 
457
static int concatenateAtom(SEXP expr)
458
{
1839 ihaka 459
    return symbolAtom(expr) && symbolMatch(expr, "paste");
2 r 460
}
461
 
1926 ihaka 462
static int symbolSymbol(SEXP expr)
2 r 463
{
1839 ihaka 464
    int i;
465
    if (symbolAtom(expr)) {
1926 ihaka 466
	for (i = 0; SymbolTable[i].code; i++)
467
	    if (symbolMatch(expr, SymbolTable[i].name))
1839 ihaka 468
		return 1;
469
    }
470
    return 0;
2 r 471
}
472
 
1839 ihaka 473
/* Code to determine a font from the */
474
/* nature of the expression */
2 r 475
 
476
static int currentFont = 3;
477
 
478
static int getFont() { return currentFont; }
479
 
480
static void setFont(font) { currentFont = font; }
481
 
482
static void boldFont() { setFont(2); }
483
static void italicFont() { setFont(3); }
484
static void plainFont() { setFont(1); }
485
static void boldItalicFont() { setFont(4); }
1016 maechler 486
 
2 r 487
static int isItalic() { return (getFont() == 3 || getFont() == 4); }
1016 maechler 488
 
1926 ihaka 489
/* Determine the appropriate font for an atom. */
490
/* Switch to symbol for greek and math and */
491
/* switch to plain for numbers. */
492
 
493
/* FIXME : Should this switch to font=3 if the */
494
/* current font is 4 (bolditalic). */
495
 
2 r 496
static int atomFontFace(SEXP expr)
497
{
1926 ihaka 498
    int fontFace = getFont();
499
    if (symbolAtom(expr) && (symbolSymbol(expr) ||
500
			     binAtom(expr) ||
501
			     relAtom(expr) ||
502
			     groupAtom(expr) ||
503
			     operatorAtom(expr) ||
504
			     radicalAtom(expr)))
505
	fontFace = 5;
506
    else if (numberAtom(expr))
507
	fontFace = 1;
1839 ihaka 508
    return fontFace;
2 r 509
}
510
 
1839 ihaka 511
/* a forward declaration */
2 r 512
static double fontHeight();
513
 
1839 ihaka 514
/* some forward declarations */
2 r 515
typedef struct {
1839 ihaka 516
    double height;
517
    double depth;
518
    double width;
2 r 519
} BBOX;
520
 
521
static double bboxHeight(BBOX bbox)
522
{
1839 ihaka 523
    return bbox.height;
2 r 524
}
525
static double bboxDepth(BBOX bbox)
526
{
1839 ihaka 527
    return bbox.depth;
2 r 528
}
529
static double bboxWidth(BBOX bbox)
530
{
1839 ihaka 531
    return bbox.width;
2 r 532
}
533
static BBOX asciiBBox(int ascii);
534
static BBOX elementBBox(SEXP expr);
535
static void drawElement(SEXP expr);
536
 
537
 
1839 ihaka 538
/* code to determine superscript offsets, etc ... */
2 r 539
 
1839 ihaka 540
/* these "twiddle factors" are given as  */
541
/* proportions of the current font height */
542
/* NOTE that metric information is obtained */
543
/* in INCHES so that the information will */
544
/* be useful for rotated math.text */
2 r 545
 
546
 
547
static float SuperDrop = 0.3;
548
static float Superscript = 0.3;
549
static float SubDrop = 0.1;
550
static float Subscript = -0.3;
551
static float LineWidth = 0.05;
552
 
553
static float CustomAccentGap = 0.2;
554
static float CustomHatHeight = 0.3;
555
static float CustomRadicalWidth = 0.6;
556
static float CustomRadicalSpace = 0.1;
557
static float CustomRadicalGap = 0.2;
558
static float AbsSpace = 0.2;
559
static float OperatorSpace[] = { 0.1, 0.15, 0.2, 0.6, 0.1 };
560
 
561
static double fontHeight()
562
{
1839 ihaka 563
    double height, depth, width;
564
    GMetricInfo(0, &height, &depth, &width, metricUnit, mathDevice);
565
    return height + depth;
2 r 566
}
567
 
568
static double xHeight()
569
{
1839 ihaka 570
    int x = 'x';
571
    double xheight, depth, width;
572
    GMetricInfo(x, &xheight, &depth, &width, metricUnit, mathDevice);
573
    return xheight;
2 r 574
}
575
 
576
static double axisHeight()
577
{
1839 ihaka 578
    int plus = '+';
579
    double plusHeight, depth, width;
580
    GMetricInfo(plus, &plusHeight, &depth, &width, metricUnit, mathDevice);
581
    return 0.5 * plusHeight;
2 r 582
}
583
 
584
static double superscriptDrop()
585
{
1839 ihaka 586
    return SuperDrop * fontHeight();
2 r 587
}
588
static double subscriptDrop()
589
{
1839 ihaka 590
    return SubDrop * fontHeight();
2 r 591
}
592
static double superscript()
593
{
1839 ihaka 594
    return Superscript * fontHeight();
2 r 595
}
596
static double subscript()
597
{
1839 ihaka 598
    return Subscript * fontHeight();
2 r 599
}
600
static double lineWidth()
601
{
1839 ihaka 602
    return LineWidth * fontHeight();
2 r 603
}
604
static double numeratorShift()
605
{
1839 ihaka 606
    return axisHeight() + 3 * lineWidth();
2 r 607
}
608
static double denominatorShift()
609
{
1839 ihaka 610
    return axisHeight() - 3 * lineWidth() - xHeight();
2 r 611
}
612
static double radicalDrop()
613
{
1839 ihaka 614
    return 1.25 * lineWidth();
2 r 615
}
616
static double customAccentGap()
617
{
1839 ihaka 618
    return CustomAccentGap * fontHeight();
2 r 619
}
620
static double customHatHeight()
621
{
1839 ihaka 622
    return CustomHatHeight * fontHeight();
2 r 623
}
624
static double customRadicalWidth()
625
{
1839 ihaka 626
    return CustomRadicalWidth * fontHeight();
2 r 627
}
628
static double customRadicalSpace()
629
{
1839 ihaka 630
    return CustomRadicalSpace * fontHeight();
2 r 631
}
632
static double customRadicalGap()
633
{
1839 ihaka 634
    return CustomRadicalGap * fontHeight();
2 r 635
}
636
static double absSpace()
637
{
1839 ihaka 638
    return AbsSpace * fontHeight();
2 r 639
}
640
static double operatorSpace(int i)
641
{
1839 ihaka 642
    return OperatorSpace[i] * fontHeight();
2 r 643
}
644
 
645
 
1839 ihaka 646
/* Should only be called when font=5 (symbol) */
2 r 647
 
648
static double radicalExWidth()
649
{
1839 ihaka 650
    int radicalEx = 96;
651
    double height, depth, REWidth;
652
    GMetricInfo(radicalEx, &height, &depth, &REWidth, metricUnit, mathDevice);
653
    return REWidth;
2 r 654
}
655
 
656
static double superscriptShift(SEXP body, SEXP sup)
657
{
1839 ihaka 658
    BBOX bodyBBox = elementBBox(body);
659
    BBOX superscriptBBox;
660
    float cexSaved = mathDevice->gp.cex;
661
    double temp1 = bboxHeight(bodyBBox) - superscriptDrop();
662
    double temp2 = superscript();
663
    double temp3;
2 r 664
 
1839 ihaka 665
    mathDevice->gp.cex = mathDevice->gp.cex * scriptScale;
666
    superscriptBBox = elementBBox(sup);
667
    mathDevice->gp.cex = cexSaved;
668
    temp3 = bboxDepth(superscriptBBox) + 0.25 * xHeight();
2 r 669
 
1839 ihaka 670
    return max(temp1, max(temp2, temp3));
2 r 671
}
672
 
673
static double subscriptShift(SEXP body, SEXP sub, int subOnly)
674
{
1839 ihaka 675
    BBOX bodyBBox = elementBBox(body);
676
    BBOX subscriptBBox;
677
    float cexSaved = mathDevice->gp.cex;
678
    double temp1 = bboxDepth(bodyBBox) + subscriptDrop();
679
    double temp2 = subscript();
680
    double temp3;
2 r 681
 
1839 ihaka 682
    mathDevice->gp.cex = mathDevice->gp.cex * scriptScale;
683
    subscriptBBox = elementBBox(sub);
684
    mathDevice->gp.cex = cexSaved;
685
    temp3 = bboxHeight(subscriptBBox) - (4 * xHeight() / 5);
2 r 686
 
1839 ihaka 687
    if (subOnly)
688
	return max(temp1, max(temp2, temp3));
689
    else
690
	return max(temp1, temp2);
2 r 691
}
692
 
693
static void supsubShift(SEXP body, SEXP sup, SEXP sub,
694
			double *supShift, double *subShift)
695
{
1839 ihaka 696
    BBOX superscriptBBox, subscriptBBox;
697
    float cexSaved = mathDevice->gp.cex;
698
    double temp1, temp2;
2 r 699
 
1839 ihaka 700
    mathDevice->gp.cex = mathDevice->gp.cex * scriptScale;
701
    superscriptBBox = elementBBox(sup);
702
    subscriptBBox = elementBBox(sub);
703
    mathDevice->gp.cex = cexSaved;
704
    *supShift = superscriptShift(body, sup);
705
    *subShift = subscriptShift(body, sub, 0);
2 r 706
 
1839 ihaka 707
    temp1 = (*supShift - bboxDepth(superscriptBBox)) -
708
	(bboxHeight(subscriptBBox) - *subShift);
709
    if (temp1 < (4 * lineWidth()))
710
	*subShift = bboxHeight(subscriptBBox) -
711
	    *supShift +
712
	    bboxDepth(superscriptBBox) +
713
	    (4 * lineWidth());
2 r 714
 
1839 ihaka 715
    temp2 = (4 * xHeight() / 5) -
716
	(*supShift - bboxDepth(superscriptBBox));
717
    if (temp2 > 0) {
718
	*supShift = *supShift + temp2;
719
	*subShift = *subShift - temp2;
720
    }
2 r 721
}
722
 
723
static double accentVShift(SEXP body)
724
{
1839 ihaka 725
    double temp = xHeight();
726
    double bodyHeight = bboxHeight(elementBBox(body));
2 r 727
 
1839 ihaka 728
    if (bodyHeight > temp)
729
	return bodyHeight - temp;
730
    else
731
	return 0;
2 r 732
}
733
 
734
static double accentHShift(SEXP body, SEXP accent)
735
{
1839 ihaka 736
    return (bboxWidth(elementBBox(body)) -
737
	    bboxWidth(elementBBox(accent))) / 2;
2 r 738
}
739
 
740
static double numeratorVShift(SEXP num)
741
{
1839 ihaka 742
    BBOX numBBox;
743
    float cexSaved = mathDevice->gp.cex;
744
    double theShift, theClearance, minClearance;
2 r 745
 
1839 ihaka 746
    mathDevice->gp.cex = mathDevice->gp.cex * scriptScale;
747
    numBBox = elementBBox(num);
748
    mathDevice->gp.cex = cexSaved;
2 r 749
 
1839 ihaka 750
    theShift = numeratorShift();
751
    theClearance = numeratorShift() - bboxDepth(numBBox) -
752
	(axisHeight() + 0.5 * lineWidth());
753
    minClearance = 3 * lineWidth();
2 r 754
 
1839 ihaka 755
    if (theClearance < minClearance)
756
	theShift += minClearance - theClearance;
2 r 757
 
1839 ihaka 758
    return theShift;
2 r 759
}
760
 
761
/* RATIO */
762
static double denominatorVShift(SEXP denom)
763
{
1839 ihaka 764
    BBOX denomBBox;
765
    float cexSaved = mathDevice->gp.cex;
766
    double theShift, theClearance, minClearance;
2 r 767
 
768
#ifdef OLD
1839 ihaka 769
    mathDevice->gp.cex = mathDevice->gp.cex * scriptScale;
2 r 770
#else
1839 ihaka 771
    mathDevice->gp.cex = mathDevice->gp.cex * ratioScale;
2 r 772
#endif
1839 ihaka 773
    denomBBox = elementBBox(denom);
2 r 774
#ifdef OLD
1839 ihaka 775
    mathDevice->gp.cex = cexSaved;
2 r 776
#else
1839 ihaka 777
    mathDevice->gp.cex = cexSaved;
2 r 778
#endif
779
 
1839 ihaka 780
    theShift = denominatorShift();
781
    theClearance = axisHeight() - 0.5 * lineWidth() -
782
	(bboxHeight(denomBBox) - denominatorShift());
783
    minClearance = 3 * lineWidth();
2 r 784
 
1839 ihaka 785
    if (theClearance < minClearance)
786
	theShift += minClearance - theClearance;
2 r 787
 
1839 ihaka 788
    return theShift;
2 r 789
}
790
 
791
/* RATIO */
792
static double fractionWidth(SEXP num, SEXP denom)
793
{
1839 ihaka 794
    BBOX numBBox;
795
    BBOX denomBBox;
796
    float cexSaved = mathDevice->gp.cex;
797
    double temp1, temp2;
2 r 798
 
799
#ifdef OLD
1839 ihaka 800
    mathDevice->gp.cex = mathDevice->gp.cex * scriptScale;
2 r 801
#else
1839 ihaka 802
    mathDevice->gp.cex = mathDevice->gp.cex * ratioScale;
2 r 803
#endif
1839 ihaka 804
    numBBox = elementBBox(num);
805
    denomBBox = elementBBox(denom);
2 r 806
#ifdef OLD
1839 ihaka 807
    mathDevice->gp.cex = cexSaved;
2 r 808
#else
1839 ihaka 809
    mathDevice->gp.cex = cexSaved;
2 r 810
#endif
1839 ihaka 811
    temp1 = bboxWidth(numBBox);
812
    temp2 = bboxWidth(denomBBox);
2 r 813
 
1839 ihaka 814
    return max(temp1, temp2);
2 r 815
}
816
 
817
/* RATIO */
818
static void numdenomHShift(SEXP num, SEXP denom,
819
			   double *numShift, double *denomShift)
820
{
1839 ihaka 821
    BBOX numBBox;
822
    BBOX denomBBox;
823
    float cexSaved = mathDevice->gp.cex;
824
    double temp1, temp2;
2 r 825
 
826
#ifdef OLD
1839 ihaka 827
    mathDevice->gp.cex = mathDevice->gp.cex * scriptScale;
2 r 828
#else
1839 ihaka 829
    mathDevice->gp.cex = mathDevice->gp.cex * ratioScale;
2 r 830
#endif
1839 ihaka 831
    numBBox = elementBBox(num);
832
    denomBBox = elementBBox(denom);
2 r 833
#ifdef OLD
1839 ihaka 834
    mathDevice->gp.cex = cexSaved;
2 r 835
#else
1839 ihaka 836
    mathDevice->gp.cex = cexSaved;
2 r 837
#endif
1839 ihaka 838
    temp1 = bboxWidth(numBBox);
839
    temp2 = bboxWidth(denomBBox);
2 r 840
 
1839 ihaka 841
    if (temp1 > temp2) {
842
	*numShift = 0;
843
	*denomShift = (temp1 - temp2) / 2;
844
    }
845
    else {
846
	*numShift = (temp2 - temp1) / 2;
847
	*denomShift = 0;
848
    }
2 r 849
}
850
 
851
static int normalRadical(SEXP body)
852
{
1839 ihaka 853
    BBOX bodyBBox = elementBBox(body);
854
    BBOX radicalBBox = asciiBBox(radicalAscii());
2 r 855
 
1839 ihaka 856
    return ((bboxHeight(bodyBBox) + radicalDrop())
857
	    <= bboxHeight(radicalBBox)) &&
858
	(bboxDepth(bodyBBox) <= bboxDepth(radicalBBox));
2 r 859
}
860
 
861
static double radicalVShift(SEXP body)
862
{
1839 ihaka 863
    BBOX bodyBBox = elementBBox(body);
864
    return bboxHeight(bodyBBox) + radicalDrop();
2 r 865
}
866
 
867
 
868
static BBOX theOperatorBBox(SEXP operator);
869
 
870
static BBOX operatorLimitBBox(SEXP operator);
871
 
872
static double operatorLowerShift(SEXP operator, SEXP lower)
873
{
1839 ihaka 874
    BBOX opBBox = theOperatorBBox(operator);
875
    BBOX lowerBBox;
876
    double lowerHeight;
877
    double spacing2 = operatorSpace(1);
878
    double spacing4 = operatorSpace(3);
2 r 879
 
1839 ihaka 880
    lowerBBox = operatorLimitBBox(lower);
881
    lowerHeight = bboxHeight(lowerBBox);
882
    if (spacing2 > (spacing4 - lowerHeight))
883
	return bboxDepth(opBBox) + spacing2 + lowerHeight;
884
    else
885
	return bboxDepth(opBBox) + spacing4;
2 r 886
}
887
 
888
static double operatorUpperShift(SEXP operator, SEXP upper)
889
{
1839 ihaka 890
    BBOX opBBox = theOperatorBBox(operator);
891
    BBOX upperBBox;
892
    double upperDepth;
893
    double spacing1 = operatorSpace(0);
894
    double spacing3 = operatorSpace(2);
2 r 895
 
1839 ihaka 896
    upperBBox = operatorLimitBBox(upper);
897
    upperDepth = bboxDepth(upperBBox);
898
    if (spacing1 > (spacing3 - upperDepth))
899
	return bboxHeight(opBBox) + spacing1 + upperDepth;
900
    else
901
	return bboxHeight(opBBox) + spacing3;
2 r 902
}
903
 
904
static double operatorLowerHShift(SEXP operator, SEXP lower)
905
{
1839 ihaka 906
    BBOX opBBox = theOperatorBBox(operator);
907
    BBOX lowerBBox;
908
    double maxWidth = bboxWidth(opBBox);
2 r 909
 
1839 ihaka 910
    lowerBBox = operatorLimitBBox(lower);
911
    if (bboxWidth(lowerBBox) < maxWidth)
912
	return (maxWidth - bboxWidth(lowerBBox))/2;
913
    else
914
	return 0;
2 r 915
}
916
 
917
static double operatorHShift(SEXP operator, SEXP lower)
918
{
1839 ihaka 919
    BBOX opBBox = theOperatorBBox(operator);
920
    BBOX lowerBBox;
921
    double maxWidth = bboxWidth(opBBox);
2 r 922
 
1839 ihaka 923
    lowerBBox = operatorLimitBBox(lower);
924
    if (bboxWidth(lowerBBox) > maxWidth)
925
	return (bboxWidth(lowerBBox) - maxWidth)/2;
926
    else
927
	return 0;
2 r 928
}
929
 
930
static double operatorLowerHShiftAll(SEXP operator, SEXP lower, SEXP upper)
931
{
1839 ihaka 932
    BBOX opBBox = theOperatorBBox(operator);
933
    BBOX lowerBBox, upperBBox;
934
    double maxWidth = bboxWidth(opBBox);
2 r 935
 
1839 ihaka 936
    lowerBBox = operatorLimitBBox(lower);
937
    upperBBox = operatorLimitBBox(upper);
938
    maxWidth = max(maxWidth, max(bboxWidth(lowerBBox), bboxWidth(upperBBox)));
2 r 939
 
1839 ihaka 940
    if (bboxWidth(lowerBBox) < maxWidth)
941
	return (maxWidth - bboxWidth(lowerBBox))/2;
942
    else
943
	return 0;
2 r 944
}
945
 
946
static double operatorUpperHShiftAll(SEXP operator, SEXP lower, SEXP upper)
947
{
1839 ihaka 948
    BBOX opBBox = theOperatorBBox(operator);
949
    BBOX lowerBBox, upperBBox;
950
    double maxWidth = bboxWidth(opBBox);
2 r 951
 
1839 ihaka 952
    lowerBBox = operatorLimitBBox(lower);
953
    upperBBox = operatorLimitBBox(upper);
954
    maxWidth = max(maxWidth, max(bboxWidth(lowerBBox), bboxWidth(upperBBox)));
2 r 955
 
1839 ihaka 956
    if (bboxWidth(upperBBox) < maxWidth)
957
	return (maxWidth - bboxWidth(upperBBox))/2;
958
    else
959
	return 0;
2 r 960
}
961
 
962
static double operatorHShiftAll(SEXP operator, SEXP lower, SEXP upper)
963
{
1839 ihaka 964
    BBOX opBBox = theOperatorBBox(operator);
965
    BBOX lowerBBox, upperBBox;
966
    double maxWidth = bboxWidth(opBBox);
2 r 967
 
1839 ihaka 968
    lowerBBox = operatorLimitBBox(lower);
969
    upperBBox = operatorLimitBBox(upper);
970
    maxWidth = max(maxWidth, max(bboxWidth(lowerBBox), bboxWidth(upperBBox)));
2 r 971
 
1839 ihaka 972
    if (bboxWidth(opBBox) < maxWidth)
973
	return (maxWidth - bboxWidth(opBBox))/2;
974
    else
975
	return 0;
2 r 976
}
977
 
1839 ihaka 978
/* Code to generate bounding boxes and draw formulae. */
2 r 979
 
1839 ihaka 980
/* Bounding box basics. */
2 r 981
 
982
static BBOX makeBBox(double height, double depth, double width)
983
{
1839 ihaka 984
    BBOX bbox;
985
    bbox.height = height;
986
    bbox.depth = depth;
987
    bbox.width = width;
988
    return bbox;
2 r 989
}
990
 
991
static BBOX nullBBox()
992
{
1839 ihaka 993
    return makeBBox(0.0, 0.0, 0.0);
2 r 994
}
995
 
996
static BBOX makeBBoxFromChar(int chr)
997
{
1839 ihaka 998
    double height, depth, width;
999
    GMetricInfo(chr, &height, &depth, &width, metricUnit, mathDevice);
1000
    return makeBBox(height, depth, width);
2 r 1001
}
1002
 
1003
static BBOX shiftBBox(BBOX bbox, double shiftV)
1004
{
1839 ihaka 1005
    return makeBBox(bboxHeight(bbox) + shiftV,
1006
		    bboxDepth(bbox) - shiftV,
1007
		    bboxWidth(bbox));
2 r 1008
}
1009
 
1010
static BBOX combineBBoxes(BBOX bbox1, BBOX bbox2)
1011
{
1839 ihaka 1012
    return makeBBox(max(bboxHeight(bbox1), bboxHeight(bbox2)),
1013
		    max(bboxDepth(bbox1), bboxDepth(bbox2)),
1014
		    bboxWidth(bbox1) + bboxWidth(bbox2));
2 r 1015
}
1016
 
1017
static BBOX combineAlignedBBoxes(BBOX bbox1, BBOX bbox2)
1018
{
1839 ihaka 1019
    return makeBBox(max(bboxHeight(bbox1), bboxHeight(bbox2)),
1020
		    max(bboxDepth(bbox1), bboxDepth(bbox2)),
1021
		    max(bboxWidth(bbox1), bboxWidth(bbox2)));
2 r 1022
}
1023
 
1024
/* Drawing basics */
1025
 
1026
static double referenceX;
1027
static double referenceY;
1028
static double currentX;
1029
static double currentY;
1030
static double currentAngle;
1031
static double cosAngle;
1032
static double sinAngle;
1033
 
1839 ihaka 1034
/* Convert currentX and currentY from 0 angle */
1035
/* to and currentAngle */
1036
 
2 r 1037
static double convertedX()
1038
{
1839 ihaka 1039
    double rotatedX = referenceX +
2 r 1040
	(currentX - referenceX) * cosAngle -
1041
	(currentY - referenceY) * sinAngle;
1839 ihaka 1042
    return rotatedX;
2 r 1043
}
1044
 
1045
static double convertedY()
1046
{
1839 ihaka 1047
    double rotatedY = referenceY +
2 r 1048
	(currentY - referenceY) * cosAngle +
1049
	(currentX - referenceX) * sinAngle;
1839 ihaka 1050
    return rotatedY;
2 r 1051
}
1052
 
1053
static void moveAcross(double xamount)
1054
{
1839 ihaka 1055
    currentX += xamount;
2 r 1056
}
1057
 
1058
static void moveUp(double yamount)
1059
{
1839 ihaka 1060
    currentY += yamount;
2 r 1061
}
1062
 
1063
static void moveTo(double x, double y)
1064
{
1839 ihaka 1065
    currentX = x;
1066
    currentY = y;
2 r 1067
}
1068
 
1839 ihaka 1069
/* Code for ascii atoms. */
2 r 1070
 
1839 ihaka 1071
/* NOTE that I assume that all symbols which have */
1072
/* been converted to ascii are in the symbol font. */
2 r 1073
 
1074
static BBOX asciiBBox(int ascii)
1075
{
1839 ihaka 1076
    if ((ascii == hatAscii()) || (ascii == tildeAscii()))
1077
	mathDevice->gp.font = 1;
1078
    else
1079
	mathDevice->gp.font = 5;
1080
    return makeBBoxFromChar(ascii);
2 r 1081
}
1082
 
1083
static void drawAscii(int ascii)
1084
{
1839 ihaka 1085
    char asciiStr[2];
2 r 1086
 
1839 ihaka 1087
    if ((ascii == hatAscii()) || (ascii == tildeAscii()))
1088
	mathDevice->gp.font = 1;
1089
    else
1090
	mathDevice->gp.font = 5;
1091
    asciiStr[0] = ascii;
1092
    asciiStr[1] = '\0';
1093
    GText(convertedX(), convertedY(), INCHES, asciiStr,
1094
	  0.0, 0.0, currentAngle, mathDevice);
1095
    moveAcross(GStrWidth(asciiStr, metricUnit, mathDevice));
2 r 1096
}
1097
 
1839 ihaka 1098
/* Code for character atoms. */
2 r 1099
 
1100
static BBOX charBBox(char *str, SEXP expr)
1101
{
1839 ihaka 1102
    BBOX resultBBox = nullBBox();
1103
    int i;
2 r 1104
 
1839 ihaka 1105
    mathDevice->gp.font = atomFontFace(expr);
1106
    for (i = 0; i < strlen(str); i++)
1107
	resultBBox = combineBBoxes(resultBBox, makeBBoxFromChar(str[i]));
2 r 1108
 
1839 ihaka 1109
    return resultBBox;
2 r 1110
}
1111
 
1112
static void drawChar(char *str, SEXP expr)
1113
{
1839 ihaka 1114
    mathDevice->gp.font = atomFontFace(expr);
1115
    GText(convertedX(), convertedY(), INCHES, str,
1116
	  0.0, 0.0, currentAngle, mathDevice);
1117
    moveAcross(GStrWidth(str, metricUnit, mathDevice));
2 r 1118
}
1119
 
1120
/* code for symbol atoms */
1121
 
1122
static BBOX symbolBBox(SEXP expr)
1123
{
1926 ihaka 1124
    if (symbolSymbol(expr))
1125
	return asciiBBox(symbolAscii(expr));
1839 ihaka 1126
    else
1127
	return charBBox(CHAR(PRINTNAME(expr)), expr);
2 r 1128
}
1129
 
1130
static void drawSymbol(SEXP expr)
1131
{
1926 ihaka 1132
    if (symbolSymbol(expr))
1133
	drawAscii(symbolAscii(expr));
1839 ihaka 1134
    else
1135
	drawChar(CHAR(PRINTNAME(expr)), expr);
2 r 1136
}
1137
 
1138
/* code for numeric atoms */
1139
 
1140
static BBOX numberBBox(SEXP expr)
1141
{
1926 ihaka 1142
#ifdef OLD
1143
    int fontFace = getFont();
1144
    setFont(1);
1145
#endif
1839 ihaka 1146
    return charBBox(CHAR(asChar(expr)), expr);
1926 ihaka 1147
#ifdef OLD
1148
    setFont(fontFace);
1149
#endif
2 r 1150
}
1151
 
1152
static void drawNumber(SEXP expr)
1153
{
1926 ihaka 1154
#ifdef OLD
1155
    int fontFace = getFont();
1156
    setFont(1);
1157
#endif
1839 ihaka 1158
    drawChar(CHAR(asChar(expr)), expr);
1926 ihaka 1159
#ifdef OLD
1160
    setFont(fontFace);
1161
#endif
2 r 1162
}
1163
 
1164
/* code for string atoms */
1165
 
1166
static BBOX stringBBox(SEXP expr)
1167
{
1839 ihaka 1168
    return charBBox(CHAR(STRING(expr)[0]), expr);
2 r 1169
}
1170
 
1171
static void drawString(SEXP expr)
1172
{
1839 ihaka 1173
    drawChar(CHAR(STRING(expr)[0]), expr);
2 r 1174
}
1175
 
1176
/* code for atoms */
1177
 
1178
static BBOX atomBBox(SEXP expr)
1179
{
1839 ihaka 1180
    if (symbolAtom(expr))
1181
	return symbolBBox(expr);
1182
    else if (numberAtom(expr))
1183
	return numberBBox(expr);
1184
    else if (stringAtom(expr))
1185
	return stringBBox(expr);
2 r 1186
}
1187
 
1188
static void drawAtom(SEXP expr)
1189
{
1839 ihaka 1190
    if (symbolAtom(expr))
1191
	drawSymbol(expr);
1192
    else if (numberAtom(expr))
1193
	drawNumber(expr);
1194
    else if (stringAtom(expr))
1195
	drawString(expr);
2 r 1196
}
1197
 
1198
/* code for italic corrections */
1199
 
1200
static double half_pi = 1.57079632679489661922;
1201
 
1202
static double italicCorrection(SEXP expr)
1203
{
1839 ihaka 1204
    BBOX exprBBox = elementBBox(expr);
1205
    return bboxHeight(exprBBox) * tan(half_pi / 6);
2 r 1206
}
1207
 
1208
	/* correction within expression checks for current font italic */
1209
 
1210
static BBOX correctionWithinBBox(SEXP expr)
1211
{
1839 ihaka 1212
    if (isItalic() && !nonItalicExpression(expr))
1213
	return makeBBox(0, 0, italicCorrection(expr));
1214
    else
1215
	return nullBBox();
2 r 1216
}
1217
 
1218
static void drawCorrectionWithin(SEXP expr)
1219
{
1839 ihaka 1220
    if (isItalic() && !nonItalicExpression(expr))
1221
	moveAcross(italicCorrection(expr));
1016 maechler 1222
}
2 r 1223
 
1224
	/* correction between expressions checks current font and font */
1225
	/* of each expression */
1226
 
1227
static BBOX correctionBetweenBBox(SEXP expr1, SEXP expr2)
1228
{
1839 ihaka 1229
    if (((isItalic() && !nonItalicExpression(expr1)) ||
1230
	 italicExpression(expr1)) &&
1231
	((!isItalic() && !italicExpression(expr2)) ||
1232
	 nonItalicExpression(expr2)))
1233
	return makeBBox(0, 0, italicCorrection(expr1));
1234
    else
1235
	return nullBBox();
2 r 1236
}
1237
 
1238
static void drawCorrectionBetween(SEXP expr1, SEXP expr2)
1239
{
1839 ihaka 1240
    if (((isItalic() && !nonItalicExpression(expr1)) ||
1241
	 italicExpression(expr1)) &&
1242
	((!isItalic() && !italicExpression(expr2)) ||
1243
	 nonItalicExpression(expr2)))
1244
	moveAcross(italicCorrection(expr1));
2 r 1245
}
1246
 
1247
/* code for gaps */
1248
 
1249
static int cexGap = 1;
1250
 
1251
static void setGapCEX()
1252
{
581 paul 1253
  cexGap = mathDevice->gp.cex;
2 r 1254
}
1255
 
1256
static BBOX gapBBox(double gap)
1257
{
1839 ihaka 1258
    double cexSaved = mathDevice->gp.cex;
1259
    BBOX theBBox;
2 r 1260
 
1839 ihaka 1261
    mathDevice->gp.cex = cexGap;
1262
    theBBox = makeBBox(0, 0, gap * fontHeight());
1263
    mathDevice->gp.cex = cexSaved;
2 r 1264
 
1839 ihaka 1265
    return theBBox;
2 r 1266
}
1267
 
1268
static BBOX smallgapBBox(double gap)
1269
{
1839 ihaka 1270
    double cexSaved = mathDevice->gp.cex;
1271
    BBOX theBBox;
2 r 1272
 
1839 ihaka 1273
    mathDevice->gp.cex = cexGap;
1274
    theBBox = makeBBox(0, 0, 0.5 * gap * fontHeight());
1275
    mathDevice->gp.cex = cexSaved;
2 r 1276
 
1839 ihaka 1277
    return theBBox;
2 r 1278
}
1279
 
1280
static void drawGap(double gap)
1281
{
1839 ihaka 1282
    double cexSaved = mathDevice->gp.cex;
2 r 1283
 
1839 ihaka 1284
    mathDevice->gp.cex = cexGap;
1285
    moveAcross(gap * fontHeight());
1286
    mathDevice->gp.cex = cexSaved;
2 r 1287
}
1288
 
1289
static void drawSmallGap(double gap)
1290
{
1839 ihaka 1291
    double cexSaved = mathDevice->gp.cex;
2 r 1292
 
1839 ihaka 1293
    mathDevice->gp.cex = cexGap;
1294
    moveAcross(0.5 * gap * fontHeight());
1295
    mathDevice->gp.cex = cexSaved;
2 r 1296
}
1297
 
1839 ihaka 1298
/* Code for binary operator (+, -, *, /) expressions */
2 r 1299
 
1839 ihaka 1300
/* NOTE that gaps are specified as proportions */
1301
/* of the current font height */
2 r 1302
 
1303
static double binGapBefore(SEXP beforeOperand)
1304
{
1839 ihaka 1305
    return 0.2;
2 r 1306
}
1307
 
1308
static double binGapBetween(SEXP operand1, SEXP operand2)
1309
{
1310
    return 0;
1311
}
1312
 
1313
static double binGapAfter(SEXP afterOperand)
1314
{
1315
    return 0.2;
1316
}
1317
 
1926 ihaka 1318
static BBOX spaceBBox(SEXP expr)
1319
{
1320
    SEXP operator, operand1, operand2;
1321
    BBOX middleBBox;
1322
 
1323
    operator = CAR(expr);
1324
    operand1 = CADR(expr);
1325
    setGapCEX();
1326
    if(length(expr) == 3) {
1327
	operand2 = CADDR(expr);
1328
	middleBBox = asciiBBox(' ');
1329
	return combineBBoxes(elementBBox(operand1),
1330
			     combineBBoxes(middleBBox,
1331
					   elementBBox(operand2)));
1332
    }
1333
    else error("invalid formula\n");
1334
}
1335
 
1336
static void drawSpace(SEXP expr)
1337
{
1338
    SEXP operator, operand1, operand2;
1339
 
1340
    operator = CAR(expr);
1341
    operand1 = CADR(expr);
1342
    setGapCEX();
1343
    operand2 = CADDR(expr);
1344
    drawElement(operand1);
1345
    drawAscii(' ');
1346
    drawElement(operand2);
1347
}
1348
 
2 r 1349
static BBOX binBBox(SEXP expr)
1350
{
1839 ihaka 1351
    SEXP operator, operand1, operand2;
1352
    BBOX middleBBox;
2 r 1353
 
1839 ihaka 1354
    operator = CAR(expr);
1355
    operand1 = CADR(expr);
1356
    setGapCEX();
1357
    if(length(expr) == 3) {
1358
	operand2 = CADDR(expr);
1359
	if (multiplicationOperator(operator))
1360
	    middleBBox =
1361
		correctionBetweenBBox(operand1, operand2);
1362
	else
1363
	    middleBBox =
1364
		combineBBoxes(
1365
			      gapBBox(binGapBefore(operand1)),
1366
			      combineBBoxes(atomBBox(operator),
1367
					    gapBBox(binGapAfter(operand2))));
2 r 1368
 
1839 ihaka 1369
	return combineBBoxes(elementBBox(operand1),
1370
			     combineBBoxes(middleBBox,
1371
					   elementBBox(operand2)));
1372
    }
1373
    else if(length(expr) == 2) {
1374
	middleBBox = combineBBoxes(atomBBox(operator),
2 r 1375
				   smallgapBBox(binGapAfter(operand1)));
1839 ihaka 1376
	return combineBBoxes(middleBBox, elementBBox(operand1));
1377
    }
1378
    else error("invalid formula\n");
2 r 1379
}
1380
 
1381
static void drawBin(SEXP expr)
1382
{
1839 ihaka 1383
    SEXP operator, operand1, operand2;
2 r 1384
 
1839 ihaka 1385
    operator = CAR(expr);
1386
    operand1 = CADR(expr);
1387
    setGapCEX();
1388
    if(length(expr) == 3) {
1389
	operand2 = CADDR(expr);
1390
	drawElement(operand1);
1391
	if (multiplicationOperator(operator))
1392
	    drawGap(binGapBetween(operand1, operand2));
2 r 1393
	else {
1839 ihaka 1394
	    drawGap(binGapBefore(operand1));
1395
	    drawAtom(operator);
1396
	    drawGap(binGapAfter(operand2));
2 r 1397
	}
1839 ihaka 1398
	drawElement(operand2);
1399
    }
1400
    else {
1401
	drawAtom(operator);
1402
	drawSmallGap(binGapAfter(operand1));
1403
	drawElement(operand1);
1404
    }
2 r 1405
}
1406
 
1839 ihaka 1407
/* Code for superscript and subscript expressions */
2 r 1408
 
1409
static BBOX supsubBBox(SEXP body, SEXP superscript, SEXP subscript);
1410
 
1411
static BBOX superscriptBBox(SEXP superscript)
1412
{
1839 ihaka 1413
    BBOX result;
1414
    float cexSaved = mathDevice->gp.cex;
2 r 1415
 
1839 ihaka 1416
    mathDevice->gp.cex = mathDevice->gp.cex * scriptScale;
1417
    result = elementBBox(superscript);
1418
    mathDevice->gp.cex = cexSaved;
2 r 1419
 
1839 ihaka 1420
    return result;
2 r 1421
}
1422
 
1423
static BBOX supBBox(SEXP expr)
1424
{
1839 ihaka 1425
    SEXP body = CADR(expr);
1426
    SEXP superscript = CADDR(expr);
1427
    int supsub = 0;
2 r 1428
 
1839 ihaka 1429
    if (formulaExpression(body))
1430
	if (subAtom(CAR(body)))
1431
	    supsub = 1;
2 r 1432
 
1839 ihaka 1433
    if (supsub)
1434
	return supsubBBox(CADR(body), superscript, (CADDR(body)));
1435
    else
1436
	return combineBBoxes(elementBBox(body),
1437
			     combineBBoxes(correctionWithinBBox(body),
1438
					   shiftBBox(superscriptBBox(superscript),
1439
						     superscriptShift(body, superscript))));
2 r 1440
}
1441
 
1442
static BBOX subscriptBBox(SEXP subscript)
1443
{
1839 ihaka 1444
    BBOX result;
1445
    float cexSaved = mathDevice->gp.cex;
2 r 1446
 
1839 ihaka 1447
    mathDevice->gp.cex = mathDevice->gp.cex * scriptScale;
1448
    result = elementBBox(subscript);
1449
    mathDevice->gp.cex = cexSaved;
2 r 1450
 
1839 ihaka 1451
    return result;
2 r 1452
}
1453
 
1454
static BBOX subBBox(SEXP expr)
1455
{
1839 ihaka 1456
    SEXP body = CADR(expr);
1457
    SEXP subscript = CADDR(expr);
2 r 1458
 
1839 ihaka 1459
    return combineBBoxes(elementBBox(body),
1460
			 shiftBBox(subscriptBBox(subscript),
1461
				   subscriptShift(body, subscript, 1)));
2 r 1462
}
1463
 
1464
static BBOX supsubBBox(SEXP body, SEXP superscript, SEXP subscript)
1465
{
1839 ihaka 1466
    double supShift, subShift;
1467
    supsubShift(body, superscript, subscript, &supShift, &subShift);
2 r 1468
 
1839 ihaka 1469
    return combineBBoxes(elementBBox(body),
1470
	       combineAlignedBBoxes(
1471
	           combineBBoxes(correctionWithinBBox(body),
1472
			    shiftBBox(superscriptBBox(superscript), supShift)),
2 r 1473
		   shiftBBox(subscriptBBox(subscript), subShift)));
1474
}
1475
 
1476
static void drawScriptElement(SEXP expr)
1477
{
1839 ihaka 1478
    float cexSaved = mathDevice->gp.cex;
2 r 1479
 
1839 ihaka 1480
    mathDevice->gp.cex = mathDevice->gp.cex * scriptScale;
1481
    drawElement(expr);
1482
    mathDevice->gp.cex = cexSaved;
2 r 1483
}
1484
 
1485
static void drawSupSub(SEXP body, SEXP superscript, SEXP subscript);
1486
 
1487
static void drawSuper(SEXP expr)
1488
{
1839 ihaka 1489
    SEXP body = CADR(expr);
1490
    SEXP superscript = CADDR(expr);
1491
    double supShift = superscriptShift(body, superscript);
1492
    int supsub = 0;
2 r 1493
 
1839 ihaka 1494
    if (formulaExpression(body))
1495
	if (subAtom(CAR(body)))
1496
	    supsub = 1;
2 r 1497
 
1839 ihaka 1498
    if (supsub)
1499
	drawSupSub(CADR(body), superscript, CADDR(body));
1500
    else {
1501
	drawElement(body);
1502
	drawCorrectionWithin(body);
1503
	moveUp(supShift);
1504
	drawScriptElement(superscript);
1505
	moveUp(-supShift);
1506
    }
2 r 1507
}
1508
 
1509
static void drawSub(SEXP expr)
1510
{
1839 ihaka 1511
    SEXP body = CADR(expr);
1512
    SEXP subscript = CADDR(expr);
1513
    double subShift = subscriptShift(body, subscript, 1);
2 r 1514
 
1839 ihaka 1515
    drawElement(body);
1516
    moveUp(-subShift);
1517
    drawScriptElement(subscript);
1518
    moveUp(subShift);
2 r 1519
}
1520
 
1521
static void drawSupSub(SEXP body, SEXP superscript, SEXP subscript)
1522
{
1839 ihaka 1523
    double supShift, subShift;
1524
    double savedX, savedY;
1525
    BBOX supBBox = elementBBox(superscript);
1526
    BBOX subBBox = elementBBox(subscript);
2 r 1527
 
1839 ihaka 1528
    supsubShift(body, superscript, subscript, &supShift, &subShift);
1529
    drawElement(body);
1530
    savedX = currentX;
1531
    savedY = currentY;
1532
    drawCorrectionWithin(body);
1533
    moveUp(supShift);
1534
    drawScriptElement(superscript);
1535
    moveTo(savedX, savedY);
1536
    moveUp(-subShift);
1537
    drawScriptElement(subscript);
1538
    moveTo(savedX, savedY);
1539
    moveAcross(max(bboxWidth(supBBox), bboxWidth(subBBox)));
2 r 1540
}
1541
 
1542
/* code for accented expressions (hat, bar, ...) */
1543
 
1544
static BBOX hatBBox(SEXP body)
1545
{
1839 ihaka 1546
    BBOX bodyBBox = elementBBox(body);
1547
    return combineAlignedBBoxes(bodyBBox,
1548
				makeBBox(bboxHeight(bodyBBox) +
1549
					 customAccentGap() +
1550
					 customHatHeight(), 0, 0));
2 r 1551
}
1552
 
1553
static BBOX barBBox(SEXP body)
1554
{
1839 ihaka 1555
    BBOX bodyBBox = elementBBox(body);
1556
    return combineAlignedBBoxes(bodyBBox,
1557
				makeBBox(bboxHeight(bodyBBox) +
1558
					 customAccentGap(), 0, 0));
2 r 1559
}
1560
 
1561
static BBOX accentBBox(SEXP expr)
1562
{
1839 ihaka 1563
    SEXP accent = CAR(expr);
1564
    SEXP body = CADR(expr);
2 r 1565
 
1839 ihaka 1566
    if (hatAtom(accent))
1567
	return hatBBox(body);
1568
    else if (barAtom(accent))
1569
	return barBBox(body);
1570
    else
1571
	return combineAlignedBBoxes(elementBBox(body),
1572
		    combineBBoxes(makeBBox(accentHShift(body, accent), 0, 0),
1573
				  shiftBBox(asciiBBox(accentAscii(accent)),
1574
					    accentVShift(body))));
2 r 1575
}
1576
 
1577
static void drawHat(SEXP body)
1578
{
1839 ihaka 1579
    BBOX bodyBBox = elementBBox(body);
1580
    double width = bboxWidth(bodyBBox);
1581
    double savedX = currentX;
1582
    double savedY = currentY;
1583
    double x[3], y[3];
2 r 1584
 
1839 ihaka 1585
    moveUp(bboxHeight(bodyBBox) + customAccentGap());
1586
    x[0] = convertedX(); y[0] = convertedY();
1587
    moveUp(customHatHeight());
1588
    moveAcross(width / 2);
1589
    x[1] = convertedX(); y[1] = convertedY();
1590
    moveUp(-customHatHeight());
1591
    moveAcross(width / 2);
1592
    x[2] = convertedX(); y[2] = convertedY();
1593
    GPolyline(3, x, y, INCHES, mathDevice);
1594
    moveTo(savedX, savedY);
1595
    drawElement(body);
2 r 1596
}
1597
 
1598
static void drawBar(SEXP body)
1599
{
1839 ihaka 1600
    BBOX bodyBBox = elementBBox(body);
1601
    double savedX = currentX;
1602
    double savedY = currentY;
1603
    double x[2], y[2];
2 r 1604
 
1839 ihaka 1605
    moveUp(bboxHeight(bodyBBox) + customAccentGap());
1606
    x[0] = convertedX(); y[0] = convertedY();
1607
    moveAcross(bboxWidth(bodyBBox));
1608
    x[1] = convertedX(); y[1] = convertedY();
1609
    GPolyline(2, x, y, INCHES, mathDevice);
1610
    moveTo(savedX, savedY);
1611
    drawElement(body);
2 r 1612
}
1613
 
1614
static void drawAccent(SEXP expr)
1615
{
1839 ihaka 1616
    SEXP accent = CAR(expr);
1617
    SEXP body = CADR(expr);
1618
    double savedX = currentX;
1619
    double savedY = currentY;
2 r 1620
 
1839 ihaka 1621
    if (hatAtom(accent))
1622
	drawHat(body);
1623
    else if (barAtom(accent))
1624
	drawBar(body);
1625
    else {
1626
	moveAcross(accentHShift(body, accent));
1627
	moveUp(accentVShift(body));
1628
	drawAscii(accentAscii(accent));
1629
	moveTo(savedX, savedY);
1630
	drawElement(body);
1631
    }
2 r 1632
}
1633
 
1839 ihaka 1634
/* Code for fraction expressions (over) */
2 r 1635
 
1636
static BBOX fractionBBox(SEXP expr)
1637
{
1839 ihaka 1638
    SEXP numerator = CADR(expr);
1639
    SEXP denominator = CADDR(expr);
1640
    BBOX numBBox, denomBBox;
1641
    double numHShift, denomHShift;
1642
    float cexSaved = mathDevice->gp.cex;
2 r 1643
 
1644
#ifdef OLD
1839 ihaka 1645
    mathDevice->gp.cex = mathDevice->gp.cex * scriptScale;
2 r 1646
#else
1839 ihaka 1647
    mathDevice->gp.cex = mathDevice->gp.cex * ratioScale;
2 r 1648
#endif
1839 ihaka 1649
    numBBox = elementBBox(numerator);
1650
    denomBBox = elementBBox(denominator);
2 r 1651
#ifdef OLD
1839 ihaka 1652
    mathDevice->gp.cex = cexSaved;
2 r 1653
#else
1839 ihaka 1654
    mathDevice->gp.cex = cexSaved;
2 r 1655
#endif
1839 ihaka 1656
    numdenomHShift(numerator, denominator, &numHShift, &denomHShift);
2 r 1657
 
1839 ihaka 1658
    return combineAlignedBBoxes(
1659
	      shiftBBox(combineBBoxes(makeBBox(numHShift, 0, 0), numBBox),
1660
			numeratorVShift(numerator)),
1661
	      shiftBBox(combineBBoxes(makeBBox(denomHShift, 0, 0), denomBBox),
1662
			-denominatorVShift(denominator)));
2 r 1663
}
1664
 
1665
static void drawRatioElement(SEXP expr)
1666
{
1839 ihaka 1667
    float cexSaved = mathDevice->gp.cex;
1668
    mathDevice->gp.cex = mathDevice->gp.cex * ratioScale;
1669
    drawElement(expr);
1670
    mathDevice->gp.cex = cexSaved;
2 r 1671
}
1672
 
1673
static void drawFraction(SEXP expr)
1674
{
1839 ihaka 1675
    SEXP numerator = CADR(expr);
1676
    SEXP denominator = CADDR(expr);
1677
    double savedX = currentX;
1678
    double savedY = currentY;
1679
    double fWidth = fractionWidth(numerator, denominator);
1680
    double numHShift, denomHShift;
1681
    double x[2], y[2];
2 r 1682
 
1839 ihaka 1683
    numdenomHShift(numerator, denominator, &numHShift, &denomHShift);
1684
    moveAcross(numHShift);
1685
    moveUp(numeratorVShift(numerator));
2 r 1686
#ifdef OLD
1839 ihaka 1687
    drawScriptElement(numerator);
2 r 1688
#else
1839 ihaka 1689
    drawRatioElement(numerator);
2 r 1690
#endif
1839 ihaka 1691
    moveTo(savedX, savedY);
1692
    moveUp(axisHeight());
1693
    x[0] = convertedX(); y[0] = convertedY();
1694
    moveAcross(fWidth);
1695
    x[1] = convertedX(); y[1] = convertedY();
1696
    GPolyline(2, x, y, INCHES, mathDevice);
1697
    moveTo(savedX, savedY);
1698
    moveAcross(denomHShift);
1699
    moveUp(-denominatorVShift(denominator));
2 r 1700
#ifdef OLD
1839 ihaka 1701
    drawScriptElement(denominator);
2 r 1702
#else
1839 ihaka 1703
    drawRatioElement(denominator);
2 r 1704
#endif
1839 ihaka 1705
    moveTo(savedX + fWidth, savedY);
2 r 1706
}
1707
 
1708
/* code for group expressions (expressions within parentheses) */
1709
 
1710
static BBOX groupBBox(SEXP expr)
1711
{
1839 ihaka 1712
    return combineBBoxes(
1713
	      asciiBBox(groupOpenAscii()),
1714
	      combineBBoxes(elementBBox(CADR(expr)),
1715
			    combineBBoxes(correctionWithinBBox(CADR(expr)),
1716
					  asciiBBox(groupCloseAscii()))));
2 r 1717
}
1718
 
1719
static void drawGroup(SEXP expr)
1720
{
1839 ihaka 1721
    drawAscii(groupOpenAscii());
1722
    drawElement(CADR(expr));
1723
    drawCorrectionWithin(CADR(expr));
1724
    drawAscii(groupCloseAscii());
2 r 1725
}
1726
 
1727
/* code for operator expressions (sum, product, integral) */
1728
 
1729
/* NOTE that gaps are specified as proportions of the current font height */
1730
 
1731
static double operatorGap(SEXP body)
1732
{
1733
    return 0.1;
1734
}
1735
 
1736
static double integralTopShift() { return 0.5 * fontHeight(); }
1737
 
1738
static double integralBottomShift() { return -0.5 * fontHeight(); }
1739
 
1740
static BBOX theOperatorBBox(SEXP operator)
1741
{
1839 ihaka 1742
    if (integralOperator(operator))
1743
	return combineAlignedBBoxes(
1744
		  shiftBBox(asciiBBox(integralAscii(1)), integralTopShift()),
1745
		  combineAlignedBBoxes(asciiBBox(integralAscii(2)),
1746
				       shiftBBox(asciiBBox(integralAscii(3)),
1747
						 integralBottomShift())));
1748
    else
1749
	return asciiBBox(operatorAscii(operator));
2 r 1750
}
1751
 
1009 bates 1752
static int useRelGap = 1;
1016 maechler 1753
 
2 r 1754
static BBOX operatorLimitBBox(SEXP limit)
1755
{
1839 ihaka 1756
    float cexSaved = mathDevice->gp.cex;
1757
    BBOX limitBBox;
2 r 1758
 
1839 ihaka 1759
    mathDevice->gp.cex = mathDevice->gp.cex * scriptScale;
1760
    useRelGap = 0;
1761
    limitBBox = elementBBox(limit);
1762
    useRelGap = 1;
1763
    mathDevice->gp.cex = cexSaved;
2 r 1764
 
1839 ihaka 1765
    return limitBBox;
2 r 1766
}
1767
 
1768
static BBOX operatorBBox(SEXP expr)
1769
{
1839 ihaka 1770
    SEXP operator = CAR(expr);
1771
    SEXP body, lower, upper;
1772
    BBOX opBBox = theOperatorBBox(operator);
1773
    BBOX bodyBBox, lowerBBox, upperBBox;
1774
    setGapCEX();
2 r 1775
 
1839 ihaka 1776
    if (length(expr) > 1) {
1777
	body = CADR(expr);
1778
	bodyBBox = combineBBoxes(
1779
		      opBBox,
1780
		      combineBBoxes(gapBBox(operatorGap(body)),
1781
				    elementBBox(body)));
1016 maechler 1782
 
1839 ihaka 1783
	if (length(expr) > 2) {
1784
	    lower = CADDR(expr);
1785
	    lowerBBox = operatorLimitBBox(lower);
2 r 1786
 
1839 ihaka 1787
	    if (length(expr) > 3) {
1788
		upper = CADDDR(expr);
1789
		upperBBox = operatorLimitBBox(upper);
2 r 1790
 
1839 ihaka 1791
		return combineAlignedBBoxes(
1792
			 combineBBoxes(
1793
			   makeBBox(
1794
			     operatorHShiftAll(operator,lower, upper),
1795
			     0, 0),
1796
			   bodyBBox),
1797
			 combineAlignedBBoxes(
1798
			   shiftBBox(
1799
                             combineBBoxes(
1800
			       makeBBox(
1801
                                 operatorUpperHShiftAll(
1802
				   operator,lower, upper),
1803
				 0, 0),
1804
			       upperBBox),
1805
			     operatorUpperShift(operator, upper)),
1806
			   shiftBBox(
1807
                             combineBBoxes(
1808
			       makeBBox(
1809
				 operatorLowerHShiftAll(
1810
				   operator, lower, upper),
1811
				 0, 0),
1812
			       lowerBBox),
1813
			     operatorLowerShift(operator, lower))));
1814
	    }
1815
	    else
1816
		return combineAlignedBBoxes(
1817
			 combineBBoxes(
1818
			   makeBBox(
1819
			     operatorHShift(operator, lower), 0, 0),
1820
			   bodyBBox),
1821
			 shiftBBox(
1822
			   combineBBoxes(
1823
			     makeBBox(operatorLowerHShift(operator, lower),
1824
				      0, 0),
1825
			     lowerBBox),
1826
			   operatorLowerShift(operator, lower)));
1827
	}
1828
	else
1829
	    return bodyBBox;
2 r 1830
    }
1831
    else
1839 ihaka 1832
	error("Invalid Formula\n");
2 r 1833
}
1834
 
1835
static void drawTheOperator(SEXP operator)
1836
{
1839 ihaka 1837
    if (integralOperator(operator)) {
1838
	double savedX = currentX;
1839
	double savedY = currentY;
1840
	moveUp(integralTopShift());
1841
	drawAscii(integralAscii(1));
1842
	moveTo(savedX, savedY);
1843
	moveUp(integralBottomShift());
1844
	drawAscii(integralAscii(3));
1845
	moveTo(savedX, savedY);
1846
	drawAscii(integralAscii(2));
1847
    }
1848
    else
1849
	drawAscii(operatorAscii(operator));
2 r 1850
}
1016 maechler 1851
 
2 r 1852
static void drawOperatorLimit(SEXP limit)
1853
{
1839 ihaka 1854
    useRelGap = 0;
1855
    drawScriptElement(limit);
1856
    useRelGap = 1;
2 r 1857
}
1858
 
1859
static void drawOperator(SEXP expr)
1860
{
1839 ihaka 1861
    SEXP operator = CAR(expr);
1862
    SEXP body = CADR(expr);
1863
    SEXP lower, upper;
1864
    double savedX = currentX;
1865
    double savedY = currentY;
2 r 1866
 
1839 ihaka 1867
    setGapCEX();
1016 maechler 1868
 
1839 ihaka 1869
    if (length(expr) > 2) {
1870
	lower = CADDR(expr);
2 r 1871
 
1839 ihaka 1872
	if (length(expr) > 3) {
1873
	    upper = CADDDR(expr);
1874
	    moveUp(operatorUpperShift(operator, upper));
1875
	    moveAcross(operatorUpperHShiftAll(operator, lower, upper));
1876
	    drawOperatorLimit(upper);
1877
	    moveTo(savedX, savedY);
1878
	    moveUp(-operatorLowerShift(operator, lower));
1879
	    moveAcross(operatorLowerHShiftAll(operator, lower, upper));
1880
	    drawOperatorLimit(lower);
1881
	    moveTo(savedX, savedY);
1882
	    moveAcross(operatorHShiftAll(operator, lower, upper));
1883
	}
1884
	else{
1885
	    moveUp(-operatorLowerShift(operator, lower));
1886
	    moveAcross(operatorLowerHShift(operator, lower));
1887
	    drawOperatorLimit(lower);
1888
	    moveTo(savedX, savedY);
1889
	    moveAcross(operatorHShift(operator, lower));
1890
	}
2 r 1891
    }
1892
 
1839 ihaka 1893
    drawTheOperator(operator);
1894
    drawGap(operatorGap(body));
1895
    drawElement(body);
2 r 1896
}
1897
 
1839 ihaka 1898
/* Code for radical expressions (root) */
2 r 1899
 
1900
static BBOX customRadicalBBox(SEXP body)
1901
{
1839 ihaka 1902
    BBOX bodyBBox = elementBBox(body);
1903
    return combineBBoxes(makeBBox(bboxHeight(bodyBBox) + customRadicalGap(),
1904
				  0, customRadicalWidth()),
1905
			 combineBBoxes(makeBBox(0, 0, customRadicalSpace()),
1906
				       bodyBBox));
2 r 1907
}
1908
 
1909
static BBOX radicalBBox(SEXP expr)
1910
{
1839 ihaka 1911
    SEXP body = CADR(expr);
1912
    return customRadicalBBox(body);
2 r 1913
}
1914
 
1915
static void drawCustomRadical(SEXP body)
1916
{
1839 ihaka 1917
    BBOX bodyBBox = elementBBox(body);
1918
    double height = bboxHeight(bodyBBox);
1919
    double depth = bboxDepth(bodyBBox);
1920
    double width = bboxWidth(bodyBBox);
1921
    double twiddleHeight = (height - depth) / 2;
1922
    double savedX = currentX;
1923
    double savedY = currentY;
1924
    double x[5], y[5];
2 r 1925
 
1839 ihaka 1926
    moveUp(0.8 * twiddleHeight);
1927
    x[0] = convertedX(); y[0] = convertedY();
1928
    moveUp(0.2 * twiddleHeight);
1929
    moveAcross(0.3 * customRadicalWidth());
1930
    x[1] = convertedX(); y[1] = convertedY();
1931
    moveUp(-(twiddleHeight + depth));
1932
    moveAcross(0.3 * customRadicalWidth());
1933
    x[2] = convertedX(); y[2] = convertedY();
1934
    moveUp(depth + height + customRadicalGap());
1935
    moveAcross(0.4 * customRadicalWidth());
1936
    x[3] = convertedX(); y[3] = convertedY();
1937
    moveAcross(customRadicalSpace() + width);
1938
    x[4] = convertedX(); y[4] = convertedY();
1939
    GPolyline(5, x, y, INCHES, mathDevice);
1940
    moveTo(savedX, savedY);
1941
    moveAcross(customRadicalWidth() + customRadicalSpace());
1942
    drawElement(body);
2 r 1943
}
1944
 
1945
static void drawRadical(SEXP expr)
1946
{
1839 ihaka 1947
    SEXP body = CADR(expr);
1948
    drawCustomRadical(body);
2 r 1949
}
1950
 
1839 ihaka 1951
/* Code for absolute expressions (abs). */
2 r 1952
 
1953
static BBOX absBBox(SEXP expr)
1954
{
1839 ihaka 1955
    SEXP body = CADR(expr);
1956
    return combineBBoxes(makeBBox(0, 0, absSpace()),
1957
			 combineBBoxes(elementBBox(body),
1958
				       makeBBox(0, 0, absSpace())));
2 r 1959
}
1960
 
1961
static void drawAbs(SEXP expr)
1962
{
1839 ihaka 1963
    SEXP body = CADR(expr);
1964
    BBOX bodyBBox = elementBBox(expr);
1965
    double height = bboxHeight(bodyBBox);
1966
    double depth = bboxDepth(bodyBBox);
1967
    double x[2], y[2];
2 r 1968
 
1839 ihaka 1969
    moveUp(-depth);
1970
    x[0] = convertedX(); y[0] = convertedY();
1971
    moveUp(depth + height);
1972
    x[1] = convertedX(); y[1] = convertedY();
1973
    GPolyline(2, x, y, INCHES, mathDevice);
1974
    moveUp(-height);
1975
    moveAcross(absSpace());
1976
    drawElement(body);
1977
    moveAcross(absSpace());
1978
    moveUp(-depth);
1979
    x[0] = convertedX(); y[0] = convertedY();
1980
    moveUp(depth + height);
1981
    x[1] = convertedX(); y[1] = convertedY();
1982
    GPolyline(2, x, y, INCHES, mathDevice);
1983
    moveUp(-height);
2 r 1984
}
1985
 
1839 ihaka 1986
/* Code for general expressions with no special meaning */
1987
/* in mathematical notation syntax (e.g., f(x)) */
2 r 1988
 
1989
static BBOX expressionBBox(SEXP expr)
1990
{
1839 ihaka 1991
    int i;
1992
    int numParams = length(expr) - 1;
1993
    BBOX resultBBox = elementBBox(CAR(expr));
1994
    SEXP lastTerm;
2 r 1995
 
1839 ihaka 1996
    lastTerm = CAR(expr);
1997
    expr = CDR(expr);
1998
    resultBBox = combineBBoxes(resultBBox, asciiBBox(groupOpenAscii()));
1999
    for (i = 0; i < numParams; i++) {
2000
	resultBBox = combineBBoxes(resultBBox, elementBBox(CAR(expr)));
2 r 2001
	lastTerm = CAR(expr);
2002
	expr = CDR(expr);
1839 ihaka 2003
	if (i < numParams - 1)
2004
	    resultBBox = combineBBoxes(resultBBox,
2005
				       combineBBoxes(asciiBBox(commaAscii()),
2006
						     asciiBBox(spaceAscii())));
2007
    }
2008
    return combineBBoxes(resultBBox,
2009
			 combineBBoxes(correctionWithinBBox(lastTerm),
2010
				       asciiBBox(groupCloseAscii())));
2 r 2011
}
2012
 
2013
static void drawExpression(SEXP expr)
2014
{
1839 ihaka 2015
    int i;
2016
    int numParams = length(expr) - 1;
2017
    SEXP lastTerm;
2 r 2018
 
1839 ihaka 2019
    drawElement(CAR(expr));
2020
    lastTerm = CAR(expr);
2021
    expr = CDR(expr);
2022
    drawAscii(groupOpenAscii());
2023
    for (i = 0; i < numParams; i++) {
2 r 2024
	drawElement(CAR(expr));
2025
	lastTerm = CAR(expr);
2026
	expr = CDR(expr);
1839 ihaka 2027
	if (i < numParams - 1) {
2028
	    drawAscii(commaAscii());
2029
	    drawAscii(spaceAscii());
2 r 2030
	}
1839 ihaka 2031
    }
2032
    drawCorrectionWithin(lastTerm);
2033
    drawAscii(groupCloseAscii());
2 r 2034
}
2035
 
1839 ihaka 2036
/* Code for curly expressions (i.e., { ... } ) */
2 r 2037
 
2038
static BBOX curlyBBox(SEXP expr)
2039
{
1839 ihaka 2040
    return expressionBBox(CADR(expr));
2 r 2041
}
2042
 
2043
static void drawFormula(SEXP);
2044
 
526 maechler 2045
static void drawCurly(SEXP expr)
2 r 2046
{
1839 ihaka 2047
    drawFormula(CADR(expr));
2 r 2048
}
2049
 
2050
/* code for relation expressions (i.e. ... == ...) */
2051
 
2052
static double relGap()
2053
{
1839 ihaka 2054
    if (useRelGap)
2055
	return 0.3;
2056
    else
2057
	return 0.1;
2 r 2058
}
2059
 
2060
static BBOX relBBox(SEXP expr)
2061
{
1926 ihaka 2062
    SEXP op = CAR(expr);
1839 ihaka 2063
    SEXP arg1 = CADR(expr);
2064
    SEXP arg2 = CADDR(expr);
2 r 2065
 
1839 ihaka 2066
    return combineBBoxes(
2067
	      elementBBox(arg1),
2068
	      combineBBoxes(
2069
	         gapBBox(relGap()),
2070
	         combineBBoxes(
1926 ihaka 2071
		    asciiBBox(relAscii(op)),
1839 ihaka 2072
		    combineBBoxes(
2073
		       gapBBox(relGap()),
2074
		       elementBBox(arg2)))));
2 r 2075
}
2076
 
2077
static void drawRel(SEXP expr)
2078
{
1926 ihaka 2079
    SEXP op = CAR(expr);
1839 ihaka 2080
    SEXP arg1 = CADR(expr);
2081
    SEXP arg2 = CADDR(expr);
2 r 2082
 
1839 ihaka 2083
    drawElement(arg1);
2084
    drawGap(relGap());
1926 ihaka 2085
    drawAscii(relAscii(op));
1839 ihaka 2086
    drawGap(relGap());
2087
    drawElement(arg2);
2 r 2088
}
2089
 
2090
/* code for bold expressions */
2091
 
2092
static BBOX boldBBox(SEXP expr)
2093
{
1839 ihaka 2094
    BBOX result;
2095
    int savedFont = getFont();
2 r 2096
 
1839 ihaka 2097
    boldFont();
2098
    result = elementBBox(CADR(expr));
2099
    setFont(savedFont);
2 r 2100
 
1839 ihaka 2101
    return result;
2 r 2102
}
2103
 
2104
static void drawBold(SEXP expr)
2105
{
1839 ihaka 2106
    int savedFont = getFont();
2 r 2107
 
1839 ihaka 2108
    boldFont();
2109
    drawElement(CADR(expr));
2110
    setFont(savedFont);
2 r 2111
}
2112
 
2113
/* code for italic expressions */
2114
 
2115
static BBOX italicBBox(SEXP expr)
2116
{
1839 ihaka 2117
    BBOX result;
2118
    SEXP body = CADR(expr);
2119
    int savedFont = getFont();
2 r 2120
 
1839 ihaka 2121
    italicFont();
2122
    result = elementBBox(body);
2123
    setFont(savedFont);
2 r 2124
 
1839 ihaka 2125
    return result;
2 r 2126
}
2127
 
2128
static void drawItalic(SEXP expr)
2129
{
1839 ihaka 2130
    SEXP body = CADR(expr);
2131
    int savedFont = getFont();
2 r 2132
 
1839 ihaka 2133
    italicFont();
2134
    drawElement(body);
2135
    setFont(savedFont);
2 r 2136
}
2137
 
2138
/* code for plain expressions */
2139
 
2140
static BBOX plainBBox(SEXP expr)
2141
{
1839 ihaka 2142
    BBOX result = nullBBox();
2143
    int savedFont = getFont();
2 r 2144
 
1839 ihaka 2145
    plainFont();
2146
    result = elementBBox(CADR(expr));
2147
    setFont(savedFont);
2 r 2148
 
1839 ihaka 2149
    return result;
2 r 2150
}
2151
 
2152
static void drawPlain(SEXP expr)
2153
{
1839 ihaka 2154
    int savedFont = getFont();
2 r 2155
 
1839 ihaka 2156
    plainFont();
2157
    drawElement(CADR(expr));
2158
    setFont(savedFont);
2 r 2159
}
2160
 
2161
/* code for bolditalic expressions */
2162
 
2163
static BBOX boldItalicBBox(SEXP expr)
2164
{
1839 ihaka 2165
    BBOX result;
2166
    int savedFont = getFont();
2 r 2167
 
1839 ihaka 2168
    boldItalicFont();
2169
    result = elementBBox(CADR(expr));
2170
    setFont(savedFont);
2 r 2171
 
1839 ihaka 2172
    return result;
2 r 2173
}
2174
 
2175
static void drawBoldItalic(SEXP expr)
2176
{
1839 ihaka 2177
    int savedFont = getFont();
2 r 2178
 
1839 ihaka 2179
    boldItalicFont();
2180
    drawElement(CADR(expr));
2181
    setFont(savedFont);
2 r 2182
}
2183
 
1926 ihaka 2184
/* code for concatenating expressions paste(...) */
2 r 2185
 
2186
static BBOX concatenateBBox(SEXP expr)
2187
{
1839 ihaka 2188
    SEXP args = CDR(expr);
2189
    SEXP lastArg;
2190
    int i;
2191
    int numArgs = length(args);
2192
    BBOX result = nullBBox();
2 r 2193
 
1839 ihaka 2194
    if (numArgs > 0)
2195
	result = elementBBox(CAR(args));
2 r 2196
    lastArg = CAR(args);
2197
    args = CDR(args);
2198
 
1926 ihaka 2199
    for (i = 1; i < numArgs; i++) {
1839 ihaka 2200
	result = combineBBoxes(result,
2201
		    combineBBoxes(
2202
		       correctionBetweenBBox(lastArg, CAR(args)),
2203
		       elementBBox(CAR(args))));
2204
	lastArg = CAR(args);
2205
	args = CDR(args);
2206
    }
2207
 
2208
    return result;
2 r 2209
}
2210
 
2211
static void drawConcatenate(SEXP expr)
2212
{
1839 ihaka 2213
    SEXP args = CDR(expr);
2214
    SEXP lastArg;
2215
    int i;
2216
    int numArgs = length(args);
2 r 2217
 
1926 ihaka 2218
    for (i = 0; i < numArgs; i++) {
1839 ihaka 2219
	if (i > 0)
2220
	    drawCorrectionBetween(lastArg, CAR(args));
2221
	drawElement(CAR(args));
2222
	lastArg = CAR(args);
2223
	args = CDR(args);
2224
    }
2 r 2225
}
2226
 
1839 ihaka 2227
/* Dispatching procedure which determines nature of expression. */
2 r 2228
 
2229
static BBOX formulaBBox(SEXP expr)
2230
{
1839 ihaka 2231
    SEXP head = CAR(expr);
2 r 2232
 
1926 ihaka 2233
    if (spaceAtom(head))
2234
	return spaceBBox(expr);
2235
    else if (binAtom(head))
1839 ihaka 2236
	return binBBox(expr);
2237
    else if (superAtom(head))
2238
	return supBBox(expr);
2239
    else if (subAtom(head))
2240
	return subBBox(expr);
2241
    else if (accentAtom(head))
2242
	return accentBBox(expr);
2243
    else if (fractionAtom(head))
2244
	return fractionBBox(expr);
2245
    else if (groupAtom(head))
2246
	return groupBBox(expr);
2247
    else if (operatorAtom(head))
2248
	return operatorBBox(expr);
2249
    else if (radicalAtom(head))
2250
	return radicalBBox(expr);
2251
    else if (absAtom(head))
2252
	return absBBox(expr);
2253
    else if (curlyAtom(head))
2254
	return curlyBBox(expr);
2255
    else if (relAtom(head))
2256
	return relBBox(expr);
2257
    else if (boldAtom(head))
2258
	return boldBBox(expr);
2259
    else if (italicAtom(head))
2260
	return italicBBox(expr);
2261
    else if (plainAtom(head))
2262
	return plainBBox(expr);
2263
    else if (boldItalicAtom(head))
2264
	return boldItalicBBox(expr);
2265
    else if (concatenateAtom(head))
2266
	return concatenateBBox(expr);
2267
    else
2268
	return expressionBBox(expr);
2 r 2269
}
2270
 
2271
static void drawFormula(SEXP expr)
2272
{
1839 ihaka 2273
    SEXP head = CAR(expr);
2 r 2274
 
1926 ihaka 2275
    if (spaceAtom(head))
2276
	drawSpace(expr);
2277
    else if (binAtom(head))
1839 ihaka 2278
	drawBin(expr);
2279
    else if (superAtom(head))
2280
	drawSuper(expr);
2281
    else if (subAtom(head))
2282
	drawSub(expr);
2283
    else if (accentAtom(head))
2284
	drawAccent(expr);
2285
    else if (fractionAtom(head))
2286
	drawFraction(expr);
2287
    else if (groupAtom(head))
2288
	drawGroup(expr);
2289
    else if (operatorAtom(head))
2290
	drawOperator(expr);
2291
    else if (radicalAtom(head))
2292
	drawRadical(expr);
2293
    else if (absAtom(head))
2294
	drawAbs(expr);
2295
    else if (curlyAtom(head))
2296
	drawCurly(expr);
2297
    else if (relAtom(head))
2298
	drawRel(expr);
2299
    else if (boldAtom(head))
2300
	drawBold(expr);
2301
    else if (italicAtom(head))
2302
	drawItalic(expr);
2303
    else if (plainAtom(head))
2304
	drawPlain(expr);
2305
    else if (boldItalicAtom(head))
2306
	drawBoldItalic(expr);
2307
    else if (concatenateAtom(head))
2308
	drawConcatenate(expr);
2 r 2309
 
1839 ihaka 2310
    /* if expression is not a special mathematical notation */
2311
    /* function then just reconstruct expression */
2 r 2312
 
1839 ihaka 2313
    else
2314
	drawExpression(expr);
2 r 2315
}
2316
 
2317
/* top-level:  dispatch on whether atom (symbol, string, number, ...) */
2318
/* or formula (some sort of expression) */
2319
 
2320
static BBOX elementBBox(SEXP expr)
2321
{
1839 ihaka 2322
    if (formulaExpression(expr))
2323
	return formulaBBox(expr);
2324
    else
2325
	return atomBBox(expr);
2 r 2326
}
2327
 
2328
static void drawElement(SEXP expr)
2329
{
1839 ihaka 2330
    if (formulaExpression(expr))
2331
	drawFormula(expr);
2332
    else
2333
	drawAtom(expr);
2 r 2334
}
2335
 
257 paul 2336
        /* calculate width of expression */
2337
        /* BBOXes are in INCHES (see metricUnit) */
581 paul 2338
double GExpressionWidth(SEXP expr, int units, DevDesc *dd)
257 paul 2339
{
1839 ihaka 2340
    BBOX exprBBox = elementBBox(expr);
2341
    double w  = exprBBox.width;
2342
    if (units == INCHES)
2343
	return w;
2344
    else
2345
	return GConvertXUnits(w, INCHES, units, dd);
257 paul 2346
}
2347
 
2348
#define ABS(a)  ((a)>=0 ? (a) : -(a))
2349
 
581 paul 2350
double GExpressionHeight(SEXP expr, int units, DevDesc *dd)
257 paul 2351
{
1839 ihaka 2352
    BBOX exprBBox = elementBBox(expr);
2353
    double h = exprBBox.height + exprBBox.depth;
2354
    if (units == INCHES)
2355
	return h;
2356
    else
2357
	return GConvertYUnits(h, INCHES, units, dd);
257 paul 2358
}
2359
 
1839 ihaka 2360
/* Functions forming the API */
2 r 2361
 
1016 maechler 2362
void GMathText(double x, double y, int coords, SEXP expr,
581 paul 2363
	       double xc, double yc, double rot, DevDesc *dd)
2 r 2364
{
1839 ihaka 2365
    BBOX expressionBBox;
2 r 2366
 
1839 ihaka 2367
    mathDevice = dd;
2368
    expressionBBox = elementBBox(expr);
2369
    referenceX = x;
2370
    referenceY = y;
2371
    GConvert(&referenceX, &referenceY, coords, INCHES, dd);
2 r 2372
 
1839 ihaka 2373
    currentX = referenceX - xc * bboxWidth(expressionBBox);
2374
    currentY = referenceY - yc * bboxHeight(expressionBBox);
2375
    currentAngle = rot;
2376
    cosAngle = cos(rot / 90 * half_pi);
2377
    sinAngle = sin(rot / 90 * half_pi);
2378
    drawElement(expr);
2 r 2379
}
2380
 
2381
 
581 paul 2382
void GMMathText(SEXP str, int side, double line, int outer, double at, int las,
2383
		DevDesc *dd)
2 r 2384
{
1839 ihaka 2385
    int coords;
2386
    double a, xadj, yadj;
2 r 2387
 
1839 ihaka 2388
    mathDevice = dd;
581 paul 2389
 
1839 ihaka 2390
    if (outer) {
2391
	switch (side) {
2392
	case 1:
2393
	    line = line + 1;
2394
	    coords = MAR1;
2395
	    a = 0.0;
2396
	    xadj = mathDevice->gp.adj;
2397
	    yadj = 0.0;
2398
	    break;
2399
	case 2:
2400
	    coords = MAR2;
2401
	    a = 90.0;
2402
	    xadj = mathDevice->gp.adj;
2403
	    yadj = 0.0;
2404
	    break;
2405
	case 3:
2406
	    coords = MAR3;
2407
	    a = 0.0;
2408
	    xadj = mathDevice->gp.adj;
2409
	    yadj = 0.0;
2410
	    break;
2411
	case 4:
2412
	    line = line + 1;
2413
	    coords = MAR4;
2414
	    a = 90.0;
2415
	    xadj = mathDevice->gp.adj;
2416
	    yadj = 0.0;
2417
	    break;
2 r 2418
	}
1839 ihaka 2419
	GMathText(at, line, coords, str, xadj, yadj, a, dd);
2420
    }
2421
    else {
2422
	switch (side) {
2423
	case 1:
2424
	    if (las == 2) {
2425
		at = at - GConvertXUnits(dd->gp.yLineBias,
2426
					 LINES, USER, dd);
2427
		line = line + dd->gp.yLineBias;
2428
		a = 90.0;
2429
		xadj = 1.0;
2430
		yadj = 0.5;
2431
	    }
2432
	    else {
2433
		line = line + 1 - dd->gp.yLineBias;
2434
		a = 0.0;
2435
		xadj = mathDevice->gp.adj;
2436
		yadj = 0.0;
2437
	    }
2438
	    coords = MAR1;
2439
	    break;
2440
	case 2:
2441
	    if (las == 1 || las == 2) {
2442
		at = at + GConvertYUnits(dd->gp.yLineBias,
2443
					 LINES, USER, dd);
2444
		line = line + dd->gp.yLineBias;
2445
		a = 0.0;
2446
		xadj = 1.0;
2447
		yadj = 0.5;
2448
	    }
2449
	    else {
2450
		line = line + dd->gp.yLineBias;
2451
		a = 90.0;
2452
		xadj = mathDevice->gp.adj;
2453
		yadj = 0.0;
2454
	    }
2455
	    coords = MAR2;
2456
	    break;
2457
	case 3:
2458
	    if (las == 2) {
2459
		at = at - GConvertXUnits(dd->gp.yLineBias,
2460
					 LINES, USER, dd);
2461
		line = line + dd->gp.yLineBias;
2462
		a = 90.0;
2463
		xadj = 0.0;
2464
		yadj = 0.5;
2465
	    }
2466
	    else {
2467
		line = line + dd->gp.yLineBias;
2468
		a = 0.0;
2469
		xadj = mathDevice->gp.adj;
2470
		yadj = 0.0;
2471
	    }
2472
	    coords = MAR3;
2473
	    break;
2474
	case 4:
2475
	    if (las == 1 || las == 2) {
2476
		at = at + GConvertYUnits(dd->gp.yLineBias,
2477
					 LINES, USER, dd);
2478
		line = line + dd->gp.yLineBias;
2479
		a = 0.0;
2480
		xadj = 0.0;
2481
		yadj = 0.5;
2482
	    }
2483
	    else {
2484
		line = line + 1 - dd->gp.yLineBias;
2485
		a = 90.0;
2486
		xadj = mathDevice->gp.adj;
2487
		yadj = 0.0;
2488
	    }
2489
	    coords = MAR4;
2490
	    break;
2 r 2491
	}
1839 ihaka 2492
	GMathText(at, line, coords, str, xadj, yadj, a, dd);
2493
    }
2 r 2494
}