The R Project SVN R

Rev

Rev 526 | Rev 1009 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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