The R Project SVN R

Rev

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