The R Project SVN R

Rev

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