blob: f80d407dcf8b1550103dc7887741b2dc4b32ab47 [file] [log] [blame]
/*
* R : A Computer Language for Statistical Data Analysis
* Copyright (C) 1995, 1996, 1997 Robert Gentleman and Ross Ihaka
* Copyright (C) 1998-2015 The R Core Team
*
* This source code module:
* Copyright (C) 1997, 1998 Paul Murrell and Ross Ihaka
* Copyright (C) 1998-2015 The R Core Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, a copy is available at
* https://www.R-project.org/Licenses/
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <Defn.h>
#include <ctype.h>
#include <rlocale.h>
#include <Rmath.h> // provides M_2PI
#include <R_ext/GraphicsEngine.h>
/*
* TeX Math Styles
*
* The TeXBook, Appendix G, Page 441.
*
*/
typedef enum {
STYLE_SS1 = 1,
STYLE_SS = 2,
STYLE_S1 = 3,
STYLE_S = 4,
STYLE_T1 = 5,
STYLE_T = 6,
STYLE_D1 = 7,
STYLE_D = 8
} STYLE;
typedef struct {
unsigned int BoxColor;
double BaseCex;
double ReferenceX;
double ReferenceY;
double CurrentX;
double CurrentY;
double CurrentAngle;
double CosAngle;
double SinAngle;
STYLE CurrentStyle;
} mathContext;
static GEUnit MetricUnit = GE_INCHES;
/* Font Definitions */
typedef enum {
PlainFont = 1,
BoldFont = 2,
ItalicFont = 3,
BoldItalicFont = 4,
SymbolFont = 5
} FontType;
/*
* Italic Correction Factor
*
* The correction for a character is computed as ItalicFactor
* times the height (above the baseline) of the character's
* bounding box.
*
*/
static double ItalicFactor = 0.15;
/* Drawing basics */
/* Convert CurrentX and CurrentY from */
/* 0 angle to and CurrentAngle */
static double ConvertedX(mathContext *mc, pGEDevDesc dd)
{
double rotatedX = mc->ReferenceX +
(mc->CurrentX - mc->ReferenceX) * mc->CosAngle -
(mc->CurrentY - mc->ReferenceY) * mc->SinAngle;
return toDeviceX(rotatedX, MetricUnit, dd);
}
static double ConvertedY(mathContext *mc, pGEDevDesc dd)
{
double rotatedY = mc->ReferenceY +
(mc->CurrentY - mc->ReferenceY) * mc->CosAngle +
(mc->CurrentX - mc->ReferenceX) * mc->SinAngle;
return toDeviceY(rotatedY, MetricUnit, dd);
}
static void PMoveAcross(double xamount, mathContext *mc)
{
mc->CurrentX += xamount;
}
static void PMoveUp(double yamount, mathContext *mc)
{
mc->CurrentY += yamount;
}
static void PMoveTo(double x, double y, mathContext *mc)
{
mc->CurrentX = x;
mc->CurrentY = y;
}
/* Basic Font Properties */
static double xHeight(pGEcontext gc, pGEDevDesc dd)
{
double height, depth, width;
GEMetricInfo('x', gc, &height, &depth, &width, dd);
return fromDeviceHeight(height, MetricUnit, dd);
}
static double XHeight(pGEcontext gc, pGEDevDesc dd)
{
double height, depth, width;
GEMetricInfo('X', gc, &height, &depth, &width, dd);
return fromDeviceHeight(height, MetricUnit, dd);
}
static double AxisHeight(pGEcontext gc, pGEDevDesc dd)
{
double height, depth, width;
GEMetricInfo('+', gc, &height, &depth, &width, dd);
return fromDeviceHeight(0.5 * height, MetricUnit, dd);
}
static double Quad(pGEcontext gc, pGEDevDesc dd)
{
double height, depth, width;
GEMetricInfo('M', gc, &height, &depth, &width, dd);
return fromDeviceHeight(width, MetricUnit, dd);
}
/* The height of digits */
static double FigHeight(pGEcontext gc, pGEDevDesc dd)
{
double height, depth, width;
GEMetricInfo('0', gc, &height, &depth, &width, dd);
return fromDeviceHeight(height, MetricUnit, dd);
}
/* Depth of lower case descenders */
static double DescDepth(pGEcontext gc, pGEDevDesc dd)
{
double height, depth, width;
GEMetricInfo('g', gc, &height, &depth, &width, dd);
return fromDeviceHeight(depth, MetricUnit, dd);
}
/* Thickness of rules */
static double RuleThickness(void)
{
return 0.015;
}
static double ThinSpace(pGEcontext gc, pGEDevDesc dd)
{
double height, depth, width;
static double OneSixth = 0.16666666666666666666;
GEMetricInfo('M', gc, &height, &depth, &width, dd);
return fromDeviceHeight(OneSixth * width, MetricUnit, dd);
}
static double MediumSpace(pGEcontext gc, pGEDevDesc dd)
{
double height, depth, width;
static double TwoNinths = 0.22222222222222222222;
GEMetricInfo('M', gc, &height, &depth, &width, dd);
return fromDeviceHeight(TwoNinths * width, MetricUnit, dd);
}
static double ThickSpace(pGEcontext gc, pGEDevDesc dd)
{
double height, depth, width;
static double FiveEighteenths = 0.27777777777777777777;
GEMetricInfo('M', gc, &height, &depth, &width, dd);
return fromDeviceHeight(FiveEighteenths * width, MetricUnit, dd);
}
static double MuSpace(pGEcontext gc, pGEDevDesc dd)
{
double height, depth, width;
static double OneEighteenth = 0.05555555555555555555;
GEMetricInfo('M', gc, &height, &depth, &width, dd);
return fromDeviceHeight(OneEighteenth * width, MetricUnit, dd);
}
/*
* Mathematics Layout Parameters
*
* The TeXBook, Appendix G, Page 447.
*
* These values are based on an inspection of TeX metafont files
* together with some visual simplification.
*
* Note : The values are ``optimised'' for PostScript.
*
*/
typedef enum {
sigma2, sigma5, sigma6, sigma8, sigma9, sigma10, sigma11,
sigma12, sigma13, sigma14, sigma15, sigma16, sigma17, sigma18,
sigma19, sigma20, sigma21, sigma22, xi8, xi9, xi10, xi11, xi12, xi13
}
TEXPAR;
#define SUBS 0.7
static double TeX(TEXPAR which, pGEcontext gc, pGEDevDesc dd)
{
switch(which) {
case sigma2: /* space */
case sigma5: /* x_height */
return xHeight(gc, dd);
case sigma6: /* quad */
return Quad(gc, dd);
case sigma8: /* num1 */
return AxisHeight(gc, dd)
+ 3.51 * RuleThickness()
+ 0.15 * XHeight(gc, dd) /* 54/36 * 0.1 */
+ SUBS * DescDepth(gc, dd);
case sigma9: /* num2 */
return AxisHeight(gc, dd)
+ 1.51 * RuleThickness()
+ 0.08333333 * XHeight(gc, dd); /* 30/36 * 0.1 */
case sigma10: /* num3 */
return AxisHeight(gc, dd)
+ 1.51 * RuleThickness()
+ 0.1333333 * XHeight(gc, dd); /* 48/36 * 0.1 */
case sigma11: /* denom1 */
return - AxisHeight(gc, dd)
+ 3.51 * RuleThickness()
+ SUBS * FigHeight(gc, dd)
+ 0.344444 * XHeight(gc, dd); /* 124/36 * 0.1 */
case sigma12: /* denom2 */
return - AxisHeight(gc, dd)
+ 1.51 * RuleThickness()
+ SUBS * FigHeight(gc, dd)
+ 0.08333333 * XHeight(gc, dd); /* 30/36 * 0.1 */
case sigma13: /* sup1 */
return 0.95 * xHeight(gc, dd);
case sigma14: /* sup2 */
return 0.825 * xHeight(gc, dd);
case sigma15: /* sup3 */
return 0.7 * xHeight(gc, dd);
case sigma16: /* sub1 */
return 0.35 * xHeight(gc, dd);
case sigma17: /* sub2 */
return 0.45 * XHeight(gc, dd);
case sigma18: /* sup_drop */
return 0.3861111 * XHeight(gc, dd);
case sigma19: /* sub_drop */
return 0.05 * XHeight(gc, dd);
case sigma20: /* delim1 */
return 2.39 * XHeight(gc, dd);
case sigma21: /* delim2 */
return 1.01 *XHeight(gc, dd);
case sigma22: /* axis_height */
return AxisHeight(gc, dd);
case xi8: /* default_rule_thickness */
return RuleThickness();
case xi9: /* big_op_spacing1 */
case xi10: /* big_op_spacing2 */
case xi11: /* big_op_spacing3 */
case xi12: /* big_op_spacing4 */
case xi13: /* big_op_spacing5 */
return 0.15 * XHeight(gc, dd);
default:/* never happens (enum type) */
error("invalid `which' in C function TeX"); return 0;/*-Wall*/
}
}
static STYLE GetStyle(mathContext *mc)
{
return mc->CurrentStyle;
}
static void SetStyle(STYLE newstyle, mathContext *mc, pGEcontext gc)
{
switch (newstyle) {
case STYLE_D:
case STYLE_T:
case STYLE_D1:
case STYLE_T1:
gc->cex = 1.0 * mc->BaseCex;
break;
case STYLE_S:
case STYLE_S1:
gc->cex = 0.7 * mc->BaseCex;
break;
case STYLE_SS:
case STYLE_SS1:
gc->cex = 0.5 * mc->BaseCex;
break;
default:
error(_("invalid math style encountered"));
}
mc->CurrentStyle = newstyle;
}
static void SetPrimeStyle(STYLE style, mathContext *mc, pGEcontext gc)
{
switch (style) {
case STYLE_D:
case STYLE_D1:
SetStyle(STYLE_D1, mc, gc);
break;
case STYLE_T:
case STYLE_T1:
SetStyle(STYLE_T1, mc, gc);
break;
case STYLE_S:
case STYLE_S1:
SetStyle(STYLE_S1, mc, gc);
break;
case STYLE_SS:
case STYLE_SS1:
SetStyle(STYLE_SS1, mc, gc);
break;
}
}
static void SetSupStyle(STYLE style, mathContext *mc, pGEcontext gc)
{
switch (style) {
case STYLE_D:
case STYLE_T:
SetStyle(STYLE_S, mc, gc);
break;
case STYLE_D1:
case STYLE_T1:
SetStyle(STYLE_S1, mc, gc);
break;
case STYLE_S:
case STYLE_SS:
SetStyle(STYLE_SS, mc, gc);
break;
case STYLE_S1:
case STYLE_SS1:
SetStyle(STYLE_SS1, mc, gc);
break;
}
}
static void SetSubStyle(STYLE style, mathContext *mc, pGEcontext gc)
{
switch (style) {
case STYLE_D:
case STYLE_T:
case STYLE_D1:
case STYLE_T1:
SetStyle(STYLE_S1, mc, gc);
break;
case STYLE_S:
case STYLE_SS:
case STYLE_S1:
case STYLE_SS1:
SetStyle(STYLE_SS1, mc, gc);
break;
}
}
static void SetNumStyle(STYLE style, mathContext *mc, pGEcontext gc)
{
switch (style) {
case STYLE_D:
SetStyle(STYLE_T, mc, gc);
break;
case STYLE_D1:
SetStyle(STYLE_T1, mc, gc);
break;
default:
SetSupStyle(style, mc, gc);
}
}
static void SetDenomStyle(STYLE style, mathContext *mc, pGEcontext gc)
{
if (style > STYLE_T)
SetStyle(STYLE_T1, mc, gc);
else
SetSubStyle(style, mc, gc);
}
static int IsCompactStyle(STYLE style, mathContext *mc, pGEcontext gc)
{
switch (style) {
case STYLE_D1:
case STYLE_T1:
case STYLE_S1:
case STYLE_SS1:
return 1;
default:
return 0;
}
}
#ifdef max
#undef max
#endif
/* Return maximum of two doubles. */
static double max(double x, double y)
{
if (x > y) return x;
else return y;
}
/* Bounding Boxes */
/* These including italic corrections and an */
/* indication of whether the nucleus was simple. */
typedef struct {
double height;
double depth;
double width;
double italic;
int simple;
} BBOX;
#define bboxHeight(bbox) bbox.height
#define bboxDepth(bbox) bbox.depth
#define bboxWidth(bbox) bbox.width
#define bboxItalic(bbox) bbox.italic
#define bboxSimple(bbox) bbox.simple
static BBOX MakeBBox(double height, double depth, double width)
{
BBOX bbox;
bboxHeight(bbox) = height;
bboxDepth(bbox) = depth;
bboxWidth(bbox) = width;
bboxItalic(bbox) = 0;
bboxSimple(bbox) = 0;
return bbox;
}
static BBOX NullBBox(void)
{
BBOX bbox;
bboxHeight(bbox) = 0;
bboxDepth(bbox) = 0;
bboxWidth(bbox) = 0;
bboxItalic(bbox) = 0;
bboxSimple(bbox) = 0;
return bbox;
}
static BBOX ShiftBBox(BBOX bbox1, double shiftV)
{
bboxHeight(bbox1) = bboxHeight(bbox1) + shiftV;
bboxDepth(bbox1) = bboxDepth(bbox1) - shiftV;
bboxWidth(bbox1) = bboxWidth(bbox1);
bboxItalic(bbox1) = bboxItalic(bbox1);
bboxSimple(bbox1) = bboxSimple(bbox1);
return bbox1;
}
static BBOX EnlargeBBox(BBOX bbox, double deltaHeight, double deltaDepth,
double deltaWidth)
{
bboxHeight(bbox) += deltaHeight;
bboxDepth(bbox) += deltaDepth;
bboxWidth(bbox) += deltaWidth;
return bbox;
}
static BBOX CombineBBoxes(BBOX bbox1, BBOX bbox2)
{
bboxHeight(bbox1) = max(bboxHeight(bbox1), bboxHeight(bbox2));
bboxDepth(bbox1) = max(bboxDepth(bbox1), bboxDepth(bbox2));
bboxWidth(bbox1) = bboxWidth(bbox1) + bboxWidth(bbox2);
bboxItalic(bbox1) = bboxItalic(bbox2);
bboxSimple(bbox1) = bboxSimple(bbox2);
return bbox1;
}
static BBOX CombineAlignedBBoxes(BBOX bbox1, BBOX bbox2)
{
bboxHeight(bbox1) = max(bboxHeight(bbox1), bboxHeight(bbox2));
bboxDepth(bbox1) = max(bboxDepth(bbox1), bboxDepth(bbox2));
bboxWidth(bbox1) = max(bboxWidth(bbox1), bboxWidth(bbox2));
bboxItalic(bbox1) = 0;
bboxSimple(bbox1) = 0;
return bbox1;
}
static BBOX CombineOffsetBBoxes(BBOX bbox1, int italic1,
BBOX bbox2, int italic2,
double xoffset,
double yoffset)
{
double width1 = bboxWidth(bbox1) + (italic1 ? bboxItalic(bbox1) : 0);
double width2 = bboxWidth(bbox2) + (italic2 ? bboxItalic(bbox2) : 0);
bboxWidth(bbox1) = max(width1, width2 + xoffset);
bboxHeight(bbox1) = max(bboxHeight(bbox1), bboxHeight(bbox2) + yoffset);
bboxDepth(bbox1) = max(bboxDepth(bbox1), bboxDepth(bbox2) - yoffset);
bboxItalic(bbox1) = 0;
bboxSimple(bbox1) = 0;
return bbox1;
}
static double CenterShift(BBOX bbox)
{
return 0.5 * (bboxHeight(bbox) - bboxDepth(bbox));
}
typedef struct {
char *name;
int code;
} SymTab;
/* Determine a match between symbol name and string. */
static int NameMatch(SEXP expr, const char *aString)
{
if (!isSymbol(expr)) return 0;
return !strcmp(CHAR(PRINTNAME(expr)), aString);
}
static int StringMatch(SEXP expr, const char *aString)
{
return !strcmp(translateChar(STRING_ELT(expr, 0)), aString);
}
/* Code to determine the ascii code corresponding */
/* to an element of a mathematical expression. */
#define A_HAT 94
#define A_TILDE 126
#define S_SPACE 32
#define S_PARENLEFT 40
#define S_PARENRIGHT 41
#define S_ASTERISKMATH 42
#define S_COMMA 44
#define S_SLASH 47
#define S_RADICALEX 96
#define S_FRACTION 164
#define S_ELLIPSIS 188
#define S_INTERSECTION 199
#define S_UNION 200
#define S_PRODUCT 213
#define S_RADICAL 214
#define S_SUM 229
#define S_INTEGRAL 242
#define S_BRACKETLEFTTP 233
#define S_BRACKETLEFTBT 235
#define S_BRACKETRIGHTTP 249
#define S_BRACKETRIGHTBT 251
#define N_LIM 1001
#define N_LIMINF 1002
#define N_LIMSUP 1003
#define N_INF 1004
#define N_SUP 1005
#define N_MIN 1006
#define N_MAX 1007
/* The Full Adobe Symbol Font */
static SymTab
SymbolTable[] = {
{ "space", 32 },
{ "exclam", 33 },
{ "universal", 34 },
{ "numbersign", 35 },
{ "existential", 36 },
{ "percent", 37 },
{ "ampersand", 38 },
{ "suchthat", 39 },
{ "parenleft", 40 },
{ "parenright", 41 },
{ "asteriskmath", 42 },
{ "plus", 43 },
{ "comma", 44 },
{ "minus", 45 },
{ "period", 46 },
{ "slash", 47 },
{ "0", 48 },
{ "1", 49 },
{ "2", 50 },
{ "3", 51 },
{ "4", 52 },
{ "5", 53 },
{ "6", 54 },
{ "7", 55 },
{ "8", 56 },
{ "9", 57 },
{ "colon", 58 },
{ "semicolon", 59 },
{ "less", 60 },
{ "equal", 61 },
{ "greater", 62 },
{ "question", 63 },
{ "congruent", 64 },
{ "Alpha",/* 0101= */65 }, /* Upper Case Greek Characters */
{ "Beta", 66 },
{ "Chi", 67 },
{ "Delta", 68 },
{ "Epsilon", 69 },
{ "Phi", 70 },
{ "Gamma", 71 },
{ "Eta", 72 },
{ "Iota", 73 },
{ "theta1", 74 },
{ "vartheta", 74 },
{ "Kappa", 75 },
{ "Lambda", 76 },
{ "Mu", 77 },
{ "Nu", 78 },
{ "Omicron", 79 },
{ "Pi", 80 },
{ "Theta", 81 },
{ "Rho", 82 },
{ "Sigma", 83 },
{ "Tau", 84 },
{ "Upsilon", 85 },
{ "sigma1", 86 },
{ "varsigma", 86 },
{ "stigma", 86 },
{ "Omega", 87 },
{ "Xi", 88 },
{ "Psi", 89 },
{ "Zeta",/* 0132 = */90 },
{ "bracketleft", 91 }, /* Miscellaneous Special Characters */
{ "therefore", 92 },
{ "bracketright", 93 },
{ "perpendicular", 94 },
{ "underscore", 95 },
{ "radicalex", 96 },
{ "alpha",/* 0141= */97 }, /* Lower Case Greek Characters */
{ "beta", 98 },
{ "chi", 99 },
{ "delta", 100 },
{ "epsilon", 101 },
{ "phi", 102 },
{ "gamma", 103 },
{ "eta", 104 },
{ "iota", 105 },
{ "phi1", 106 },
{ "varphi", 106 },
{ "kappa", 107 },
{ "lambda", 108 },
{ "mu", 109 },
{ "nu", 110 },
{ "omicron", 111 },
{ "pi", 112 },
{ "theta", 113 },
{ "rho", 114 },
{ "sigma", 115 },
{ "tau", 116 },
{ "upsilon", 117 },
{ "omega1", 118 },
{ "omega", 119 },
{ "xi", 120 },
{ "psi", 121 },
{ "zeta",/* 0172= */122 },
{ "braceleft", 123 }, /* Miscellaneous Special Characters */
{ "bar", 124 },
{ "braceright", 125 },
{ "similar", 126 },
{ "Upsilon1", 161 }, /* Lone Greek */
{ "minute", 162 },
{ "lessequal", 163 },
{ "fraction", 164 },
{ "infinity", 165 },
{ "florin", 166 },
{ "club", 167 },
{ "diamond", 168 },
{ "heart", 169 },
{ "spade", 170 },
{ "arrowboth", 171 },
{ "arrowleft", 172 },
{ "arrowup", 173 },
{ "arrowright", 174 },
{ "arrowdown", 175 },
{ "degree", 176 },
{ "plusminus", 177 },
{ "second", 178 },
{ "greaterequal", 179 },
{ "multiply", 180 },
{ "proportional", 181 },
{ "partialdiff", 182 },
{ "bullet", 183 },
{ "divide", 184 },
{ "notequal", 185 },
{ "equivalence", 186 },
{ "approxequal", 187 },
{ "ellipsis", 188 },
{ "arrowvertex", 189 },
{ "arrowhorizex", 190 },
{ "carriagereturn", 191 },
{ "aleph", 192 },
{ "Ifraktur", 193 },
{ "Rfraktur", 194 },
{ "weierstrass", 195 },
{ "circlemultiply", 196 },
{ "circleplus", 197 },
{ "emptyset", 198 },
{ "intersection", 199 },/* = 0307 */
{ "union", 200 },/* = 0310 */
{ "propersuperset", 201 },
{ "reflexsuperset", 202 },
{ "notsubset", 203 },
{ "propersubset", 204 },
{ "reflexsubset", 205 },
{ "element", 206 },
{ "notelement", 207 },
{ "angle", 208 },
{ "nabla", 209 },/* = 0321, Adobe name 'gradient' */
{ "registerserif", 210 },
{ "copyrightserif", 211 },
{ "trademarkserif", 212 },
{ "product", 213 },
{ "radical", 214 },
{ "dotmath", 215 },
{ "logicaland", 217 },
{ "logicalor", 218 },
{ "arrowdblboth", 219 },
{ "arrowdblleft", 220 },
{ "arrowdblup", 221 },
{ "arrowdblright", 222 },
{ "arrowdbldown", 223 },
{ "lozenge", 224 },
{ "angleleft", 225 },
{ "registersans", 226 },
{ "copyrightsans", 227 },
{ "trademarksans", 228 },
{ "summation", 229 },
{ "parenlefttp", 230 },
{ "parenleftex", 231 },
{ "parenleftbt", 232 },
{ "bracketlefttp", 233 },
{ "bracketleftex", 234 },
{ "bracketleftbt", 235 },
{ "bracelefttp", 236 },
{ "braceleftmid", 237 },
{ "braceleftbt", 238 },
{ "braceex", 239 },
{ "angleright", 241 },
{ "integral", 242 },
{ "integraltp", 243 },
{ "integralex", 244 },
{ "integralbt", 245 },
{ "parenrighttp", 246 },
{ "parenrightex", 247 },
{ "parenrightbt", 248 },
{ "bracketrighttp", 249 },
{ "bracketrightex", 250 },
{ "bracketrightbt", 251 },
{ "bracerighttp", 252 },
{ "bracerightmid", 253 },
{ "bracerightbt", 254 },
{ NULL, 0 },
};
static int SymbolCode(SEXP expr)
{
int i;
for (i = 0; SymbolTable[i].code; i++)
if (NameMatch(expr, SymbolTable[i].name))
return SymbolTable[i].code;
return 0;
}
/* this is the one really used: */
static int TranslatedSymbol(SEXP expr)
{
int code = SymbolCode(expr);
if ((0101 <= code && code <= 0132) || /* l/c Greek */
(0141 <= code && code <= 0172) || /* u/c Greek */
code == 0300 || /* aleph */
code == 0241 || /* Upsilon1 */
code == 0242 || /* minute */
code == 0245 || /* infinity */
code == 0260 || /* degree */
code == 0262 || /* second */
code == 0266 || /* partialdiff */
code == 0321 || /* nabla */
0)
return code;
else // not translated
return 0;
}
/* Code to determine the nature of an expression. */
static int FormulaExpression(SEXP expr)
{
return (TYPEOF(expr) == LANGSXP);
}
static int NameAtom(SEXP expr)
{
return (TYPEOF(expr) == SYMSXP);
}
static int NumberAtom(SEXP expr)
{
return ((TYPEOF(expr) == REALSXP) ||
(TYPEOF(expr) == INTSXP) ||
(TYPEOF(expr) == CPLXSXP));
}
static int StringAtom(SEXP expr)
{
return (TYPEOF(expr) == STRSXP);
}
/* Code to determine a font from the */
/* nature of the expression */
static FontType GetFont(pGEcontext gc)
{
return gc->fontface;
}
static FontType SetFont(FontType font, pGEcontext gc)
{
FontType prevfont = gc->fontface;
gc->fontface = font;
return prevfont;
}
static int UsingItalics(pGEcontext gc)
{
return (gc->fontface == ItalicFont ||
gc->fontface == BoldItalicFont);
}
static BBOX GlyphBBox(int chr, pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox;
double height, depth, width;
int chr1 = chr;
if(dd->dev->wantSymbolUTF8 && gc->fontface == 5)
chr1 = -Rf_AdobeSymbol2ucs2(chr);
GEMetricInfo(chr1, gc, &height, &depth, &width, dd);
bboxHeight(bbox) = fromDeviceHeight(height, MetricUnit, dd);
bboxDepth(bbox) = fromDeviceHeight(depth, MetricUnit, dd);
bboxWidth(bbox) = fromDeviceHeight(width, MetricUnit, dd);
bboxItalic(bbox) = 0;
bboxSimple(bbox) = 1;
return bbox;
}
static BBOX RenderElement(SEXP, int, mathContext*, pGEcontext , pGEDevDesc);
static BBOX RenderOffsetElement(SEXP, double, double, int,
mathContext*, pGEcontext , pGEDevDesc);
static BBOX RenderExpression(SEXP, int, mathContext*, pGEcontext , pGEDevDesc);
static BBOX RenderSymbolChar(int, int, mathContext*, pGEcontext , pGEDevDesc);
/* Code to Generate Bounding Boxes and Draw Formulae. */
static BBOX RenderItalicCorr(BBOX bbox, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
if (bboxItalic(bbox) > 0) {
if (draw)
PMoveAcross(bboxItalic(bbox), mc);
bboxWidth(bbox) += bboxItalic(bbox);
bboxItalic(bbox) = 0;
}
return bbox;
}
static BBOX RenderGap(double gap, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
if (draw)
PMoveAcross(gap, mc);
return MakeBBox(0, 0, gap);
}
/* Draw a Symbol from the Special Font:
this is assumed to be 8-bit encoded in Adobe Symbol.
*/
static BBOX RenderSymbolChar(int ascii, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
FontType prev;
BBOX bbox;
char asciiStr[2];
if (ascii == A_HAT || ascii == A_TILDE)
prev = SetFont(PlainFont, gc);
else
prev = SetFont(SymbolFont, gc);
bbox = GlyphBBox(ascii, gc, dd);
if (draw) {
asciiStr[0] = (char) ascii;
asciiStr[1] = '\0';
GEText(ConvertedX(mc ,dd), ConvertedY(mc, dd), asciiStr,
CE_SYMBOL,
0.0, 0.0, mc->CurrentAngle, gc,
dd);
PMoveAcross(bboxWidth(bbox), mc);
}
SetFont(prev, gc);
return bbox;
}
/* Draw a Symbol String in "Math Mode" */
/* This code inserts italic corrections after */
/* every character. */
static BBOX RenderSymbolStr(const char *str, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
char chr[7] = "";
const char *s = str;
BBOX glyphBBox;
BBOX resultBBox = NullBBox();
double lastItalicCorr = 0;
FontType prevfont = GetFont(gc);
FontType font = prevfont;
if (str) {
/* Need to advance by character, not byte, except in the symbol font.
The latter would be hard to achieve, but perhaps not impossible.
*/
if(mbcslocale && gc->fontface != 5) {
wchar_t wc;
mbstate_t mb_st;
size_t res;
mbs_init(&mb_st);
while (*s) {
wc = 0;
res = mbrtowc(&wc, s, MB_LEN_MAX, &mb_st);
if(res == -1) error("invalid multibyte string '%s'", s);
if (iswdigit(wc) && font != PlainFont) {
font = PlainFont;
SetFont(PlainFont, gc);
}
else if (font != prevfont) {
font = prevfont;
SetFont(prevfont, gc);
}
glyphBBox = GlyphBBox((unsigned int) wc, gc, dd);
if (UsingItalics(gc))
bboxItalic(glyphBBox) =
ItalicFactor * bboxHeight(glyphBBox);
else
bboxItalic(glyphBBox) = 0;
if (draw) {
memset(chr, 0, sizeof(chr));
/* should not be possible, as we just converted to wc */
if(wcrtomb(chr, wc, &mb_st) == -1)
error("invalid multibyte string");
PMoveAcross(lastItalicCorr, mc);
GEText(ConvertedX(mc ,dd), ConvertedY(mc, dd), chr,
CE_NATIVE,
0.0, 0.0, mc->CurrentAngle, gc, dd);
PMoveAcross(bboxWidth(glyphBBox), mc);
}
bboxWidth(resultBBox) += lastItalicCorr;
resultBBox = CombineBBoxes(resultBBox, glyphBBox);
lastItalicCorr = bboxItalic(glyphBBox);
s += res;
}
} else {
while (*s) {
if (isdigit((int)*s) && font != PlainFont) {
font = PlainFont;
SetFont(PlainFont, gc);
}
else if (font != prevfont) {
font = prevfont;
SetFont(prevfont, gc);
}
glyphBBox = GlyphBBox((unsigned char) *s, gc, dd);
if (UsingItalics(gc))
bboxItalic(glyphBBox) =
ItalicFactor * bboxHeight(glyphBBox);
else
bboxItalic(glyphBBox) = 0;
if (draw) {
chr[0] = *s;
PMoveAcross(lastItalicCorr, mc);
GEText(ConvertedX(mc ,dd), ConvertedY(mc, dd), chr,
CE_NATIVE,
0.0, 0.0, mc->CurrentAngle, gc, dd);
PMoveAcross(bboxWidth(glyphBBox), mc);
}
bboxWidth(resultBBox) += lastItalicCorr;
resultBBox = CombineBBoxes(resultBBox, glyphBBox);
lastItalicCorr = bboxItalic(glyphBBox);
s++;
}
}
if (font != prevfont)
SetFont(prevfont, gc);
}
bboxSimple(resultBBox) = 1;
return resultBBox;
}
/* Code for Character String Atoms. */
/* This only gets called from RenderAccent */
static BBOX RenderChar(int ascii, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox;
char asciiStr[7];
bbox = GlyphBBox(ascii, gc, dd);
if (draw) {
memset(asciiStr, 0, sizeof(asciiStr));
if(mbcslocale) {
size_t res = wcrtomb(asciiStr, ascii, NULL);
if(res == -1)
error("invalid character in current multibyte locale");
} else
asciiStr[0] = (char) ascii;
GEText(ConvertedX(mc ,dd), ConvertedY(mc, dd), asciiStr, CE_NATIVE,
0.0, 0.0, mc->CurrentAngle, gc,
dd);
PMoveAcross(bboxWidth(bbox), mc);
}
return bbox;
}
/* This gets called on strings and PRINTNAMES */
static BBOX RenderStr(const char *str, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX glyphBBox = NullBBox(); /* might be use do italic corr on str="" */
BBOX resultBBox = NullBBox();
int nc = 0;
cetype_t enc = (gc->fontface == 5) ? CE_SYMBOL : CE_NATIVE;
if (str) {
/* need to advance by character, not byte, except in the symbol font */
if(mbcslocale && gc->fontface != 5) {
size_t n = strlen(str), used;
wchar_t wc;
const char *p = str;
mbstate_t mb_st;
mbs_init(&mb_st);
while ((used = Mbrtowc(&wc, p, n, &mb_st)) > 0) {
/* On Windows could have sign extension here */
glyphBBox = GlyphBBox((unsigned int) wc, gc, dd);
resultBBox = CombineBBoxes(resultBBox, glyphBBox);
p += used; n -= used; nc++;
}
} else {
const char *s = str;
while (*s) {
/* Watch for sign extension here - fixed > 2.7.1 */
glyphBBox = GlyphBBox((unsigned char) *s, gc, dd);
resultBBox = CombineBBoxes(resultBBox, glyphBBox);
s++; nc++;
}
}
if(nc > 1) {
/* Finding the width by adding up boxes is incorrect (kerning) */
double wd = GEStrWidth(str, enc, gc, dd);
bboxWidth(resultBBox) = fromDeviceHeight(wd, MetricUnit, dd);
}
if (draw) {
GEText(ConvertedX(mc ,dd), ConvertedY(mc, dd), str, enc,
0.0, 0.0, mc->CurrentAngle, gc, dd);
PMoveAcross(bboxWidth(resultBBox), mc);
}
if (UsingItalics(gc))
bboxItalic(resultBBox) = ItalicFactor * bboxHeight(glyphBBox);
else
bboxItalic(resultBBox) = 0;
}
bboxSimple(resultBBox) = 1;
return resultBBox;
}
/* Code for Symbol Font Atoms */
static BBOX RenderSymbol(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
int code;
if ((code = TranslatedSymbol(expr)))
return RenderSymbolChar(code, draw, mc, gc, dd);
else
return RenderSymbolStr(CHAR(PRINTNAME(expr)), draw, mc, gc, dd);
}
static BBOX RenderSymbolString(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
int code;
if ((code = TranslatedSymbol(expr)))
return RenderSymbolChar(code, draw, mc, gc, dd);
else
return RenderStr(CHAR(PRINTNAME(expr)), draw, mc, gc, dd);
}
/* Code for Numeric Atoms */
static BBOX RenderNumber(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox;
FontType prevfont = SetFont(PlainFont, gc);
PrintDefaults();
bbox = RenderStr(CHAR(asChar(expr)), draw, mc, gc, dd);
SetFont(prevfont, gc);
return bbox;
}
/* Code for String Atoms */
static BBOX RenderString(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
return RenderStr(translateChar(STRING_ELT(expr, 0)), draw, mc, gc, dd);
}
/* Code for Ellipsis (ldots, cdots, ...) */
static int DotsAtom(SEXP expr)
{
if (NameMatch(expr, "cdots") ||
NameMatch(expr, "...") ||
NameMatch(expr, "ldots"))
return 1;
return 0;
}
static BBOX RenderDots(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox = RenderSymbolChar(S_ELLIPSIS, 0, mc, gc, dd);
if (NameMatch(expr, "cdots") || NameMatch(expr, "...")) {
double shift = AxisHeight(gc, dd) - 0.5 * bboxHeight(bbox);
if (draw) {
PMoveUp(shift, mc);
RenderSymbolChar(S_ELLIPSIS, 1, mc, gc, dd);
PMoveUp(-shift, mc);
}
return ShiftBBox(bbox, shift);
}
else {
if (draw)
RenderSymbolChar(S_ELLIPSIS, 1, mc, gc, dd);
return bbox;
}
}
/*----------------------------------------------------------------------
*
* Code for Atoms
*
*/
static BBOX RenderAtom(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
if (NameAtom(expr)) {
if (DotsAtom(expr))
return RenderDots(expr, draw, mc, gc, dd);
else
return RenderSymbol(expr, draw, mc, gc, dd);
}
else if (NumberAtom(expr))
return RenderNumber(expr, draw, mc, gc, dd);
else if (StringAtom(expr))
return RenderString(expr, draw, mc, gc, dd);
return NullBBox(); /* -Wall */
}
/*----------------------------------------------------------------------
*
* Code for Binary / Unary Operators (~, +, -, ... )
*
* Note that there are unary and binary ~ s.
*
*/
static int SpaceAtom(SEXP expr)
{
return NameAtom(expr) && NameMatch(expr, "~");
}
static BBOX RenderSpace(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX opBBox, arg1BBox, arg2BBox;
int nexpr = length(expr);
if (nexpr == 2) {
opBBox = RenderSymbolChar(' ', draw, mc, gc, dd);
arg1BBox = RenderElement(CADR(expr), draw, mc, gc, dd);
return CombineBBoxes(opBBox, arg1BBox);
}
else if (nexpr == 3) {
arg1BBox = RenderElement(CADR(expr), draw, mc, gc, dd);
opBBox = RenderSymbolChar(' ', draw, mc, gc, dd);
arg2BBox = RenderElement(CADDR(expr), draw, mc, gc, dd);
opBBox = CombineBBoxes(arg1BBox, opBBox);
opBBox = CombineBBoxes(opBBox, arg2BBox);
return opBBox;
}
else
error(_("invalid mathematical annotation"));
return NullBBox(); /* -Wall */
}
static SymTab BinTable[] = {
{ "!", 041 },
{ "*", 052 }, /* Binary Operators */
{ "+", 053 },
{ "-", 055 },
{ "/", 057 },
{ ":", 072 },
{ "%+-%", 0261 },
{ "%*%", 0264 },
{ "%/%", 0270 },
{ "%intersection%", 0307 },
{ "%union%", 0310 },
{ "%.%", 0327 }, /* cdot or dotmath */
{ NULL, 0 }
};
static int BinAtom(SEXP expr)
{
int i;
for (i = 0; BinTable[i].code; i++)
if (NameMatch(expr, BinTable[i].name))
return BinTable[i].code;
return 0;
}
static BBOX RenderSlash(int draw, mathContext *mc, pGEcontext gc,
pGEDevDesc dd)
{
/* Line Drawing Version */
double x[2], y[2];
double depth = 0.5 * TeX(sigma22, gc, dd);
double height = XHeight(gc, dd) + 0.5 * TeX(sigma22, gc, dd);
double width = 0.5 * xHeight(gc, dd);
if (draw) {
int savedlty = gc->lty;
double savedlwd = gc->lwd;
PMoveAcross(0.5 * width, mc);
PMoveUp(-depth, mc);
x[0] = ConvertedX(mc, dd);
y[0] = ConvertedY(mc, dd);
PMoveAcross(width, mc);
PMoveUp(depth + height, mc);
x[1] = ConvertedX(mc, dd);
y[1] = ConvertedY(mc, dd);
PMoveUp(-height, mc);
gc->lty = LTY_SOLID;
if (gc->lwd > 1)
gc->lwd = 1;
GEPolyline(2, x, y, gc, dd);
PMoveAcross(0.5 * width, mc);
gc->lty = savedlty;
gc->lwd = savedlwd;
}
return MakeBBox(height, depth, 2 * width);
}
static BBOX RenderBin(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
int op = BinAtom(CAR(expr));
int nexpr = length(expr);
BBOX bbox;
double gap;
if(nexpr == 3) {
if (op == S_ASTERISKMATH) {
bbox = RenderElement(CADR(expr), draw, mc, gc, dd);
bbox = RenderItalicCorr(bbox, draw, mc, gc, dd);
return CombineBBoxes(bbox, RenderElement(CADDR(expr), draw,
mc, gc, dd));
}
else if (op == S_SLASH) {
gap = 0;
bbox = RenderElement(CADR(expr), draw, mc, gc, dd);
bbox = RenderItalicCorr(bbox, draw, mc, gc, dd);
bbox = CombineBBoxes(bbox, RenderGap(gap, draw, mc, gc, dd));
bbox = CombineBBoxes(bbox, RenderSlash(draw, mc, gc, dd));
bbox = CombineBBoxes(bbox, RenderGap(gap, draw, mc, gc, dd));
return CombineBBoxes(bbox, RenderElement(CADDR(expr), draw,
mc, gc, dd));
}
else {
gap = (mc->CurrentStyle > STYLE_S) ? MediumSpace(gc, dd) : 0;
bbox = RenderElement(CADR(expr), draw, mc, gc, dd);
bbox = RenderItalicCorr(bbox, draw, mc, gc, dd);
bbox = CombineBBoxes(bbox, RenderGap(gap, draw, mc, gc, dd));
bbox = CombineBBoxes(bbox, RenderSymbolChar(op, draw, mc, gc, dd));
bbox = CombineBBoxes(bbox, RenderGap(gap, draw, mc, gc, dd));
return CombineBBoxes(bbox, RenderElement(CADDR(expr), draw,
mc, gc, dd));
}
}
else if(nexpr == 2) {
gap = (mc->CurrentStyle > STYLE_S) ? ThinSpace(gc, dd) : 0;
bbox = RenderSymbolChar(op, draw, mc, gc, dd);
bbox = CombineBBoxes(bbox, RenderGap(gap, draw, mc, gc, dd));
return CombineBBoxes(bbox, RenderElement(CADR(expr), draw, mc,
gc, dd));
}
else
error(_("invalid mathematical annotation"));
return NullBBox(); /* -Wall */
}
/*----------------------------------------------------------------------
*
* Code for Subscript and Superscipt Expressions
*
* Rules 18, 18a, ..., 18f of the TeXBook.
*
*/
static int SuperAtom(SEXP expr)
{
return NameAtom(expr) && NameMatch(expr, "^");
}
static int SubAtom(SEXP expr)
{
return NameAtom(expr) && NameMatch(expr, "[");
}
/* Note : If all computations are correct */
/* We do not need to save and restore the */
/* current location here. This is paranoia. */
static BBOX RenderSub(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bodyBBox, subBBox;
SEXP body = CADR(expr);
SEXP sub = CADDR(expr);
STYLE style = GetStyle(mc);
double savedX = mc->CurrentX;
double savedY = mc->CurrentY;
double v, s16;
bodyBBox = RenderElement(body, draw, mc, gc, dd);
bodyBBox = RenderItalicCorr(bodyBBox, draw, mc, gc, dd);
v = bboxSimple(bodyBBox) ? 0 : bboxDepth(bodyBBox) + TeX(sigma19, gc, dd);
s16 = TeX(sigma16, gc, dd);
SetSubStyle(style, mc, gc);
subBBox = RenderElement(sub, 0, mc, gc, dd);
v = max(max(v, s16), bboxHeight(subBBox) - 0.8 * sigma5);
subBBox = RenderOffsetElement(sub, 0, -v, draw, mc, gc, dd);
bodyBBox = CombineBBoxes(bodyBBox, subBBox);
SetStyle(style, mc, gc);
if (draw)
PMoveTo(savedX + bboxWidth(bodyBBox), savedY, mc);
return bodyBBox;
}
static BBOX RenderSup(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bodyBBox, subBBox, supBBox;
SEXP body = CADR(expr);
SEXP sup = CADDR(expr);
SEXP sub = R_NilValue; /* -Wall */
STYLE style = GetStyle(mc);
double savedX = mc->CurrentX;
double savedY = mc->CurrentY;
double theta, delta, width;
double u, p;
double v, s5, s17;
int haveSub;
if (FormulaExpression(body) && SubAtom(CAR(body))) {
sub = CADDR(body);
body = CADR(body);
haveSub = 1;
}
else haveSub = 0;
bodyBBox = RenderElement(body, draw, mc, gc, dd);
delta = bboxItalic(bodyBBox);
bodyBBox = RenderItalicCorr(bodyBBox, draw, mc, gc, dd);
width = bboxWidth(bodyBBox);
if (bboxSimple(bodyBBox)) {
u = 0;
v = 0;
}
else {
u = bboxHeight(bodyBBox) - TeX(sigma18, gc, dd);
v = bboxDepth(bodyBBox) + TeX(sigma19, gc, dd);
}
theta = TeX(xi8, gc, dd);
s5 = TeX(sigma5, gc, dd);
s17 = TeX(sigma17, gc, dd);
if (style == STYLE_D)
p = TeX(sigma13, gc, dd);
else if (IsCompactStyle(style, mc, gc))
p = TeX(sigma15, gc, dd);
else
p = TeX(sigma14, gc, dd);
SetSupStyle(style, mc, gc);
supBBox = RenderElement(sup, 0, mc, gc, dd);
u = max(max(u, p), bboxDepth(supBBox) + 0.25 * s5);
if (haveSub) {
SetSubStyle(style, mc, gc);
subBBox = RenderElement(sub, 0, mc, gc, dd);
v = max(v, s17);
if ((u - bboxDepth(supBBox)) - (bboxHeight(subBBox) - v) < 4 * theta) {
double psi = 0.8 * s5 - (u - bboxDepth(supBBox));
if (psi > 0) {
u += psi;
v -= psi;
}
}
if (draw)
PMoveTo(savedX, savedY, mc);
subBBox = RenderOffsetElement(sub, width, -v, draw, mc, gc, dd);
if (draw)
PMoveTo(savedX, savedY, mc);
SetSupStyle(style, mc, gc);
supBBox = RenderOffsetElement(sup, width + delta, u, draw, mc, gc, dd);
bodyBBox = CombineAlignedBBoxes(bodyBBox, subBBox);
bodyBBox = CombineAlignedBBoxes(bodyBBox, supBBox);
}
else {
supBBox = RenderOffsetElement(sup, 0, u, draw, mc, gc, dd);
bodyBBox = CombineBBoxes(bodyBBox, supBBox);
}
if (draw)
PMoveTo(savedX + bboxWidth(bodyBBox), savedY, mc);
SetStyle(style, mc, gc);
return bodyBBox;
}
/*----------------------------------------------------------------------
*
* Code for Accented Expressions (widehat, bar, widetilde, ...)
*
*/
#define ACCENT_GAP 0.2
#define HAT_HEIGHT 0.3
#define NTILDE 8
#define DELTA 0.05
static int WideTildeAtom(SEXP expr)
{
return NameAtom(expr) && NameMatch(expr, "widetilde");
}
static BBOX RenderWideTilde(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
double savedX = mc->CurrentX;
double savedY = mc->CurrentY;
BBOX bbox = RenderElement(CADR(expr), draw, mc, gc, dd);
double height = bboxHeight(bbox);
/*double width = bboxWidth(bbox);*/
double totalwidth = bboxWidth(bbox) + bboxItalic(bbox);
double delta = totalwidth * (1 - 2 * DELTA) / NTILDE;
double start = DELTA * totalwidth;
double accentGap = ACCENT_GAP * XHeight(gc, dd);
double hatHeight = 0.5 * HAT_HEIGHT * XHeight(gc, dd);
double c = M_2PI / NTILDE;
double x[NTILDE + 3], y[NTILDE + 3];
double baseX, baseY, xval, yval;
int i;
if (draw) {
int savedlty = gc->lty;
double savedlwd = gc->lwd;
baseX = savedX;
baseY = savedY + height + accentGap;
PMoveTo(baseX, baseY, mc);
x[0] = ConvertedX(mc, dd);
y[0] = ConvertedY(mc, dd);
for (i = 0; i <= NTILDE; i++) {
xval = start + i * delta;
yval = 0.5 * hatHeight * (sin(c * i) + 1);
PMoveTo(baseX + xval, baseY + yval, mc);
x[i + 1] = ConvertedX(mc, dd);
y[i + 1] = ConvertedY(mc, dd);
}
PMoveTo(baseX + totalwidth, baseY + hatHeight, mc);
x[NTILDE + 2] = ConvertedX(mc, dd);
y[NTILDE + 2] = ConvertedY(mc, dd);
gc->lty = LTY_SOLID;
if (gc->lwd > 1)
gc->lwd = 1;
GEPolyline(NTILDE + 3, x, y, gc, dd);
PMoveTo(savedX + totalwidth, savedY, mc);
gc->lty = savedlty;
gc->lwd = savedlwd;
}
return MakeBBox(height + accentGap + hatHeight,
bboxDepth(bbox), totalwidth);
}
static int WideHatAtom(SEXP expr)
{
return NameAtom(expr) && NameMatch(expr, "widehat");
}
static BBOX RenderWideHat(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
double savedX = mc->CurrentX;
double savedY = mc->CurrentY;
BBOX bbox = RenderElement(CADR(expr), draw, mc, gc, dd);
double accentGap = ACCENT_GAP * XHeight(gc, dd);
double hatHeight = HAT_HEIGHT * XHeight(gc, dd);
double totalwidth = bboxWidth(bbox) + bboxItalic(bbox);
double height = bboxHeight(bbox);
double width = bboxWidth(bbox);
double x[3], y[3];
if (draw) {
int savedlty = gc->lty;
double savedlwd = gc->lwd;
PMoveTo(savedX, savedY + height + accentGap, mc);
x[0] = ConvertedX(mc, dd);
y[0] = ConvertedY(mc, dd);
PMoveAcross(0.5 * totalwidth, mc);
PMoveUp(hatHeight, mc);
x[1] = ConvertedX(mc, dd);
y[1] = ConvertedY(mc, dd);
PMoveAcross(0.5 * totalwidth, mc);
PMoveUp(-hatHeight, mc);
x[2] = ConvertedX(mc, dd);
y[2] = ConvertedY(mc, dd);
gc->lty = LTY_SOLID;
if (gc->lwd > 1)
gc->lwd = 1;
GEPolyline(3, x, y, gc, dd);
PMoveTo(savedX + width, savedY, mc);
gc->lty = savedlty;
gc->lwd = savedlwd;
}
return EnlargeBBox(bbox, accentGap + hatHeight, 0, 0);
}
static int BarAtom(SEXP expr)
{
return NameAtom(expr) && NameMatch(expr, "bar");
}
static BBOX RenderBar(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
double savedX = mc->CurrentX;
double savedY = mc->CurrentY;
BBOX bbox = RenderElement(CADR(expr), draw, mc, gc, dd);
double accentGap = ACCENT_GAP * XHeight(gc, dd);
/*double hatHeight = HAT_HEIGHT * XHeight(gc, dd);*/
double height = bboxHeight(bbox);
double width = bboxWidth(bbox);
double offset = bboxItalic(bbox);
double x[2], y[2];
if (draw) {
int savedlty = gc->lty;
double savedlwd = gc->lwd;
PMoveTo(savedX + offset, savedY + height + accentGap, mc);
x[0] = ConvertedX(mc, dd);
y[0] = ConvertedY(mc, dd);
PMoveAcross(width, mc);
x[1] = ConvertedX(mc, dd);
y[1] = ConvertedY(mc, dd);
gc->lty = LTY_SOLID;
if (gc->lwd > 1)
gc->lwd = 1;
GEPolyline(2, x, y, gc, dd);
PMoveTo(savedX + width, savedY, mc);
gc->lty = savedlty;
gc->lwd = savedlwd;
}
return EnlargeBBox(bbox, accentGap, 0, 0);
}
static struct {
char *name;
int code;
}
AccentTable[] = {
{ "hat", 94 },
{ "ring", 176 },
{ "tilde", 126 },
{ "dot", 215 },
{ NULL, 0 },
};
static int AccentCode(SEXP expr)
{
int i;
for (i = 0; AccentTable[i].code; i++)
if (NameMatch(expr, AccentTable[i].name))
return AccentTable[i].code;
return 0;
}
static int AccentAtom(SEXP expr)
{
return NameAtom(expr) && (AccentCode(expr) != 0);
}
static void NORET InvalidAccent(SEXP expr)
{
errorcall(expr, _("invalid accent"));
}
static BBOX RenderAccent(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
SEXP body, accent;
double savedX = mc->CurrentX;
double savedY = mc->CurrentY;
BBOX bodyBBox, accentBBox;
double xoffset, yoffset, width, italic;
int code;
if (length(expr) != 2)
InvalidAccent(expr);
accent = CAR(expr);
body = CADR(expr);
code = AccentCode(accent);
if (code == 0)
InvalidAccent(expr);
bodyBBox = RenderElement(body, 0, mc, gc, dd);
italic = bboxItalic(bodyBBox);
if (code == 176 || /* ring (as degree) */
code == 215) /* dotmath */
accentBBox = RenderSymbolChar(code, 0, mc, gc, dd);
else
accentBBox = RenderChar(code, 0, mc, gc, dd);
width = max(bboxWidth(bodyBBox) + bboxItalic(bodyBBox),
bboxWidth(accentBBox));
xoffset = 0.5 *(width - bboxWidth(bodyBBox));
bodyBBox = RenderGap(xoffset, draw, mc, gc, dd);
bodyBBox = CombineBBoxes(bodyBBox, RenderElement(body, draw, mc, gc, dd));
bodyBBox = CombineBBoxes(bodyBBox, RenderGap(xoffset, draw, mc, gc, dd));
PMoveTo(savedX, savedY, mc);
xoffset = 0.5 *(width - bboxWidth(accentBBox))
+ 0.9 * italic;
yoffset = bboxHeight(bodyBBox) + bboxDepth(accentBBox) +
0.1 * XHeight(gc, dd);
if (draw) {
PMoveTo(savedX + xoffset, savedY + yoffset, mc);
if (code == 176 || /* ring (as degree) */
code == 215) /* dotmath */
RenderSymbolChar(code, draw, mc, gc, dd);
else
RenderChar(code, draw, mc, gc, dd);
}
bodyBBox = CombineOffsetBBoxes(bodyBBox, 0, accentBBox, 0,
xoffset, yoffset);
if (draw)
PMoveTo(savedX + width, savedY, mc);
return bodyBBox;
}
/*----------------------------------------------------------------------
*
* Code for Fraction Expressions (over, atop)
*
* Rules 15, 15a, ..., 15e of the TeXBook
*
*/
static void NumDenomVShift(BBOX numBBox, BBOX denomBBox,
double *u, double *v,
mathContext *mc, pGEcontext gc, pGEDevDesc dd)
{
double a, delta, phi, theta;
a = TeX(sigma22, gc, dd);
theta = TeX(xi8, gc, dd);
if(mc->CurrentStyle > STYLE_T) {
*u = TeX(sigma8, gc, dd);
*v = TeX(sigma11, gc, dd);
phi = 3 * theta;
}
else {
*u = TeX(sigma9, gc, dd);
*v = TeX(sigma12, gc, dd);
phi = theta;
}
delta = (*u - bboxDepth(numBBox)) - (a + 0.5 * theta);
/*
* Numerators and denominators on fractions appear too far from
* horizontal bar.
* Reread of Knuth suggests removing "+ theta" components below.
*/
if (delta < phi)
*u += (phi - delta); /* + theta; */
delta = (a + 0.5 * theta) - (bboxHeight(denomBBox) - *v);
if (delta < phi)
*v += (phi - delta); /* + theta; */
}
static void NumDenomHShift(BBOX numBBox, BBOX denomBBox,
double *numShift, double *denomShift)
{
double numWidth = bboxWidth(numBBox);
double denomWidth = bboxWidth(denomBBox);
if (numWidth > denomWidth) {
*numShift = 0;
*denomShift = (numWidth - denomWidth) / 2;
}
else {
*numShift = (denomWidth - numWidth) / 2;
*denomShift = 0;
}
}
static BBOX RenderFraction(SEXP expr, int rule, int draw,
mathContext *mc, pGEcontext gc, pGEDevDesc dd)
{
SEXP numerator = CADR(expr);
SEXP denominator = CADDR(expr);
BBOX numBBox, denomBBox;
double nHShift, dHShift;
double nVShift, dVShift;
double width, x[2], y[2];
double savedX = mc->CurrentX;
double savedY = mc->CurrentY;
STYLE style;
style = GetStyle(mc);
SetNumStyle(style, mc, gc);
numBBox = RenderItalicCorr(RenderElement(numerator, 0, mc, gc, dd), 0,
mc, gc, dd);
SetDenomStyle(style, mc, gc);
denomBBox = RenderItalicCorr(RenderElement(denominator, 0, mc, gc, dd), 0,
mc, gc, dd);
SetStyle(style, mc, gc);
width = max(bboxWidth(numBBox), bboxWidth(denomBBox));
NumDenomHShift(numBBox, denomBBox, &nHShift, &dHShift);
NumDenomVShift(numBBox, denomBBox, &nVShift, &dVShift, mc, gc, dd);
mc->CurrentX = savedX;
mc->CurrentY = savedY;
SetNumStyle(style, mc, gc);
numBBox = RenderOffsetElement(numerator, nHShift, nVShift, draw, mc,
gc, dd);
mc->CurrentX = savedX;
mc->CurrentY = savedY;
SetDenomStyle(style, mc, gc);
denomBBox = RenderOffsetElement(denominator, dHShift, -dVShift, draw,
mc, gc, dd);
SetStyle(style, mc, gc);
if (draw) {
if (rule) {
int savedlty = gc->lty;
double savedlwd = gc->lwd;
mc->CurrentX = savedX;
mc->CurrentY = savedY;
PMoveUp(AxisHeight(gc, dd), mc);
x[0] = ConvertedX(mc, dd);
y[0] = ConvertedY(mc, dd);
PMoveAcross(width, mc);
x[1] = ConvertedX(mc, dd);
y[1] = ConvertedY(mc, dd);
gc->lty = LTY_SOLID;
if (gc->lwd > 1)
gc->lwd = 1;
GEPolyline(2, x, y, gc, dd);
PMoveUp(-AxisHeight(gc, dd), mc);
gc->lty = savedlty;
gc->lwd = savedlwd;
}
PMoveTo(savedX + width, savedY, mc);
}
return CombineAlignedBBoxes(numBBox, denomBBox);
}
static BBOX RenderUnderline(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
SEXP body = CADR(expr);
BBOX BBox;
double width, adepth, depth, x[2], y[2];
double savedX = mc->CurrentX;
double savedY = mc->CurrentY;
BBox = RenderItalicCorr(RenderElement(body, 0, mc, gc, dd), 0, mc, gc, dd);
width = bboxWidth(BBox);
mc->CurrentX = savedX;
mc->CurrentY = savedY;
BBox = RenderElement(body, draw, mc, gc, dd);
adepth = 0.1 * XHeight(gc, dd);
depth = bboxDepth(BBox) + adepth;
if (draw) {
int savedlty = gc->lty;
double savedlwd = gc->lwd;
mc->CurrentX = savedX;
mc->CurrentY = savedY;
PMoveUp(-depth, mc);
x[0] = ConvertedX(mc, dd);
y[0] = ConvertedY(mc, dd);
PMoveAcross(width, mc);
x[1] = ConvertedX(mc, dd);
y[1] = ConvertedY(mc, dd);
gc->lty = LTY_SOLID;
if (gc->lwd > 1)
gc->lwd = 1;
GEPolyline(2, x, y, gc, dd);
PMoveUp(depth, mc);
gc->lty = savedlty;
gc->lwd = savedlwd;
PMoveTo(savedX + width, savedY, mc);
}
return EnlargeBBox(BBox, 0.0, adepth, 0.0);
}
static int OverAtom(SEXP expr)
{
return NameAtom(expr) &&
(NameMatch(expr, "over") || NameMatch(expr, "frac"));
}
static BBOX RenderOver(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
return RenderFraction(expr, 1, draw, mc, gc, dd);
}
static int UnderlAtom(SEXP expr)
{
return NameAtom(expr) && NameMatch(expr, "underline");
}
static BBOX RenderUnderl(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
return RenderUnderline(expr, draw, mc, gc, dd);
}
static int AtopAtom(SEXP expr)
{
return NameAtom(expr) && NameMatch(expr, "atop");
}
static BBOX RenderAtop(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
return RenderFraction(expr, 0, draw, mc, gc, dd);
}
/*----------------------------------------------------------------------
*
* Code for Grouped Expressions (e.g. ( ... ))
*
* group(ldelim, body, rdelim)
*
* bgroup(ldelim, body, rdelim)
*
*/
#define DelimSymbolMag 1.25
static int DelimCode(SEXP expr, SEXP head)
{
int code = 0;
if (NameAtom(head)) {
if (NameMatch(head, "lfloor"))
code = S_BRACKETLEFTBT;
else if (NameMatch(head, "rfloor"))
code = S_BRACKETRIGHTBT;
if (NameMatch(head, "lceil"))
code = S_BRACKETLEFTTP;
else if (NameMatch(head, "rceil"))
code = S_BRACKETRIGHTTP;
}
else if (StringAtom(head) && length(head) > 0) {
if (StringMatch(head, "|"))
code = '|';
else if (StringMatch(head, "||")) // historical anomaly
code = '|';
else if (StringMatch(head, "("))
code = '(';
else if (StringMatch(head, ")"))
code = ')';
else if (StringMatch(head, "["))
code = '[';
else if (StringMatch(head, "]"))
code = ']';
else if (StringMatch(head, "{"))
code = '{';
else if (StringMatch(head, "}"))
code = '}';
else if (StringMatch(head, "") || StringMatch(head, "."))
code = '.';
}
if (code == 0)
errorcall(expr, _("invalid group delimiter"));
return code;
}
static BBOX RenderDelimiter(int delim, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox;
double savecex = gc->cex;
gc->cex = DelimSymbolMag * gc->cex;
bbox = RenderSymbolChar(delim, draw, mc, gc, dd);
gc->cex = savecex;
return bbox;
}
static int GroupAtom(SEXP expr)
{
return NameAtom(expr) && NameMatch(expr, "group");
}
static BBOX RenderGroup(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
double cexSaved = gc->cex;
BBOX bbox;
int code;
if (length(expr) != 4)
errorcall(expr, _("invalid group specification"));
bbox = NullBBox();
code = DelimCode(expr, CADR(expr));
gc->cex = DelimSymbolMag * gc->cex;
if (code != '.')
bbox = RenderSymbolChar(code, draw, mc, gc, dd);
gc->cex = cexSaved;
bbox = CombineBBoxes(bbox, RenderElement(CADDR(expr), draw, mc, gc, dd));
bbox = RenderItalicCorr(bbox, draw, mc, gc, dd);
code = DelimCode(expr, CADDDR(expr));
gc->cex = DelimSymbolMag * gc->cex;
if (code != '.')
bbox = CombineBBoxes(bbox, RenderSymbolChar(code, draw, mc, gc, dd));
gc->cex = cexSaved;
return bbox;
}
static int BGroupAtom(SEXP expr)
{
return NameAtom(expr) && NameMatch(expr, "bgroup");
}
static BBOX RenderDelim(int which, double dist, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
double savedX = mc->CurrentX;
double savedY = mc->CurrentY;
FontType prev = SetFont(SymbolFont, gc);
BBOX ansBBox, topBBox, botBBox, extBBox, midBBox;
int top, bot, ext, mid;
int i, n;
double topShift, botShift, extShift, midShift;
double ytop, ybot, extHeight, delta;
double axisHeight = TeX(sigma22, gc, dd);
switch(which) {
case '.':
SetFont(prev, gc);
return NullBBox();
break;
case '|':
top = 239; ext = 239; bot = 239; mid = 0;
break;
case '(':
top = 230; ext = 231; bot = 232; mid = 0;
break;
case ')':
top = 246; ext = 247; bot = 248; mid = 0;
break;
case '[':
top = 233; ext = 234; bot = 235; mid = 0;
break;
case ']':
top = 249; ext = 250; bot = 251; mid = 0;
break;
case '{':
top = 236; ext = 239; bot = 238; mid = 237;
break;
case '}':
top = 252; ext = 239; bot = 254; mid = 253;
break;
default:
error(_("group is incomplete"));
return NullBBox();/*never reached*/
}
topBBox = GlyphBBox(top, gc, dd);
extBBox = GlyphBBox(ext, gc, dd);
botBBox = GlyphBBox(bot, gc, dd);
if (which == '{' || which == '}') {
if (1.2 * (bboxHeight(topBBox) + bboxDepth(topBBox)) > dist)
dist = 1.2 * (bboxHeight(topBBox) + bboxDepth(botBBox));
}
else {
if (0.8 * (bboxHeight(topBBox) + bboxDepth(topBBox)) > dist)
dist = 0.8 * (bboxHeight(topBBox) + bboxDepth(topBBox));
}
extHeight = bboxHeight(extBBox) + bboxDepth(extBBox);
topShift = dist - bboxHeight(topBBox) + axisHeight;
botShift = dist - bboxDepth(botBBox) - axisHeight;
extShift = 0.5 * (bboxHeight(extBBox) - bboxDepth(extBBox));
topBBox = ShiftBBox(topBBox, topShift);
botBBox = ShiftBBox(botBBox, -botShift);
ansBBox = CombineAlignedBBoxes(topBBox, botBBox);
if (which == '{' || which == '}') {
midBBox = GlyphBBox(mid, gc, dd);
midShift = axisHeight
- 0.5 * (bboxHeight(midBBox) - bboxDepth(midBBox));
midBBox = ShiftBBox(midBBox, midShift);
ansBBox = CombineAlignedBBoxes(ansBBox, midBBox);
if (draw) {
PMoveTo(savedX, savedY + topShift, mc);
RenderSymbolChar(top, draw, mc, gc, dd);
PMoveTo(savedX, savedY + midShift, mc);
RenderSymbolChar(mid, draw, mc, gc, dd);
PMoveTo(savedX, savedY - botShift, mc);
RenderSymbolChar(bot, draw, mc, gc, dd);
PMoveTo(savedX + bboxWidth(ansBBox), savedY, mc);
}
}
else {
if (draw) {
/* draw the top and bottom elements */
PMoveTo(savedX, savedY + topShift, mc);
RenderSymbolChar(top, draw, mc, gc, dd);
PMoveTo(savedX, savedY - botShift, mc);
RenderSymbolChar(bot, draw, mc, gc, dd);
/* now join with extenders */
ytop = axisHeight + dist
- (bboxHeight(topBBox) + bboxDepth(topBBox));
ybot = axisHeight - dist
+ (bboxHeight(botBBox) + bboxDepth(botBBox));
n = (int) ceil((ytop - ybot) / (0.99 * extHeight));
if (n > 0) {
delta = (ytop - ybot) / n;
for (i = 0; i < n; i++) {
PMoveTo(savedX, savedY + ybot +
(i + 0.5) * delta - extShift, mc);
RenderSymbolChar(ext, draw, mc, gc, dd);
}
}
PMoveTo(savedX + bboxWidth(ansBBox), savedY, mc);
}
}
SetFont(prev, gc);
return ansBBox;
}
static BBOX RenderBGroup(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
double dist;
BBOX bbox;
double axisHeight = TeX(sigma22, gc, dd);
double extra = 0.2 * xHeight(gc, dd);
int delim1, delim2;
if (length(expr) != 4)
errorcall(expr, _("invalid group specification"));
bbox = NullBBox();
delim1 = DelimCode(expr, CADR(expr));
delim2 = DelimCode(expr, CADDDR(expr));
bbox = RenderElement(CADDR(expr), 0, mc, gc, dd);
dist = max(bboxHeight(bbox) - axisHeight, bboxDepth(bbox) + axisHeight);
bbox = RenderDelim(delim1, dist + extra, draw, mc, gc, dd);
bbox = CombineBBoxes(bbox, RenderElement(CADDR(expr), draw, mc, gc, dd));
bbox = RenderItalicCorr(bbox, draw, mc, gc, dd);
bbox = CombineBBoxes(bbox, RenderDelim(delim2, dist + extra, draw, mc,
gc, dd));
return bbox;
}
/*----------------------------------------------------------------------
*
* Code for Parenthetic Expressions (i.e. ( ... ))
*
*/
static int ParenAtom(SEXP expr)
{
return NameAtom(expr) && NameMatch(expr, "(");
}
static BBOX RenderParen(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox;
bbox = RenderDelimiter(S_PARENLEFT, draw, mc, gc, dd);
bbox = CombineBBoxes(bbox, RenderElement(CADR(expr), draw, mc, gc, dd));
bbox = RenderItalicCorr(bbox, draw, mc, gc, dd);
return CombineBBoxes(bbox, RenderDelimiter(S_PARENRIGHT, draw, mc, gc, dd));
}
/*----------------------------------------------------------------------
*
* Code for Integral Operators.
*
*/
static int IntAtom(SEXP expr)
{
return NameAtom(expr) && NameMatch(expr, "integral");
}
static BBOX RenderIntSymbol(int draw, mathContext *mc, pGEcontext gc,
pGEDevDesc dd)
{
double savedX = mc->CurrentX;
double savedY = mc->CurrentY;
if (GetStyle(mc) > STYLE_T) {
BBOX bbox1 = RenderSymbolChar(243, 0, mc, gc, dd);
BBOX bbox2 = RenderSymbolChar(245, 0, mc, gc, dd);
double shift;
shift = TeX(sigma22, gc, dd) + 0.99 * bboxDepth(bbox1);
PMoveUp(shift, mc);
bbox1 = ShiftBBox(RenderSymbolChar(243, draw, mc, gc, dd), shift);
mc->CurrentX = savedX;
mc->CurrentY = savedY;
shift = TeX(sigma22, gc, dd) - 0.99 * bboxHeight(bbox2);
PMoveUp(shift, mc);
bbox2 = ShiftBBox(RenderSymbolChar(245, draw, mc, gc, dd), shift);
if (draw)
PMoveTo(savedX + max(bboxWidth(bbox1), bboxWidth(bbox2)),
savedY, mc);
else
PMoveTo(savedX, savedY, mc);
return CombineAlignedBBoxes(bbox1, bbox2);
}
else {
return RenderSymbolChar(0362, draw, mc, gc, dd);
}
}
static BBOX RenderInt(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX opBBox, lowerBBox, upperBBox, bodyBBox;
int nexpr = length(expr);
STYLE style = GetStyle(mc);
double savedX = mc->CurrentX;
double savedY = mc->CurrentY;
double hshift, vshift, width;
opBBox = RenderIntSymbol(draw, mc, gc, dd);
width = bboxWidth(opBBox);
mc->CurrentX = savedX;
mc->CurrentY = savedY;
if (nexpr > 2) {
hshift = 0.5 * width + ThinSpace(gc, dd);
SetSubStyle(style, mc, gc);
lowerBBox = RenderElement(CADDR(expr), 0, mc, gc, dd);
vshift = bboxDepth(opBBox) + CenterShift(lowerBBox);
lowerBBox = RenderOffsetElement(CADDR(expr), hshift, -vshift, draw,
mc, gc, dd);
opBBox = CombineAlignedBBoxes(opBBox, lowerBBox);
SetStyle(style, mc, gc);
mc->CurrentX = savedX;
mc->CurrentY = savedY;
}
if (nexpr > 3) {
hshift = width + ThinSpace(gc, dd);
SetSupStyle(style, mc, gc);
upperBBox = RenderElement(CADDDR(expr), 0, mc, gc, dd);
vshift = bboxHeight(opBBox) - CenterShift(upperBBox);
upperBBox = RenderOffsetElement(CADDDR(expr), hshift, vshift, draw,
mc, gc, dd);
opBBox = CombineAlignedBBoxes(opBBox, upperBBox);
SetStyle(style, mc, gc);
mc->CurrentX = savedX;
mc->CurrentY = savedY;
}
PMoveAcross(bboxWidth(opBBox), mc);
if (nexpr > 1) {
bodyBBox = RenderElement(CADR(expr), draw, mc, gc, dd);
opBBox = CombineBBoxes(opBBox, bodyBBox);
}
return opBBox;
}
/*----------------------------------------------------------------------
*
* Code for Operator Expressions (sum, product, lim, inf, sup, ...)
*
*/
#define OperatorSymbolMag 1.25
static SymTab OpTable[] = {
{ "prod", S_PRODUCT },
{ "sum", S_SUM },
{ "union", S_UNION },
{ "intersect", S_INTERSECTION },
{ "lim", N_LIM },
{ "liminf", N_LIMINF },
{ "limsup", N_LIMINF },
{ "inf", N_INF },
{ "sup", N_SUP },
{ "min", N_MIN },
{ "max", N_MAX },
{ NULL, 0 }
};
static int OpAtom(SEXP expr)
{
int i;
for (i = 0; OpTable[i].code; i++)
if (NameMatch(expr, OpTable[i].name))
return OpTable[i].code;
return 0;
}
static BBOX RenderOpSymbol(SEXP op, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox;
double cexSaved = gc->cex;
/*double savedX = mc->CurrentX;*/
/*double savedY = mc->CurrentY;*/
double shift;
int display = (GetStyle(mc) > STYLE_T);
int opId = OpAtom(op);
if (opId == S_SUM || opId == S_PRODUCT ||
opId == S_UNION || opId == S_INTERSECTION) {
if (display) {
gc->cex = OperatorSymbolMag * gc->cex;
bbox = RenderSymbolChar(OpAtom(op), 0, mc, gc, dd);
shift = 0.5 * (bboxHeight(bbox) - bboxDepth(bbox)) -
TeX(sigma22, gc, dd);
if (draw) {
PMoveUp(-shift, mc);
bbox = RenderSymbolChar(opId, 1, mc, gc, dd);
PMoveUp(shift, mc);
}
gc->cex = cexSaved;
return ShiftBBox(bbox, -shift);
}
else return RenderSymbolChar(opId, draw, mc, gc, dd);
}
else {
FontType prevfont = SetFont(PlainFont, gc);
bbox = RenderStr(CHAR(PRINTNAME(op)), draw, mc, gc, dd);
SetFont(prevfont, gc);
return bbox;
}
}
static BBOX RenderOp(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX lowerBBox = NullBBox() /* -Wall */, upperBBox = NullBBox(), bodyBBox;
double savedX = mc->CurrentX;
double savedY = mc->CurrentY;
int nexpr = length(expr);
STYLE style = GetStyle(mc);
BBOX opBBox = RenderOpSymbol(CAR(expr), 0, mc, gc, dd);
double width = bboxWidth(opBBox);
double hshift, lvshift, uvshift;
lvshift = uvshift = 0; /* -Wall */
if (nexpr > 2) {
SetSubStyle(style, mc, gc);
lowerBBox = RenderElement(CADDR(expr), 0, mc, gc, dd);
SetStyle(style, mc, gc);
width = max(width, bboxWidth(lowerBBox));
lvshift = max(TeX(xi10, gc, dd), TeX(xi12, gc, dd) -
bboxHeight(lowerBBox));
lvshift = bboxDepth(opBBox) + bboxHeight(lowerBBox) + lvshift;
}
if (nexpr > 3) {
SetSupStyle(style, mc, gc);
upperBBox = RenderElement(CADDDR(expr), 0, mc, gc, dd);
SetStyle(style, mc, gc);
width = max(width, bboxWidth(upperBBox));
uvshift = max(TeX(xi9, gc, dd), TeX(xi11, gc, dd) -
bboxDepth(upperBBox));
uvshift = bboxHeight(opBBox) + bboxDepth(upperBBox) + uvshift;
}
hshift = 0.5 * (width - bboxWidth(opBBox));
opBBox = RenderGap(hshift, draw, mc, gc, dd);
opBBox = CombineBBoxes(opBBox,
RenderOpSymbol(CAR(expr), draw, mc, gc, dd));
mc->CurrentX = savedX;
mc->CurrentY = savedY;
if (nexpr > 2) {
SetSubStyle(style, mc, gc);
hshift = 0.5 * (width - bboxWidth(lowerBBox));
lowerBBox = RenderOffsetElement(CADDR(expr), hshift, -lvshift, draw,
mc, gc, dd);
SetStyle(style, mc, gc);
opBBox = CombineAlignedBBoxes(opBBox, lowerBBox);
mc->CurrentX = savedX;
mc->CurrentY = savedY;
}
if (nexpr > 3) {
SetSupStyle(style, mc, gc);
hshift = 0.5 * (width - bboxWidth(upperBBox));
upperBBox = RenderOffsetElement(CADDDR(expr), hshift, uvshift, draw,
mc, gc, dd);
SetStyle(style, mc, gc);
opBBox = CombineAlignedBBoxes(opBBox, upperBBox);
mc->CurrentX = savedX;
mc->CurrentY = savedY;
}
opBBox = EnlargeBBox(opBBox, TeX(xi13, gc, dd), TeX(xi13, gc, dd), 0);
if (draw)
PMoveAcross(width, mc);
opBBox = CombineBBoxes(opBBox,
RenderGap(ThinSpace(gc, dd), draw, mc, gc, dd));
bodyBBox = RenderElement(CADR(expr), draw, mc, gc, dd);
return CombineBBoxes(opBBox, bodyBBox);
}
/*----------------------------------------------------------------------
*
* Code for radical expressions (root, sqrt)
*
* Tunable parameteters :
*
* RADICAL_GAP The gap between the nucleus and the radical extension.
* RADICAL_SPACE Extra space to the left and right of the nucleus.
*
*/
#define RADICAL_GAP 0.4
#define RADICAL_SPACE 0.2
static int RadicalAtom(SEXP expr)
{
return NameAtom(expr) &&
(NameMatch(expr, "root") ||
NameMatch(expr, "sqrt"));
}
static BBOX RenderScript(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox;
STYLE style = GetStyle(mc);
SetSupStyle(style, mc, gc);
bbox = RenderElement(expr, draw, mc, gc, dd);
SetStyle(style, mc, gc);
return bbox;
}
static BBOX RenderRadical(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
SEXP body = CADR(expr);
SEXP order = CADDR(expr);
BBOX bodyBBox, orderBBox;
double radWidth, radHeight;
double leadWidth, leadHeight, twiddleHeight;
double hshift, vshift;
double radGap, radSpace, radTrail;
STYLE style = GetStyle(mc);
double savedX = mc->CurrentX;
double savedY = mc->CurrentY;
double x[5], y[5];
radGap = RADICAL_GAP * xHeight(gc, dd);
radSpace = RADICAL_SPACE * xHeight(gc, dd);
radTrail = MuSpace(gc, dd);
SetPrimeStyle(style, mc, gc);
bodyBBox = RenderElement(body, 0, mc, gc, dd);
bodyBBox = RenderItalicCorr(bodyBBox, 0, mc, gc, dd);
radWidth = 0.6 *XHeight(gc, dd);
radHeight = bboxHeight(bodyBBox) + radGap;
twiddleHeight = CenterShift(bodyBBox);
leadWidth = radWidth;
leadHeight = radHeight;
if (order != R_NilValue) {
SetSupStyle(style, mc, gc);
orderBBox = RenderScript(order, 0, mc, gc, dd);
leadWidth = max(leadWidth, bboxWidth(orderBBox) + 0.4 * radWidth);
hshift = leadWidth - bboxWidth(orderBBox) - 0.4 * radWidth;
vshift = leadHeight - bboxHeight(orderBBox);
if (vshift - bboxDepth(orderBBox) < twiddleHeight + radGap)
vshift = twiddleHeight + bboxDepth(orderBBox) + radGap;
if (draw) {
PMoveTo(savedX + hshift, savedY + vshift, mc);
orderBBox = RenderScript(order, draw, mc, gc, dd);
}
orderBBox = EnlargeBBox(orderBBox, vshift, 0, hshift);
}
else
orderBBox = NullBBox();
if (draw) {
int savedlty = gc->lty;
double savedlwd = gc->lwd;
PMoveTo(savedX + leadWidth - radWidth, savedY, mc);
PMoveUp(0.8 * twiddleHeight, mc);
x[0] = ConvertedX(mc, dd);
y[0] = ConvertedY(mc, dd);
PMoveUp(0.2 * twiddleHeight, mc);
PMoveAcross(0.3 * radWidth, mc);
x[1] = ConvertedX(mc, dd);
y[1] = ConvertedY(mc, dd);
PMoveUp(-(twiddleHeight + bboxDepth(bodyBBox)), mc);
PMoveAcross(0.3 * radWidth, mc);
x[2] = ConvertedX(mc, dd);
y[2] = ConvertedY(mc, dd);
PMoveUp(bboxDepth(bodyBBox) + bboxHeight(bodyBBox) + radGap, mc);
PMoveAcross(0.4 * radWidth, mc);
x[3] = ConvertedX(mc, dd);
y[3] = ConvertedY(mc, dd);
PMoveAcross(radSpace + bboxWidth(bodyBBox) + radTrail, mc);
x[4] = ConvertedX(mc, dd);
y[4] = ConvertedY(mc, dd);
gc->lty = LTY_SOLID;
if (gc->lwd > 1)
gc->lwd = 1;
GEPolyline(5, x, y, gc, dd);
PMoveTo(savedX, savedY, mc);
gc->lty = savedlty;
gc->lwd = savedlwd;
}
orderBBox =
CombineAlignedBBoxes(orderBBox,
RenderGap(leadWidth + radSpace, draw, mc, gc, dd));
SetPrimeStyle(style, mc, gc);
orderBBox = CombineBBoxes(orderBBox,
RenderElement(body, draw, mc, gc, dd));
orderBBox = CombineBBoxes(orderBBox,
RenderGap(2 * radTrail, draw, mc, gc, dd));
orderBBox = EnlargeBBox(orderBBox, radGap, 0, 0);/* << fixes PR#1101 */
SetStyle(style, mc, gc);
return orderBBox;
}
/*----------------------------------------------------------------------
*
* Code for Absolute Value Expressions (abs)
*
*/
static int AbsAtom(SEXP expr)
{
return NameAtom(expr) && NameMatch(expr, "abs");
}
static BBOX RenderAbs(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox = RenderElement(CADR(expr), 0, mc, gc, dd);
double height = bboxHeight(bbox);
double depth = bboxDepth(bbox);
double x[2], y[2];
bbox= RenderGap(MuSpace(gc, dd), draw, mc, gc, dd);
if (draw) {
int savedlty = gc->lty;
double savedlwd = gc->lwd;
PMoveUp(-depth, mc);
x[0] = ConvertedX(mc, dd);
y[0] = ConvertedY(mc, dd);
PMoveUp(depth + height, mc);
x[1] = ConvertedX(mc, dd);
y[1] = ConvertedY(mc, dd);
gc->lty = LTY_SOLID;
if (gc->lwd > 1)
gc->lwd = 1;
GEPolyline(2, x, y, gc, dd);
PMoveUp(-height, mc);
gc->lty = savedlty;
gc->lwd = savedlwd;
}
bbox = CombineBBoxes(bbox, RenderGap(MuSpace(gc, dd), draw, mc, gc, dd));
bbox = CombineBBoxes(bbox, RenderElement(CADR(expr), draw, mc, gc, dd));
bbox = RenderItalicCorr(bbox, draw, mc, gc, dd);
bbox = CombineBBoxes(bbox, RenderGap(MuSpace(gc, dd), draw, mc, gc, dd));
if (draw) {
int savedlty = gc->lty;
double savedlwd = gc->lwd;
PMoveUp(-depth, mc);
x[0] = ConvertedX(mc, dd);
y[0] = ConvertedY(mc, dd);
PMoveUp(depth + height, mc);
x[1] = ConvertedX(mc, dd);
y[1] = ConvertedY(mc, dd);
gc->lty = LTY_SOLID;
if (gc->lwd > 1)
gc->lwd = 1;
GEPolyline(2, x, y, gc, dd);
PMoveUp(-height, mc);
gc->lty = savedlty;
gc->lwd = savedlwd;
}
bbox = CombineBBoxes(bbox, RenderGap(MuSpace(gc, dd), draw, mc, gc, dd));
return bbox;
}
/*----------------------------------------------------------------------
*
* Code for Grouped Expressions (i.e. { ... } )
*
*/
static int CurlyAtom(SEXP expr)
{
return NameAtom(expr) &&
NameMatch(expr, "{");
}
static BBOX RenderCurly(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
return RenderElement(CADR(expr), draw, mc, gc, dd);
}
/*----------------------------------------------------------------------
*
* Code for Relation Expressions (i.e. ... ==, !=, ...)
*
*/
/* Binary Relationships */
static
SymTab RelTable[] = {
{ "<", 60 }, /* less */
{ "==", 61 }, /* equal */
{ ">", 62 }, /* greater */
{ "%=~%", 64 }, /* congruent */
{ "!=", 185 }, /* not equal */
{ "<=", 163 }, /* less or equal */
{ ">=", 179 }, /* greater or equal */
{ "%==%", 186 }, /* equivalence */
{ "%~~%", 187 }, /* approxequal */
{ "%prop%", 181 }, /* proportional to */
{ "%~%", 126 }, /* distributed as */
{ "%<->%", 171 }, /* Arrows */
{ "%<-%", 172 },
{ "%up%", 173 },
{ "%->%", 174 },
{ "%down%", 175 },
{ "%<=>%", 219 },
{ "%<=%", 220 },
{ "%dblup%", 221 },
{ "%=>%", 222 },
{ "%dbldown%", 223 },
{ "%supset%", 201 }, /* Sets (TeX Names) */
{ "%supseteq%", 202 },
{ "%notsubset%", 203 },
{ "%subset%", 204 },
{ "%subseteq%", 205 },
{ "%in%", 206 },
{ "%notin%", 207 },
{ NULL, 0 },
};
static int RelAtom(SEXP expr)
{
int i;
for (i = 0; RelTable[i].code; i++)
if (NameMatch(expr, RelTable[i].name))
return RelTable[i].code;
return 0;
}
static BBOX RenderRel(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
int op = RelAtom(CAR(expr));
int nexpr = length(expr);
BBOX bbox;
double gap;
if(nexpr == 3) {
gap = (mc->CurrentStyle > STYLE_S) ? ThickSpace(gc, dd) : 0;
bbox = RenderElement(CADR(expr), draw, mc, gc, dd);
bbox = RenderItalicCorr(bbox, draw, mc, gc, dd);
bbox = CombineBBoxes(bbox, RenderGap(gap, draw, mc, gc, dd));
bbox = CombineBBoxes(bbox, RenderSymbolChar(op, draw, mc, gc, dd));
bbox = CombineBBoxes(bbox, RenderGap(gap, draw, mc, gc, dd));
return
CombineBBoxes(bbox, RenderElement(CADDR(expr), draw, mc, gc, dd));
}
else error(_("invalid mathematical annotation"));
return NullBBox(); /* -Wall */
}
/*----------------------------------------------------------------------
*
* Code for Boldface Expressions
*
*/
static int BoldAtom(SEXP expr)
{
return NameAtom(expr) &&
NameMatch(expr, "bold");
}
static BBOX RenderBold(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox;
FontType prevfont = SetFont(BoldFont, gc);
bbox = RenderElement(CADR(expr), draw, mc, gc, dd);
SetFont(prevfont, gc);
return bbox;
}
/*----------------------------------------------------------------------
*
* Code for Italic Expressions
*
*/
static int ItalicAtom(SEXP expr)
{
return NameAtom(expr) &&
(NameMatch(expr, "italic") || NameMatch(expr, "math"));
}
static BBOX RenderItalic(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox;
FontType prevfont = SetFont(ItalicFont, gc);
bbox = RenderElement(CADR(expr), draw, mc, gc, dd);
SetFont(prevfont, gc);
return bbox;
}
/*----------------------------------------------------------------------
*
* Code for Plain (i.e. Roman) Expressions
*
*/
static int PlainAtom(SEXP expr)
{
return NameAtom(expr) &&
NameMatch(expr, "plain");
}
static BBOX RenderPlain(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox;
int prevfont = SetFont(PlainFont, gc);
bbox = RenderElement(CADR(expr), draw, mc, gc, dd);
SetFont(prevfont, gc);
return bbox;
}
/*----------------------------------------------------------------------
*
* Code for SymbolFace (i.e. font = 5) Expressions
*
* This makes the default font an Adobe Symbol Encoded font
* (provides access to any character in the Adobe Symbol Font
* encoding via strings like "\042" for the universal ["for all"]
* symbol, without the need for separate special names for each
* of these symbols).
*
*/
static int SymbolFaceAtom(SEXP expr)
{
return NameAtom(expr) &&
NameMatch(expr, "symbol");
}
static BBOX RenderSymbolFace(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox;
int prevfont = SetFont(SymbolFont, gc);
bbox = RenderElement(CADR(expr), draw, mc, gc, dd);
SetFont(prevfont, gc);
return bbox;
}
/*----------------------------------------------------------------------
*
* Code for Bold Italic Expressions
*
*/
static int BoldItalicAtom(SEXP expr)
{
return NameAtom(expr) &&
(NameMatch(expr, "bolditalic") || NameMatch(expr, "boldmath"));
}
static BBOX RenderBoldItalic(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox;
int prevfont = SetFont(BoldItalicFont, gc);
bbox = RenderElement(CADR(expr), draw, mc, gc, dd);
SetFont(prevfont, gc);
return bbox;
}
/*----------------------------------------------------------------------
*
* Code for Styles
*
*/
static int StyleAtom(SEXP expr)
{
return (NameAtom(expr) &&
(NameMatch(expr, "displaystyle") ||
NameMatch(expr, "textstyle") ||
NameMatch(expr, "scriptstyle") ||
NameMatch(expr, "scriptscriptstyle")));
}
static BBOX RenderStyle(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
STYLE prevstyle = GetStyle(mc);
BBOX bbox;
if (NameMatch(CAR(expr), "displaystyle"))
SetStyle(STYLE_D, mc, gc);
else if (NameMatch(CAR(expr), "textstyle"))
SetStyle(STYLE_T, mc, gc);
else if (NameMatch(CAR(expr), "scriptstyle"))
SetStyle(STYLE_S, mc, gc);
else if (NameMatch(CAR(expr), "scriptscriptstyle"))
SetStyle(STYLE_SS, mc, gc);
bbox = RenderElement(CADR(expr), draw, mc, gc, dd);
SetStyle(prevstyle, mc, gc);
return bbox;
}
/*----------------------------------------------------------------------
*
* Code for Phantom Expressions
*
*/
static int PhantomAtom(SEXP expr)
{
return (NameAtom(expr) &&
(NameMatch(expr, "phantom") ||
NameMatch(expr, "vphantom")));
}
static BBOX RenderPhantom(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox = RenderElement(CADR(expr), 0, mc, gc, dd);
if (NameMatch(CAR(expr), "vphantom")) {
bboxWidth(bbox) = 0;
bboxItalic(bbox) = 0;
}
else RenderGap(bboxWidth(bbox), draw, mc, gc, dd);
return bbox;
}
/*----------------------------------------------------------------------
*
* Code for Concatenate Expressions
*
*/
static int ConcatenateAtom(SEXP expr)
{
return NameAtom(expr) && NameMatch(expr, "paste");
}
static BBOX RenderConcatenate(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox = NullBBox();
int i, n;
expr = CDR(expr);
n = length(expr);
for (i = 0; i < n; i++) {
bbox = CombineBBoxes(bbox, RenderElement(CAR(expr), draw, mc, gc, dd));
if (i != n - 1)
bbox = RenderItalicCorr(bbox, draw, mc, gc, dd);
expr = CDR(expr);
}
return bbox;
}
/*----------------------------------------------------------------------
*
* Code for Comma-Separated Lists
*
*/
static BBOX RenderCommaList(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox = NullBBox();
double small = 0.4 * ThinSpace(gc, dd);
int i, n;
n = length(expr);
for (i = 0; i < n; i++) {
if (NameAtom(CAR(expr)) && NameMatch(CAR(expr), "...")) {
if (i > 0) {
bbox = CombineBBoxes(bbox, RenderSymbolChar(S_COMMA, draw,
mc, gc, dd));
bbox = CombineBBoxes(bbox, RenderSymbolChar(S_SPACE, draw,
mc, gc, dd));
}
bbox = CombineBBoxes(bbox, RenderSymbolChar(S_ELLIPSIS, draw,
mc, gc, dd));
bbox = CombineBBoxes(bbox, RenderGap(small, draw, mc, gc, dd));
}
else {
if (i > 0) {
bbox = CombineBBoxes(bbox, RenderSymbolChar(S_COMMA, draw,
mc, gc, dd));
bbox = CombineBBoxes(bbox, RenderSymbolChar(S_SPACE, draw,
mc, gc, dd));
}
bbox = CombineBBoxes(bbox, RenderElement(CAR(expr), draw, mc,
gc, dd));
}
expr = CDR(expr);
}
return bbox;
}
/*----------------------------------------------------------------------
*
* Code for General Expressions
*
*/
static BBOX RenderExpression(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
BBOX bbox;
if (NameAtom(CAR(expr)))
bbox = RenderSymbolString(CAR(expr), draw, mc, gc, dd);
else
bbox = RenderElement(CAR(expr), draw, mc, gc, dd);
bbox = RenderItalicCorr(bbox, draw, mc, gc, dd);
bbox = CombineBBoxes(bbox, RenderDelimiter(S_PARENLEFT, draw, mc, gc, dd));
bbox = CombineBBoxes(bbox, RenderCommaList(CDR(expr), draw, mc, gc, dd));
bbox = RenderItalicCorr(bbox, draw, mc, gc, dd);
bbox = CombineBBoxes(bbox, RenderDelimiter(S_PARENRIGHT, draw, mc, gc, dd));
return bbox;
}
/*----------------------------------------------------------------------
*
* Code for Comma Separated List Expressions
*
*/
static int ListAtom(SEXP expr)
{
return NameAtom(expr) && NameMatch(expr, "list");
}
static BBOX RenderList(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
return RenderCommaList(CDR(expr), draw, mc, gc, dd);
}
/* Dispatching procedure which determines nature of expression. */
static BBOX RenderFormula(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
SEXP head = CAR(expr);
if (SpaceAtom(head))
return RenderSpace(expr, draw, mc, gc, dd);
else if (BinAtom(head))
return RenderBin(expr, draw, mc, gc, dd);
else if (SuperAtom(head))
return RenderSup(expr, draw, mc, gc, dd);
else if (SubAtom(head))
return RenderSub(expr, draw, mc, gc, dd);
else if (WideTildeAtom(head))
return RenderWideTilde(expr, draw, mc, gc, dd);
else if (WideHatAtom(head))
return RenderWideHat(expr, draw, mc, gc, dd);
else if (BarAtom(head))
return RenderBar(expr, draw, mc, gc, dd);
else if (AccentAtom(head))
return RenderAccent(expr, draw, mc, gc, dd);
else if (OverAtom(head))
return RenderOver(expr, draw, mc, gc, dd);
else if (UnderlAtom(head))
return RenderUnderl(expr, draw, mc, gc, dd);
else if (AtopAtom(head))
return RenderAtop(expr, draw, mc, gc, dd);
else if (ParenAtom(head))
return RenderParen(expr, draw, mc, gc, dd);
else if (BGroupAtom(head))
return RenderBGroup(expr, draw, mc, gc, dd);
else if (GroupAtom(head))
return RenderGroup(expr, draw, mc, gc, dd);
else if (IntAtom(head))
return RenderInt(expr, draw, mc, gc, dd);
else if (OpAtom(head))
return RenderOp(expr, draw, mc, gc, dd);
else if (RadicalAtom(head))
return RenderRadical(expr, draw, mc, gc, dd);
else if (AbsAtom(head))
return RenderAbs(expr, draw, mc, gc, dd);
else if (CurlyAtom(head))
return RenderCurly(expr, draw, mc, gc, dd);
else if (RelAtom(head))
return RenderRel(expr, draw, mc, gc, dd);
else if (BoldAtom(head))
return RenderBold(expr, draw, mc, gc, dd);
else if (ItalicAtom(head))
return RenderItalic(expr, draw, mc, gc, dd);
else if (PlainAtom(head))
return RenderPlain(expr, draw, mc, gc, dd);
else if (SymbolFaceAtom(head))
return RenderSymbolFace(expr, draw, mc, gc, dd);
else if (BoldItalicAtom(head))
return RenderBoldItalic(expr, draw, mc, gc, dd);
else if (StyleAtom(head))
return RenderStyle(expr, draw, mc, gc, dd);
else if (PhantomAtom(head))
return RenderPhantom(expr, draw, mc, gc, dd);
else if (ConcatenateAtom(head))
return RenderConcatenate(expr, draw, mc, gc, dd);
else if (ListAtom(head))
return RenderList(expr, draw, mc, gc, dd);
else
return RenderExpression(expr, draw, mc, gc, dd);
}
/* Dispatch on whether atom (symbol, string, number, ...) */
/* or formula (some sort of expression) */
static BBOX RenderElement(SEXP expr, int draw, mathContext *mc,
pGEcontext gc, pGEDevDesc dd)
{
if (FormulaExpression(expr))
return RenderFormula(expr, draw, mc, gc, dd);
else
return RenderAtom(expr, draw, mc, gc, dd);
}
static BBOX RenderOffsetElement(SEXP expr, double x, double y, int draw,
mathContext *mc, pGEcontext gc,
pGEDevDesc dd)
{
BBOX bbox;
double savedX = mc->CurrentX;
double savedY = mc->CurrentY;
if (draw) {
mc->CurrentX += x;
mc->CurrentY += y;
}
bbox = RenderElement(expr, draw, mc, gc, dd);
bboxWidth(bbox) += x;
bboxHeight(bbox) += y;
bboxDepth(bbox) -= y;
mc->CurrentX = savedX;
mc->CurrentY = savedY;
return bbox;
}
/* Functions forming the R API */
/* Calculate width of expression */
/* BBOXes are in INCHES (see MetricUnit) */
double GEExpressionWidth(SEXP expr,
pGEcontext gc,
pGEDevDesc dd)
{
BBOX bbox;
double width;
/*
* Build a "drawing context" for the current expression
*/
mathContext mc;
mc.BaseCex = gc->cex;
mc.BoxColor = 4291543295U; // name2col("pink");
mc.CurrentStyle = STYLE_D;
/*
* Some "empty" values. Will be filled in after BBox is calc'ed
*/
mc.ReferenceX = 0;
mc.ReferenceY = 0;
mc.CurrentX = 0;
mc.CurrentY = 0;
mc.CurrentAngle = 0;
mc.CosAngle = 0;
mc.SinAngle = 0;
SetFont(PlainFont, gc);
bbox = RenderElement(expr, 0, &mc, gc, dd);
width = bboxWidth(bbox);
/*
* NOTE that we do fabs() here in case the device
* runs right-to-left.
* This is so that these calculations match those
* for string widths and heights, where the width
* and height of text is positive no matter how
* the device drawing is oriented.
*/
return fabs(toDeviceWidth(width, GE_INCHES, dd));
}
double GEExpressionHeight(SEXP expr,
pGEcontext gc,
pGEDevDesc dd)
{
BBOX bbox;
double height;
/*
* Build a "drawing context" for the current expression
*/
mathContext mc;
mc.BaseCex = gc->cex;
mc.BoxColor = 4291543295U; // name2col("pink");
mc.CurrentStyle = STYLE_D;
/*
* Some "empty" values. Will be filled in after BBox is calc'ed
*/
mc.ReferenceX = 0;
mc.ReferenceY = 0;
mc.CurrentX = 0;
mc.CurrentY = 0;
mc.CurrentAngle = 0;
mc.CosAngle = 0;
mc.SinAngle = 0;
SetFont(PlainFont, gc);
bbox = RenderElement(expr, 0, &mc, gc, dd);
height = bboxHeight(bbox) + bboxDepth(bbox);
/* NOTE that we do fabs() here in case the device
* draws top-to-bottom (like an X11 window).
* This is so that these calculations match those
* for string widths and heights, where the width
* and height of text is positive no matter how
* the device drawing is oriented.
*/
return fabs(toDeviceHeight(height, GE_INCHES, dd));
}
void GEExpressionMetric(SEXP expr,
const pGEcontext gc,
double *ascent, double *descent, double *width,
pGEDevDesc dd)
{
BBOX bbox;
/*
* Build a "drawing context" for the current expression
*/
mathContext mc;
mc.BaseCex = gc->cex;
mc.BoxColor = 4291543295U; // name2col("pink");
mc.CurrentStyle = STYLE_D;
/*
* Some "empty" values. Will be filled in after BBox is calc'ed
*/
mc.ReferenceX = 0;
mc.ReferenceY = 0;
mc.CurrentX = 0;
mc.CurrentY = 0;
mc.CurrentAngle = 0;
mc.CosAngle = 0;
mc.SinAngle = 0;
SetFont(PlainFont, gc);
bbox = RenderElement(expr, 0, &mc, gc, dd);
/* NOTE that we do fabs() here in case the device
* draws top-to-bottom (like an X11 window).
* This is so that these calculations match those
* for string widths and heights, where the width
* and height of text is positive no matter how
* the device drawing is oriented.
*/
*width = fabs(toDeviceWidth(bboxWidth(bbox), GE_INCHES, dd));
*ascent = fabs(toDeviceHeight(bboxHeight(bbox), GE_INCHES, dd));
*descent = fabs(toDeviceHeight(bboxDepth(bbox), GE_INCHES, dd));
}
void GEMathText(double x, double y, SEXP expr,
double xc, double yc, double rot,
pGEcontext gc,
pGEDevDesc dd)
{
BBOX bbox;
mathContext mc;
/* If font metric information is not available for device
then bail out */
double ascent, descent, width;
GEMetricInfo('M', gc, &ascent, &descent, &width, dd);
if ((ascent == 0.0) && (descent == 0.0) && (width == 0.0))
error(_("Metric information not available for this family/device"));
/*
* Build a "drawing context" for the current expression
*/
mc.BaseCex = gc->cex;
mc.BoxColor = 4291543295U; // name2col("pink");
mc.CurrentStyle = STYLE_D;
/*
* Some "empty" values. Will be filled in after BBox is calc'ed
*/
mc.ReferenceX = 0;
mc.ReferenceY = 0;
mc.CurrentX = 0;
mc.CurrentY = 0;
mc.CurrentAngle = 0;
mc.CosAngle = 0;
mc.SinAngle = 0;
SetFont(PlainFont, gc);
bbox = RenderElement(expr, 0, &mc, gc, dd);
mc.ReferenceX = fromDeviceX(x, GE_INCHES, dd);
mc.ReferenceY = fromDeviceY(y, GE_INCHES, dd);
if (R_FINITE(xc))
mc.CurrentX = mc.ReferenceX - xc * bboxWidth(bbox);
else
/* Paul 2002-02-11
* If xc == NA then should centre horizontally.
* Used to left-adjust.
*/
mc.CurrentX = mc.ReferenceX - 0.5 * bboxWidth(bbox);
if (R_FINITE(yc))
mc.CurrentY = mc.ReferenceY + bboxDepth(bbox)
- yc * (bboxHeight(bbox) + bboxDepth(bbox));
else
/* Paul 11/2/02
* If xc == NA then should centre vertically.
* Used to bottom-adjust.
*/
mc.CurrentY = mc.ReferenceY + bboxDepth(bbox)
- 0.5 * (bboxHeight(bbox) + bboxDepth(bbox));
mc.CurrentAngle = rot;
rot *= M_PI_2 / 90 ;/* radians */
mc.CosAngle = cos(rot);
mc.SinAngle = sin(rot);
RenderElement(expr, 1, &mc, gc, dd);
}/* GEMathText */