Rev 2 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** R : A Computer Langage for Statistical Data Analysis* Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka** 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, write to the Free Software* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/#include "Defn.h"#include "Mathlib.h"#include "Graphics.h"/* Coordinate Mappings *//* Linear/Logarithmic Scales */static double (*xt) (double);static double (*yt) (double);static double Log10(double x){return (FINITE(x) && x > 0.0) ? log10(x) : NA_REAL;}static double Ident(double x){return x;}void NewFrameConfirm(){int c;REprintf("Hit <Return> to see next plot: ");yyprompt("");while((c = cget()) != '\n' && c != R_EOF);}/* Device Startup */SEXP do_device(SEXP call, SEXP op, SEXP args, SEXP env){SEXP s;char *device;int i, ncpars, nnpars;char *cpars[20];double *npars;/* NO GARBAGE COLLECTS ALLOWED HERE*//* WE ARE USING REAL POINTERS *//* SWITCH TO R_ALLOCING IF NECESSARY */checkArity(op, args);s = CAR(args);if (!isString(s) || length(s) <= 0)errorcall(call, "device name must be a character string\n");device = CHAR(STRING(s)[0]);s = CADR(args);if (!isString(s) || length(s) > 20)errorcall(call, "invalid device driver parameters\n");ncpars = LENGTH(s);for(i=0 ; i<LENGTH(s) ; i++)cpars[i] = CHAR(STRING(s)[i]);s = CADDR(args);if (!isReal(CADDR(args)))errorcall(call, "width and height must be numeric\n");nnpars = LENGTH(s);if( !strcmp(device,"X11") )for(i=0 ; i<nnpars ; i++ )if(!FINITE(REAL(s)[i]) || REAL(s)[i] <= 0)errorcall(call, "invalid device driver parameter\n");npars = REAL(s);if (!SetDevice(device, cpars, ncpars, npars, nnpars))errorcall(call, "unable to start device %s\n", device);xt = Ident;yt = Ident;/* GARBAGE COLLECTS ARE OK FROM HERE */s = mkString(CHAR(STRING(CAR(args))[0]));gsetVar(install(".Device"), s, R_NilValue);return CAR(args);}SEXP do_devoff(SEXP call, SEXP op, SEXP args, SEXP env){checkArity(op, args);KillDevice();gsetVar(install(".Device"), R_NilValue, R_NilValue);return R_NilValue;}void Specify2(char*, SEXP);void ProcessInlinePars(SEXP s){if(isList(s)) {while(s != R_NilValue) {if(isList(CAR(s)))ProcessInlinePars(CAR(s));else if(TAG(s) != R_NilValue)Specify2(CHAR(PRINTNAME(TAG(s))), CAR(s));s = CDR(s);}}}/* GetPar is intended for looking through a list *//* typically that bound to ... for a particular *//* parameter value. This is easier than trying *//* to match every graphics parameter in argument *//* lists and passing them explicitly. */SEXP GetPar(char *which, SEXP parlist){SEXP w, p;w = install(which);for(p=parlist ; p!=R_NilValue ; p=CDR(p)) {if(TAG(p) == w)return CAR(p);}return R_NilValue;}SEXP FixupPch(SEXP pch){int i, n;SEXP ans;if(length(pch) == 0) {ans = allocVector(INTSXP, n=1);INTEGER(ans)[0] = GP->pch;}else if(isList(pch)) {ans = allocVector(INTSXP, n=length(pch));for(i=0 ; pch != R_NilValue ; pch = CDR(pch))INTEGER(ans)[i++] = asInteger(CAR(pch));}else if(isInteger(pch)) {ans = allocVector(INTSXP, n=length(pch));for(i=0 ; i<n ; i++)INTEGER(ans)[i] = INTEGER(pch)[i];}else if(isReal(pch)) {ans = allocVector(INTSXP, n=length(pch));for(i=0 ; i<n ; i++)INTEGER(ans)[i] = FINITE(REAL(pch)[i]) ?REAL(pch)[i] : NA_INTEGER;}else if(isString(pch)) {ans = allocVector(INTSXP, n=length(pch));for(i=0 ; i<n ; i++)INTEGER(ans)[i] = CHAR(STRING(pch)[i])[0];}else error("invalid plotting symbol\n");for(i=0 ; i<n ; i++) {if(INTEGER(ans)[i] < 0)INTEGER(ans)[i] = GP->pch;}return ans;}SEXP FixupLty(SEXP lty){int i, n;SEXP ans;if(length(lty) == 0) {ans = allocVector(INTSXP, 1);INTEGER(ans)[0] = GP->lty;}else {ans = allocVector(INTSXP, n=length(lty));for(i=0 ; i<n; i++)INTEGER(ans)[i] = LTYpar(lty, i);}return ans;}SEXP FixupFont(SEXP font){int i, k, n;SEXP ans;if(length(font) == 0) {ans = allocVector(INTSXP, 1);INTEGER(ans)[0] = NA_INTEGER;}else if(isInteger(font)) {ans = allocVector(INTSXP, n=length(font));for(i=0 ; i<n; i++) {k = INTEGER(font)[i];if(k < 1 || k > 4) k = NA_INTEGER;INTEGER(ans)[i] = k;}}else if(isReal(font)) {ans = allocVector(INTSXP, n=length(font));for(i=0 ; i<n; i++) {k = REAL(font)[i];if(k < 1 || k > 4) k = NA_INTEGER;INTEGER(ans)[i] = k;}}else error("invalid font specification\n");return ans;}SEXP FixupCol(SEXP col){int i, n;SEXP ans;if(length(col) == 0) {ans = allocVector(INTSXP, 1);INTEGER(ans)[0] = NA_INTEGER;}else if(isList(col)) {ans = allocVector(INTSXP, n=length(col));for(i=0 ; i<n; i++) {INTEGER(ans)[i] = RGBpar(CAR(col), 0);col = CDR(col);}}else {ans = allocVector(INTSXP, n=length(col));for(i=0 ; i<n; i++)INTEGER(ans)[i] = RGBpar(col, i);}return ans;}SEXP FixupCex(SEXP cex){SEXP ans;int i, n;double c;if(length(cex) == 0) {ans = allocVector(REALSXP, 1);REAL(ans)[0] = NA_REAL;}else if(isReal(cex)) {ans = allocVector(REALSXP, n=length(cex));for(i=0 ; i<n; i++) {c = REAL(cex)[i];if(FINITE(c) && c > 0)REAL(ans)[i] = c;elseREAL(ans)[i] = NA_REAL;}}else if(isInteger(cex)) {ans = allocVector(REALSXP, n=length(cex));for(i=0 ; i<n; i++) {c = INTEGER(cex)[i];if(c == NA_INTEGER || c <= 0)c = NA_REAL;REAL(ans)[i] = c;}}return ans;}/* plot.new(ask) *//* create a new plot */SEXP do_plot_new(SEXP call, SEXP op, SEXP args, SEXP env){int ask, asksave;checkArity(op, args);ask = asLogical(CAR(args));if(ask == NA_LOGICAL) ask = DP->ask;asksave = GP->ask;GP->ask = ask;GNewPlot();DP->xlog = GP->xlog = 0;DP->ylog = GP->ylog = 0;xt = Ident;yt = Ident;GScale(0.0, 1.0, 1);GScale(0.0, 1.0, 2);GMapWin2Fig();GSetState(1);GP->ask = asksave;return R_NilValue;}/* plot.window(xlim, ylim, log) *//* define world coordinates */SEXP do_plot_window(SEXP call, SEXP op, SEXP args, SEXP env){SEXP xlim, ylim, log;double xmin, xmax, ymin, ymax;char *p;/* checkArity(op, args); */if(length(args) < 3)errorcall(call, "at least 3 arguments required\n");xlim = CAR(args);if(!isNumeric(xlim) || LENGTH(xlim) != 2)errorcall(call, "invalid xlim\n");args = CDR(args);ylim = CAR(args);if(!isNumeric(ylim) || LENGTH(ylim) != 2)errorcall(call, "invalid ylim\n");args = CDR(args);log = CAR(args);if (!isString(log))error("invalid \"log=\" specification\n");p = CHAR(STRING(log)[0]);while (*p) {switch (*p) {case 'x':DP->xlog = GP->xlog = 1;xt = Log10;break;case 'y':DP->ylog = GP->ylog = 1;yt = Log10;break;default:error("invalid \"log=\" specification\n");}p++;}args = CDR(args);GSavePars();ProcessInlinePars(args);if(isInteger(xlim)) {if(INTEGER(xlim)[0] == NA_INTEGER || INTEGER(xlim)[1] == NA_INTEGER)errorcall(call, "NAs not allowed in xlim\n");xmin = INTEGER(xlim)[0];xmax = INTEGER(xlim)[1];}else {if(!FINITE(REAL(xlim)[0]) || !FINITE(REAL(xlim)[1]))errorcall(call, "NAs not allowed in xlim\n");xmin = REAL(xlim)[0];xmax = REAL(xlim)[1];}if(isInteger(ylim)) {if(INTEGER(ylim)[0] == NA_INTEGER || INTEGER(ylim)[1] == NA_INTEGER)errorcall(call, "NAs not allowed in ylim\n");ymin = INTEGER(ylim)[0];ymax = INTEGER(ylim)[1];}else {if(!FINITE(REAL(ylim)[0]) || !FINITE(REAL(ylim)[1]))errorcall(call, "NAs not allowed in ylim\n");ymin = REAL(ylim)[0];ymax = REAL(ylim)[1];}GCheckState();GScale(xmin, xmax, 1);GScale(ymin, ymax, 2);GMapWin2Fig();GRestorePars();return R_NilValue;}static void GetAxisLimits(double left, double right, double *low, double *high){double eps;if(left <= right) {eps = FLT_EPSILON * (right - left);if(eps == 0) eps = 0.5 * FLT_EPSILON;*low = left - eps;*high = right + eps;}else {eps = FLT_EPSILON * (left - right);if(eps == 0) eps = 0.5 * FLT_EPSILON;*low = right - eps;*high = left + eps;}}/* axis(which, at, labels, ...) -- draw an axis */SEXP do_axis(SEXP call, SEXP op, SEXP args, SEXP env){SEXP at, lab;int col, fg, i, n, which;double x, y, xc, yc, xtk, ytk, tnew, tlast, cwid;double gap, labw, low, high;/* Initial checks */GCheckState();if(length(args) < 3)errorcall(call, "too few arguments\n");/* Required arguments */which = asInteger(CAR(args));if (which < 1 || which > 4)errorcall(call, "invalid axis number\n");args = CDR(args);internalTypeCheck(call, at = CAR(args), REALSXP);args = CDR(args);/* internalTypeCheck(call, lab = CAR(args), STRSXP); */lab = CAR(args);if (LENGTH(at) != LENGTH(lab))errorcall(call, "location and label lengths differ\n");n = LENGTH(at);args = CDR(args);/* Process any optional graphical parameters */R_Visible = 0;GSavePars();GP->xpd = 1;ProcessInlinePars(args);/* GP->lwd = DP->lwd; /* override inline lwd */GP->adj = 0.5;GP->font = GP->fontaxis;GP->cex = GP->cex * GP->cexbase;col = GP->col;fg = GP->fg;/* Check the axis type parameter *//* If it is 'n', there is nothing to do */if(which == 1 || which == 3) {if(GP->xaxt == 'n') {GRestorePars();return R_NilValue;}}else if(which == 2 || which == 4) {if(GP->yaxt == 'n') {GRestorePars();return R_NilValue;}}else errorcall(call, "invalid \"which\" value\n");/* Compute the ticksize in NDC units */xc = fabs(GP->mex * GP->cexbase * GP->cra[1] * GP->asp / GP->fig2dev.bx);yc = fabs(GP->mex * GP->cexbase * GP->cra[1] / GP->fig2dev.by);xtk = 0.5 * xc;ytk = 0.5 * yc;x = GP->plt[0];y = GP->plt[2];/* Draw the axis */GMode(1);switch (which) {case 1:case 3:GetAxisLimits(GP->plt[0], GP->plt[1], &low, &high);if (which == 3) {y = GP->plt[3];ytk = -ytk;}GP->col = fg;GStartPath();GMoveTo(XMAP(xt(REAL(at)[0])), y);GLineTo(XMAP(xt(REAL(at)[n - 1])), y);for (i = 0; i < n; i++) {x = XMAP(xt(REAL(at)[i]));if (low <= x && x <= high) {GMoveTo(x, y);GLineTo(x, y - ytk);}}GEndPath();GP->col = GP->colaxis;tlast = -1.0;gap = GStrWidth("m", 2); /* FIXUP x/y distance */for (i = 0; i < n; i++) {x = XMAP(xt(REAL(at)[i]));if(isExpression(lab)) {GMMathText(VECTOR(lab)[i], which, GP->mgp[1], 0, xt(REAL(at)[i]), GP->las);}else {labw = GStrWidth(CHAR(STRING(lab)[i]), 2);tnew = x - 0.5 * labw;if (tnew - tlast >= gap) {GMtext(CHAR(STRING(lab)[i]), which, GP->mgp[1], 0, xt(REAL(at)[i]), GP->las);tlast = x + 0.5 *labw;}}}break;case 2:case 4:GetAxisLimits(GP->plt[2], GP->plt[3], &low, &high);if (which == 4) {x = GP->plt[1];xtk = -xtk;}GP->col = fg;GStartPath();GMoveTo(x, YMAP(yt(REAL(at)[0])));GLineTo(x, YMAP(yt(REAL(at)[n - 1])));for (i = 0; i < n; i++) {y = YMAP(yt(REAL(at)[i]));if (low <= y && y <= high) {GMoveTo(x, y);GLineTo(x - xtk, y);}}GEndPath();GP->col = GP->colaxis;gap = GStrWidth("m", 2);gap = yInchtoFig(xFigtoInch(gap));tlast = -1.0;cwid = GP->cex * fabs(GP->cra[0] / GP->ndc2dev.by);for (i = 0; i < n; i++) {y = YMAP(yt(REAL(at)[i]));if(isExpression(lab)) {GMMathText(VECTOR(lab)[i], which, GP->mgp[1], 0, yt(REAL(at)[i]), GP->las);}else {labw = GStrWidth(CHAR(STRING(lab)[i]), 2);labw = yInchtoFig(xFigtoInch(labw));tnew = y - 0.5 * labw;if (tnew - tlast >= gap) {GMtext(CHAR(STRING(lab)[i]), which, GP->mgp[1], 0, yt(REAL(at)[i]), GP->las);tlast = y + 0.5 *labw;}}}break;}GMode(0);GRestorePars();return R_NilValue;}/* plot.xy(xy, type, pch, lty, col, cex, ...) *//* plot points or lines of various types */SEXP do_plot_xy(SEXP call, SEXP op, SEXP args, SEXP env){SEXP sxy, sx, sy, pch, cex, col, bg, lty;double *x, *y, xold, yold, xx, yy;int i, n, npch, ncex, ncol, nbg, nlty, type;/* Basic Checks */GCheckState();if(length(args) < 6)errorcall(call, "too few arguments\n");/* Required Arguments */sxy = CAR(args);if (!isList(sxy) || length(sxy) < 2)errorcall(call, "invalid plotting structure\n");internalTypeCheck(call, sx = CAR(sxy), REALSXP);internalTypeCheck(call, sy = CADR(sxy), REALSXP);if (LENGTH(sx) != LENGTH(sy))error("x and y lengths differ for plot\n");n = LENGTH(sx);args = CDR(args);if(isNull(CAR(args))) type = 'p';else {if(isString(CAR(args)) && LENGTH(CAR(args)) == 1)type = CHAR(STRING(CAR(args))[0])[0];else errorcall(call, "invalid plot type\n");}args = CDR(args);PROTECT(pch = FixupPch(CAR(args)));npch = length(pch);args = CDR(args);PROTECT(lty = FixupLty(CAR(args)));nlty = length(lty);args = CDR(args);PROTECT(col = FixupCol(CAR(args)));ncol = LENGTH(col);args = CDR(args);PROTECT(bg = FixupCol(CAR(args)));nbg = LENGTH(bg);args = CDR(args);PROTECT(cex = FixupCex(CAR(args)));ncex = LENGTH(cex);args = CDR(args);/* Miscellaneous Graphical Parameters */GSavePars();ProcessInlinePars(args);x = REAL(sx);y = REAL(sy);if(nlty && INTEGER(lty)[0] != NA_INTEGER)GP->lty = INTEGER(lty)[0];if(ncex && FINITE(REAL(cex)[0]))GP->cex = GP->cexbase * REAL(cex)[0];elseGP->cex = GP->cexbase;GMode(1);GClip();/* lines and overplotted lines and points */if (type == 'l' || type == 'o') {GP->col = INTEGER(col)[0];xold = NA_REAL;yold = NA_REAL;GStartPath();for (i = 0; i < n; i++) {xx = xt(x[i]);yy = yt(y[i]);if (FINITE(xold) && FINITE(yold) && FINITE(xx) && FINITE(yy)) {GLineTo(XMAP(xx), YMAP(yy));}else if (FINITE(xx) && FINITE(yy))GMoveTo(XMAP(xx), YMAP(yy));xold = xx;yold = yy;}GEndPath();}/* points connected with broken lines */if(type == 'b' || type == 'c') {double d, f, x0, x1, xc, y0, y1, yc;d = 0.5 * GP->cex * GP->cra[1] * GP->ipr[1];xc = xNDCtoInch(GP->fig2dev.bx / GP->ndc2dev.bx) * (GP->plt[1] - GP->plt[0]);yc = yNDCtoInch(GP->fig2dev.by / GP->ndc2dev.by) * (GP->plt[3] - GP->plt[2]);xc = xc / (GP->usr[1] - GP->usr[0]);yc = yc / (GP->usr[3] - GP->usr[2]);GP->col = INTEGER(col)[0];xold = NA_REAL;yold = NA_REAL;GStartPath();for (i = 0; i < n; i++) {xx = xt(x[i]);yy = yt(y[i]);if (FINITE(xold) && FINITE(yold) && FINITE(xx) && FINITE(yy)) {if((f = d/hypot(xc * (xx-xold), yc * (yy-yold))) < 0.5) {x0 = xold + f * (xx - xold);y0 = yold + f * (yy - yold);x1 = xx + f * (xold - xx);y1 = yy + f * (yold - yy);GMoveTo(XMAP(x0), YMAP(y0));GLineTo(XMAP(x1), YMAP(y1));}}xold = xx;yold = yy;}GEndPath();}if (type == 's') {GP->col = INTEGER(col)[0];xold = xt(x[0]);yold = xt(y[0]);GStartPath();if (FINITE(xold) && FINITE(yold))GMoveTo(XMAP(xold), YMAP(yold));for (i = 1; i < n; i++) {xx = xt(x[i]);yy = yt(y[i]);if (FINITE(xold) && FINITE(yold) && FINITE(xx) && FINITE(yy)) {GLineTo(XMAP(xx), YMAP(yold));GLineTo(XMAP(xx), YMAP(yy));}else if (FINITE(x[i]) && FINITE(y[i]))GMoveTo(XMAP(xx), YMAP(yy));xold = xx;yold = yy;}GEndPath();}if (type == 'S') {GP->col = INTEGER(col)[0];xold = xt(x[0]);yold = xt(y[0]);GStartPath();if (FINITE(xold) && FINITE(yold))GMoveTo(XMAP(xold), YMAP(yold));for (i = 1; i < n; i++) {xx = xt(x[i]);yy = yt(y[i]);if (FINITE(xold) && FINITE(yold) && FINITE(xx) && FINITE(yy)) {GLineTo(XMAP(xold), YMAP(yy));GLineTo(XMAP(xx), YMAP(yy));}else if (FINITE(x[i]) && FINITE(y[i]))GMoveTo(XMAP(xx), YMAP(yy));xold = xx;yold = yy;}GEndPath();}if (type == 'h') {GP->col = INTEGER(col)[0];for (i = 0; i < n; i++) {xx = xt(x[i]);yy = yt(y[i]);if (FINITE(xx) && FINITE(yy)) {GStartPath();GMoveTo(XMAP(xx), YMAP(yt(0.0)));GLineTo(XMAP(xx), YMAP(yy));GEndPath();}}}if (type == 'p' || type == 'b' || type == 'o') {for (i = 0; i < n; i++) {xx = xt(x[i]);yy = yt(y[i]);if (FINITE(xx) && FINITE(yy)) {GP->col = INTEGER(col)[i % ncol];GP->bg = INTEGER(bg)[i % nbg];GSymbol(XMAP(xx), YMAP(yy), INTEGER(pch)[i % npch]);}}}GMode(0);GRestorePars();UNPROTECT(5);return R_NilValue;}static void xypoints(SEXP call, SEXP args, int *n){int k;if (!isNumeric(CAR(args)) || (k = LENGTH(CAR(args))) <= 0)errorcall(call, "first argument invalid\n");CAR(args) = coerceVector(CAR(args), REALSXP);*n = k;args = CDR(args);if (!isNumeric(CAR(args)) || (k = LENGTH(CAR(args))) <= 0)errorcall(call, "second argument invalid\n");CAR(args) = coerceVector(CAR(args), REALSXP);if (k > *n) *n = k;args = CDR(args);if (!isNumeric(CAR(args)) || (k = LENGTH(CAR(args))) <= 0)errorcall(call, "third argument invalid\n");CAR(args) = coerceVector(CAR(args), REALSXP);if (k > *n) *n = k;args = CDR(args);if (!isNumeric(CAR(args)) || (k = LENGTH(CAR(args))) <= 0)errorcall(call, "fourth argument invalid\n");CAR(args) = coerceVector(CAR(args), REALSXP);if (k > *n) *n = k;args = CDR(args);}/* segments(x0, y0, x1, y1, col, lty) */SEXP do_segments(SEXP call, SEXP op, SEXP args, SEXP env){SEXP sx0, sy0, sx1, sy1, col, lty;double *x0, *x1, *y0, *y1;double xx0, yy0, xx1, yy1;int nx0, nx1, ny0, ny1;int i, n, ncol, nlty;GCheckState();if(length(args) < 4) errorcall(call, "too few arguments\n");xypoints(call, args, &n);sx0 = CAR(args); nx0 = length(sx0); args = CDR(args);sy0 = CAR(args); ny0 = length(sy0); args = CDR(args);sx1 = CAR(args); nx1 = length(sx1); args = CDR(args);sy1 = CAR(args); ny1 = length(sy1); args = CDR(args);PROTECT(lty = FixupLty(GetPar("lty", args)));nlty = length(lty);PROTECT(col = FixupCol(GetPar("col", args)));ncol = LENGTH(col);GSavePars();x0 = REAL(sx0);y0 = REAL(sy0);x1 = REAL(sx1);y1 = REAL(sy1);GMode(1);for (i = 0; i < n; i++) {xx0 = xt(x0[i%nx0]);yy0 = yt(y0[i%ny0]);xx1 = xt(x1[i%nx1]);yy1 = yt(y1[i%ny1]);if (FINITE(xx0) && FINITE(yy0) && FINITE(xx1) && FINITE(yy1)) {GP->col = INTEGER(col)[i % ncol];if(GP->col == NA_INTEGER) GP->col = DP->col;GP->lty = INTEGER(lty)[i % nlty];GStartPath();GMoveTo(XMAP(xx0), YMAP(yy0));GLineTo(XMAP(xx1), YMAP(yy1));GEndPath();}}GMode(0);GRestorePars();UNPROTECT(2);return R_NilValue;}/* rect(xl, yb, xr, yt, col, border) */SEXP do_rect(SEXP call, SEXP op, SEXP args, SEXP env){SEXP sxl, sxr, syb, syt, col, lty, border;double *xl, *xr, *yb, *yt;int i, n, nxl, nxr, nyb, nyt;int ncol, nlty, nborder;GCheckState();if(length(args) < 4) errorcall(call, "too few arguments\n");xypoints(call, args, &n);sxl = CAR(args); nxl = length(sxl); args = CDR(args);syb = CAR(args); nyb = length(syb); args = CDR(args);sxr = CAR(args); nxr = length(sxr); args = CDR(args);syt = CAR(args); nyt = length(syt); args = CDR(args);PROTECT(col = FixupCol(GetPar("col", args)));ncol = LENGTH(col);PROTECT(border = FixupCol(GetPar("border", args)));nborder = LENGTH(border);PROTECT(lty = FixupLty(GetPar("lty", args)));nlty = length(lty);GSavePars();if(nlty && INTEGER(lty)[i % nlty] != NA_INTEGER) GP->lty = INTEGER(lty)[i % nlty];else GP->lty = DP->lty;xl = REAL(sxl);xr = REAL(sxr);yb = REAL(syb);yt = REAL(syt);GMode(1);for (i = 0; i < n; i++) {if (FINITE(xl[i%nxl]) && FINITE(yb[i%nyb])&& FINITE(xr[i%nxr]) && FINITE(yt[i%nyt]))GRect(XMAP(xl[i % nxl]), YMAP(yb[i % nyb]),XMAP(xr[i % nxr]), YMAP(yt[i % nyt]),INTEGER(col)[i % ncol],INTEGER(border)[i % nborder]);}GMode(0);GRestorePars();UNPROTECT(3);return R_NilValue;}/* do_arrows(x0, y0, x1, y1, length, angle, code, col) */SEXP do_arrows(SEXP call, SEXP op, SEXP args, SEXP env){SEXP sx0, sx1, sy0, sy1, col, lty;double *x0, *x1, *y0, *y1;double xx0, yy0, xx1, yy1, hlength, angle;int code, i, n, nx0, nx1, ny0, ny1;int ncol, nlty, xpd;GCheckState();if(length(args) < 4) errorcall(call, "too few arguments\n");xypoints(call, args, &n);sx0 = CAR(args); nx0 = length(sx0); args = CDR(args);sy0 = CAR(args); ny0 = length(sy0); args = CDR(args);sx1 = CAR(args); nx1 = length(sx1); args = CDR(args);sy1 = CAR(args); ny1 = length(sy1); args = CDR(args);hlength = asReal(GetPar("length", args));if (!FINITE(hlength) || hlength <= 0)errorcall(call, "invalid head length\n");angle = asReal(GetPar("angle", args));if (!FINITE(angle))errorcall(call, "invalid head angle\n");code = asInteger(GetPar("code", args));if (code == NA_INTEGER || code < 0 || code > 3)errorcall(call, "invalid arrow head specification\n");PROTECT(col = FixupCol(GetPar("col", args)));ncol = LENGTH(col);PROTECT(lty = FixupLty(GetPar("lty", args)));nlty = length(lty);xpd = asLogical(GetPar("xpd", args));if(xpd == NA_LOGICAL) xpd = GP->xpd;GSavePars();x0 = REAL(sx0);y0 = REAL(sy0);x1 = REAL(sx1);y1 = REAL(sy1);GMode(1);for (i = 0; i < n; i++) {xx0 = xt(x0[i%nx0]);yy0 = yt(y0[i%ny0]);xx1 = xt(x1[i%nx1]);yy1 = yt(y1[i%ny1]);if (FINITE(xx0) && FINITE(yy0) && FINITE(xx1) && FINITE(yy1)) {GP->col = INTEGER(col)[i % ncol];if(GP->col == NA_INTEGER) GP->col = DP->col;if(nlty == 0 || INTEGER(lty)[i % nlty] == NA_INTEGER)GP->lty = DP->lty;elseGP->lty = INTEGER(lty)[i % nlty];GArrow(XMAP(xx0), YMAP(yy0), XMAP(xx1), YMAP(yy1),hlength, angle, code);}}GMode(0);GRestorePars();UNPROTECT(2);return R_NilValue;}/* polygon(x, y, col, border) */SEXP do_polygon(SEXP call, SEXP op, SEXP args, SEXP env){SEXP sx, sy, col, border, lty;int nx, ny, ncol, nborder, nlty, xpd;double *work;char *vmax;GCheckState();if(length(args) < 2) errorcall(call, "too few arguments\n");if (!isNumeric(CAR(args)) || (nx = LENGTH(CAR(args))) <= 0)errorcall(call, "first argument invalid\n");sx = CAR(args) = coerceVector(CAR(args), REALSXP);args = CDR(args);if (!isNumeric(CAR(args)) || (ny = LENGTH(CAR(args))) <= 0)errorcall(call, "second argument invalid\n");sy = CAR(args) = coerceVector(CAR(args), REALSXP);args = CDR(args);if (ny != nx)errorcall(call, "x and y lengths differ in polygon");PROTECT(col = FixupCol(GetPar("col", args)));ncol = LENGTH(col);PROTECT(border = FixupCol(GetPar("border", args)));nborder = LENGTH(border);PROTECT(lty = FixupLty(GetPar("lty", args)));nlty = length(lty);xpd = asLogical(GetPar("xpd", args));if(xpd == NA_LOGICAL) xpd = GP->xpd;GSavePars();GMode(1);vmax = vmaxget();work = (double*)R_alloc(2*nx, sizeof(double));if(INTEGER(lty)[0] == NA_INTEGER) GP->lty = DP->lty;else GP->lty = INTEGER(lty)[0];GPolygon(nx, REAL(sx), REAL(sy), INTEGER(col)[0], INTEGER(border)[0], 1, work);vmaxset(vmax);GMode(0);GRestorePars();UNPROTECT(3);return R_NilValue;}SEXP do_text(SEXP call, SEXP op, SEXP args, SEXP env){SEXP sx, sy, sxy, txt, adj, cex, col, font;int i, n, ncex, ncol, nfont, ntxt, xpd;double adjx, adjy;double *x, *y;double xx, yy;GCheckState();if(length(args) < 2) errorcall(call, "too few arguments\n");sxy = CAR(args);if (!isList(sxy) || length(sxy) < 2)errorcall(call, "invalid plotting structure\n");internalTypeCheck(call, sx = CAR(sxy), REALSXP);internalTypeCheck(call, sy = CADR(sxy), REALSXP);if (LENGTH(sx) != LENGTH(sy))error("x and y lengths differ for plot\n");n = LENGTH(sx);args = CDR(args);#ifdef OLDinternalTypeCheck(call, txt = CAR(args), STRSXP);#elsetxt = CAR(args);#endifif (LENGTH(txt) <= 0)errorcall(call, "zero length \"text\" specified\n");args = CDR(args);PROTECT(cex = FixupCex(GetPar("cex", args)));ncex = LENGTH(cex);PROTECT(col = FixupCol(GetPar("col", args)));ncol = LENGTH(col);PROTECT(font = FixupFont(GetPar("font", args)));nfont = LENGTH(font);PROTECT(adj = GetPar("adj", args));if(isNull(adj) || (isNumeric(adj) && length(adj) == 0)) {adjx = GP->adj;adjy = GP->yCharOffset;}else if(isReal(adj)) {if(LENGTH(adj) == 1) {adjx = REAL(adj)[0];adjy = GP->yCharOffset;}else {adjx = REAL(adj)[0];adjy = REAL(adj)[1];}}else errorcall(call, "invalid adj value\n");xpd = asLogical(GetPar("xpd", args));if(xpd == NA_LOGICAL) xpd = 0;x = REAL(sx);y = REAL(sy);n = LENGTH(sx);ntxt = LENGTH(txt);GSavePars();GMode(1);for (i = 0; i < n; i++) {xx = xt(x[i % n]);yy = yt(y[i % n]);if (FINITE(xx) && FINITE(yy)) {if (ncol && INTEGER(col)[i % ncol] != NA_INTEGER)GP->col = INTEGER(col)[i % ncol];else GP->col = DP->col;if(ncex && FINITE(REAL(cex)[i%ncex]))GP->cex = GP->cexbase * REAL(cex)[i % ncex];else GP->cex = GP->cexbase;if (nfont && INTEGER(font)[i % nfont] != NA_INTEGER)GP->font = INTEGER(font)[i % nfont];else GP->font = DP->font;if(isExpression(txt))GMathText(XMAP(xx), YMAP(yy), VECTOR(txt)[i % ntxt], adjx, adjy, 0.0);elseGText(XMAP(xx), YMAP(yy), CHAR(STRING(txt)[i % ntxt]), adjx, adjy, 0.0);}}GMode(0);GRestorePars();UNPROTECT(4);return R_NilValue;}/* mtext(text, side, line, outer, at = NULL) */SEXP do_mtext(SEXP call, SEXP op, SEXP args, SEXP env){SEXP adj, cex, col, font, text;double line, at, adjx, adjy;int i, side, outer;int newsave;GCheckState();if(length(args) < 5) errorcall(call, "too few arguments\n");/* internalTypeCheck(call, text = CAR(args), STRSXP); */text = CAR(args);if (LENGTH(text) <= 0)errorcall(call, "zero length \"text\" specified\n");args = CDR(args);side = asInteger(CAR(args));if(side < 1 || side > 4) errorcall(call, "invalid side value\n");args = CDR(args);line = asReal(CAR(args));if(!FINITE(line)) /* || line < 0.0 -- negative values make sense ! */errorcall(call, "invalid line value\n");args = CDR(args);outer = asInteger(CAR(args));if(outer == NA_INTEGER) outer = 0;args = CDR(args);if (CAR(args) != R_NilValue || LENGTH(CAR(args)) == 0) {at = asReal(CAR(args));if(!FINITE(at)) errorcall(call, "invalid at value\n");args = CDR(args);}else at = NA_REAL;GSavePars();PROTECT(adj = GetPar("adj", args));if(isNull(adj) || (isNumeric(adj) && length(adj) == 0)) {adjx = GP->adj;adjy = GP->yCharOffset;}else if(isReal(adj)) {if(LENGTH(adj) == 1) {adjx = REAL(adj)[0];adjy = GP->yCharOffset;}else {adjx = REAL(adj)[0];adjy = REAL(adj)[1];}}else errorcall(call, "invalid adj value\n");if(!FINITE(at)) {switch(side % 2) {case 0:at = (1 - adjx) * GP->usr[0] + adjx * GP->usr[1];break;case 1:at = (1 - adjx) * GP->usr[2] + adjx * GP->usr[3];break;}}/*======= now you should MAKE USE of adjx, adjy !!! =======*/PROTECT(cex = FixupCex(GetPar("cex", args)));if(FINITE(REAL(cex)[0])) GP->cex = GP->cexbase * REAL(cex)[0];else GP->cex = GP->cexbase;PROTECT(col = FixupCol(GetPar("col", args)));if(INTEGER(col)[0] != NA_INTEGER) GP->col = INTEGER(col)[0];PROTECT(font = FixupFont(GetPar("font", args)));if(INTEGER(font)[0] != NA_INTEGER) GP->font = INTEGER(font)[0];GP->adj = adjx;GP->xpd = 1;if(outer) newsave = GP->new;GMode(1);if(isExpression(text))GMMathText(VECTOR(text)[0], side, line, outer, at, 0);elseGMtext(CHAR(STRING(text)[0]), side, line, outer, at, 0);GMode(0);GRestorePars();if(outer) GP->new = DP->new = newsave;UNPROTECT(4);return R_NilValue;}/* Title(main=NULL, sub=NULL, xlab=NULL, ylab=NULL, ...) */SEXP do_title(SEXP call, SEXP op, SEXP args, SEXP env){SEXP main, xlab, ylab, sub;double x, y, adj;GCheckState();if(length(args) < 4) errorcall(call, "too few arguments\n");main = sub = xlab = ylab = R_NilValue;if (CAR(args) != R_NilValue && LENGTH(CAR(args)) > 0)main = CAR(args);args = CDR(args);if (CAR(args) != R_NilValue && LENGTH(CAR(args)) > 0)sub = CAR(args);args = CDR(args);if (CAR(args) != R_NilValue && LENGTH(CAR(args)) > 0)xlab = CAR(args);args = CDR(args);if (CAR(args) != R_NilValue && LENGTH(CAR(args)) > 0)ylab = CAR(args);args = CDR(args);GSavePars();ProcessInlinePars(args);/* Always work in expanded mode */GP->xpd = 1;x = fabs((GP->cra[1] * GP->mex) / GP->fig2dev.bx);y = fabs((GP->asp * GP->cra[1] * GP->mex)/GP->fig2dev.by);adj = GP->adj;GMode(1);if(main != R_NilValue) {GP->cex = GP->cexbase * GP->cexmain;GP->col = GP->colmain;GP->font = GP->fontmain;if(isExpression(main))GMathText((1-adj)*GP->plt[0]+adj*GP->plt[1], 0.5*GP->plt[3]+0.5,VECTOR(main)[0], 0.5, 0.5, 0.0);elseGText((1-adj)*GP->plt[0]+adj*GP->plt[1], 0.5*GP->plt[3]+0.5,CHAR(STRING(main)[0]), adj, 0.5, 0.0);}if(sub != R_NilValue) {GP->cex = GP->cexbase * GP->cexsub;GP->col = GP->colsub;GP->font = GP->fontsub;if(isExpression(sub))GMMathText(VECTOR(sub)[0], 1, GP->mgp[0]+1.0, 0, (1-adj)*GP->usr[0]+adj*GP->usr[1], 0);elseGMtext(CHAR(STRING(sub)[0]), 1, GP->mgp[0]+1.0, 0, (1-adj)*GP->usr[0]+adj*GP->usr[1], 0);}if(xlab != R_NilValue) {GP->cex = GP->cexbase * GP->cexlab;GP->col = GP->collab;GP->font = GP->fontlab;if(isExpression(xlab))GMMathText(VECTOR(xlab)[0], 1, GP->mgp[0], 0, (1-adj)*GP->usr[0]+adj*GP->usr[1], 0);elseGMtext(CHAR(STRING(xlab)[0]), 1, GP->mgp[0], 0, (1-adj)*GP->usr[0]+adj*GP->usr[1], 0);}if(ylab != R_NilValue) {GP->cex = GP->cexbase * GP->cexlab;GP->col = GP->collab;GP->font = GP->fontlab;if(isExpression(ylab))GMMathText(VECTOR(ylab)[0], 2, GP->mgp[0], 0, (1-adj)*GP->usr[2]+adj*GP->usr[3], 0);elseGMtext(CHAR(STRING(ylab)[0]), 2, GP->mgp[0], 0, (1-adj)*GP->usr[2]+adj*GP->usr[3], 0);}GMode(0);GRestorePars();return R_NilValue;}SEXP do_abline(SEXP call, SEXP op, SEXP args, SEXP env){SEXP a, b, h, v, col, lty;int i, ncol, nlines, nlty;double aa, bb;GCheckState();if(length(args) < 4) errorcall(call, "too few arguments\n");if((a = CAR(args)) != R_NilValue)CAR(args) = a = coerceVector(a, REALSXP);args = CDR(args);if((b = CAR(args)) != R_NilValue)CAR(args) = b = coerceVector(b, REALSXP);args = CDR(args);if((h = CAR(args)) != R_NilValue)CAR(args) = h = coerceVector(h, REALSXP);args = CDR(args);if((v = CAR(args)) != R_NilValue)CAR(args) = v = coerceVector(v, REALSXP);args = CDR(args);PROTECT(col = FixupCol(CAR(args)));ncol = LENGTH(col);args = CDR(args);PROTECT(lty = FixupLty(CAR(args)));nlty = length(lty);args = CDR(args);GSavePars();ProcessInlinePars(args);nlines = 0;if (a != R_NilValue) {if (b == R_NilValue) {if (LENGTH(a) != 2)errorcall(call, "invalid a=, b= specification in \"abline\"\n");aa = REAL(a)[0];bb = REAL(a)[1];}else {aa = asReal(a);bb = asReal(b);}if (!FINITE(aa) || !FINITE(bb))errorcall(call, "\"a\" and \"b\" must be non-missing\n");GP->col = INTEGER(col)[i % ncol];if(nlty && INTEGER(lty)[i % nlty] != NA_INTEGER) GP->lty = INTEGER(lty)[i % nlty];else GP->lty = DP->lty;GMode(1);GStartPath();GMoveTo(XMAP(GP->usr[0]), YMAP(aa + GP->usr[0] * bb));GLineTo(XMAP(GP->usr[1]), YMAP(aa + GP->usr[1] * bb));GEndPath();GMode(0);nlines++;}if (h != R_NilValue) {GMode(1);for (i = 0; i < LENGTH(h); i++) {GP->col = INTEGER(col)[i % ncol];if(nlty && INTEGER(lty)[i % nlty] != NA_INTEGER) GP->lty = INTEGER(lty)[i % nlty];else GP->lty = DP->lty;aa = yt(REAL(h)[i]);if (FINITE(aa)) {GStartPath();GMoveTo(XMAP(GP->usr[0]), YMAP(aa));GLineTo(XMAP(GP->usr[1]), YMAP(aa));GEndPath();}nlines++;}GMode(0);}if (v != R_NilValue) {GMode(1);for (i = 0; i < LENGTH(v); i++) {GP->col = INTEGER(col)[i % ncol];if(nlty && INTEGER(lty)[i % nlty] != NA_INTEGER) GP->lty = INTEGER(lty)[i % nlty];else GP->lty = DP->lty;aa = xt(REAL(v)[i]);if (FINITE(aa)) {GStartPath();GMoveTo(XMAP(aa), YMAP(GP->usr[2]));GLineTo(XMAP(aa), YMAP(GP->usr[3]));GEndPath();}nlines++;}GMode(0);}UNPROTECT(2);GRestorePars();return R_NilValue;}SEXP do_box(SEXP call, SEXP op, SEXP args, SEXP env){int which, col, fg;GCheckState();GSavePars();which = asInteger(CAR(args));if(which < 1 || which > 4)errorcall(call, "invalid \"which\" specification\n");col = GP->col;fg = GP->fg;GP->col = NA_INTEGER;GP->fg = NA_INTEGER;GP->xpd = 1;ProcessInlinePars(args);if(GP->col == NA_REAL) fg = GP->col;if(GP->fg == NA_REAL) fg = GP->fg;GP->col = fg;GMode(1);GBox(which);GMode(0);GRestorePars();return R_NilValue;}SEXP do_locator(SEXP call, SEXP op, SEXP args, SEXP env){SEXP x, y, nobs, ans;int i, n;GCheckState();checkArity(op, args);n = asInteger(CAR(args));if(n <= 0 || n == NA_INTEGER)error("invalid number of points in locator\n");PROTECT(x = allocVector(REALSXP, n));PROTECT(y = allocVector(REALSXP, n));PROTECT(nobs=allocVector(INTSXP,1));i = 0;GMode(2);while(i < n) {if(!GLocator(&(REAL(x)[i]), &(REAL(y)[i]), 1))break;i += 1;}GMode(0);INTEGER(nobs)[0] = i;while(i < n) {REAL(x)[i] = NA_REAL;REAL(y)[i] = NA_REAL;i += 1;}ans = allocList(3);UNPROTECT(3);CAR(ans) = x;CADR(ans) = y;CADDR(ans) = nobs;return ans;}#define THRESHOLD 0.25#ifdef Macintoshdouble hypot(double x, double y){return sqrt(x*x+y*y);}#endifSEXP do_identify(SEXP call, SEXP op, SEXP args, SEXP env){SEXP ans, x, y, l, ind, pos;double xi, yi, xp, yp, d, dmin, offset;int i, imin, k, n;GCheckState();checkArity(op, args);x = CAR(args);y = CADR(args);l = CADDR(args);if(!isReal(x) || !isReal(y) || !isString(l))errorcall(call, "incorrect argument type\n");if(LENGTH(x) != LENGTH(y) || LENGTH(x) != LENGTH(l))errorcall(call, "different argument lengths\n");n = LENGTH(x);if(n <= 0) {R_Visible = 0;return NULL;}offset = xChartoInch(0.5);PROTECT(ind = allocVector(LGLSXP, n));PROTECT(pos = allocVector(INTSXP, n));for(i=0 ; i<n ; i++)LOGICAL(ind)[i] = 0;k = 0;GMode(2);while(k < n) {if(!GLocator(&xp, &yp, 0)) break;dmin = DBL_MAX;imin = -1;for(i=0 ; i<n ; i++) {xi = xt(REAL(x)[i]);yi = yt(REAL(y)[i]);if(!FINITE(xi) || !FINITE(yi)) continue;d = hypot(xFigtoInch(xp-XMAP(xi)), yFigtoInch(yp-YMAP(yi)));if(d < dmin) {imin = i;dmin = d;}}if(dmin > THRESHOLD)REprintf("warning: no point with %.2f inches\n", THRESHOLD);else if(LOGICAL(ind)[imin])REprintf("warning: nearest point already identified\n");else {LOGICAL(ind)[imin] = 1;xi = XMAP(xt(REAL(x)[imin]));yi = YMAP(yt(REAL(y)[imin]));if(fabs(xFigtoInch(xp-xi)) >= fabs(yFigtoInch(yp-yi))) {if(xp >= xi) {INTEGER(pos)[imin] = 4;xi = xi+xInchtoFig(offset);GText(xi, yi, CHAR(STRING(l)[imin]), 0.0, GP->yCharOffset, 0.0);}else {INTEGER(pos)[imin] = 2;xi = xi-xInchtoFig(offset);GText(xi, yi, CHAR(STRING(l)[imin]), 1.0, GP->yCharOffset, 0.0);}}else {if(yp >= yi) {INTEGER(pos)[imin] = 3;yi = yi+yInchtoFig(offset);GText(xi, yi, CHAR(STRING(l)[imin]), 0.5, 0.0, 0.0);}else {INTEGER(pos)[imin] = 1;yi = yi-yInchtoFig(offset);GText(xi, yi, CHAR(STRING(l)[imin]), 0.5, 1-(0.5-GP->yCharOffset), 0.0);}}}}GMode(0);ans = allocList(2);CAR(ans) = ind;CADR(ans) = pos;UNPROTECT(2);return ans;}/* strwidth(str, units) */SEXP do_strwidth(SEXP call, SEXP op, SEXP args, SEXP env){SEXP ans, str;int i, n, units;double cex, cexsave;/* GCheckState(); */checkArity(op, args);str = CAR(args);if(TYPEOF(str) != STRSXP)errorcall(call, "character first argument expected\n");args = CDR(args);if((units = asInteger(CAR(args))) == NA_INTEGER || units < 0)errorcall(call, "invalid units\n");args = CDR(args);if(isNull(CAR(args)))cex = GP->cex;else if(!FINITE(cex = asReal(CAR(args))) || cex <= 0.0)errorcall(call, "invalid cex value\n");n = LENGTH(str);ans = allocVector(REALSXP, n);cexsave = GP->cex;GP->cex = cex * GP->cexbase;for(i=0 ; i<n ; i++)REAL(ans)[i] = GStrWidth(CHAR(STRING(str)[i]), units);GP->cex = cexsave;return ans;}static int n;static int *lptr;static int *rptr;static double *hght;static double *xpos;static double hang;static double offset;static SEXP *llabels;static void drawdend(int node, double *x, double *y){double xl, xr, yl, yr;int k;*y = hght[node-1];k = lptr[node-1];if(k > 0) drawdend(k, &xl, &yl);else {xl = xpos[-k-1];if(hang >= 0) yl = *y - hang;else yl = 0;GText(XMAP(xl), YMAP(yl-offset), CHAR(llabels[-k-1]), 1.0, 0.3, 90.0);}k = rptr[node-1];if(k > 0) drawdend(k, &xr, &yr);else {xr = xpos[-k-1];if(hang >= 0) yr = *y - hang;else yr = 0;GText(XMAP(xr), YMAP(yr-offset), CHAR(llabels[-k-1]), 1.0, 0.3, 90.0);}GStartPath();GMoveTo(XMAP(xl), YMAP(yl));GLineTo(XMAP(xl), YMAP(*y));GLineTo(XMAP(xr), YMAP(*y));GLineTo(XMAP(xr), YMAP(yr));GEndPath();*x = 0.5 * (xl + xr);}SEXP do_dend(SEXP call, SEXP op, SEXP args, SEXP env){int xpdsave;double x, y, ypin;checkArity(op, args);GCheckState();n = asInteger(CAR(args));if(n == NA_INTEGER || n < 2)goto badargs;args = CDR(args);if(TYPEOF(CAR(args)) != INTSXP || length(CAR(args)) != 2*n)goto badargs;lptr = &(INTEGER(CAR(args))[0]);rptr = &(INTEGER(CAR(args))[n]);args = CDR(args);if(TYPEOF(CAR(args)) != REALSXP || length(CAR(args)) != n)goto badargs;hght = REAL(CAR(args));args = CDR(args);if(TYPEOF(CAR(args)) != REALSXP || length(CAR(args)) != n+1)goto badargs;xpos = REAL(CAR(args));args = CDR(args);hang = asReal(CAR(args));if(!FINITE(hang))goto badargs;args = CDR(args);if(TYPEOF(CAR(args)) != STRSXP || length(CAR(args)) != n+1)goto badargs;llabels = STRING(CAR(args));ypin = yNDCtoInch(DP->fig2dev.by / DP->ndc2dev.by) * (DP->plt[3] - DP->plt[2]);offset = GStrWidth("m", 3) * (GP->usr[3] - GP->usr[2]) / ypin;xpdsave = GP->xpd;GP->xpd = 1;GMode(1);drawdend(n, &x, &y);GMode(0);GP->xpd = xpdsave;return R_NilValue;badargs:error("invalid dendrogram input\n");}SEXP do_saveplot(SEXP call, SEXP op, SEXP args, SEXP env){checkArity(op, args);GCheckState();if(!isString(CAR(args)) || length(CAR(args)) < 1 || *CHAR(STRING(CAR(args))[0]) == '\0')errorcall(call, "file name expected as argument\n");GSavePlot(CHAR(STRING(CAR(args))[0]));return R_NilValue;}SEXP do_printplot(SEXP call, SEXP op, SEXP args, SEXP env){checkArity(op, args);GCheckState();GPrintPlot();return R_NilValue;}SEXP do_erase(SEXP call, SEXP op, SEXP args, SEXP env){SEXP col;int ncol;GCheckState();checkArity(op, args);PROTECT(col = FixupCol(CAR(args)));ncol = LENGTH(col);GSavePars();GMode(1);GRect(0.0, 0.0, 1.0, 1.0, INTEGER(col)[0], NA_INTEGER);GMode(0);GRestorePars();UNPROTECT(1);return R_NilValue;}/* text.math(x, y , txt, ...) *//* txt should be an expression object *//* i.e. a last with a class of "expression" */SEXP do_mathtext(SEXP call, SEXP op, SEXP args, SEXP env){int c;double a, d, w;checkArity(op, args);GCheckState();GSavePars();GMode(1);GP->cex = GP->cex * GP->cexbase;/* Note: 4 = Pixels, 3 = Inches *//* I think inches make the most sense *//* To convert to "moveto" / "lineto" units *//* use "xInchtoFig" and "yInchtoFig" *//* to convert the units *//* Basic "pointsize" yardstick */GMetricInfo(0, &a, &d, &w, 4);printf("char = \\0 ascent = %.0f descent = %.0f width = %.0f\n", a, d, w);/* Metrics for 'a' */c = 'a';GMetricInfo(c, &a, &d, &w, 4);printf("char = %c ascent = %.0f descent = %.0f width = %.0f\n", c, a, d, w);GMode(0);GRestorePars();return R_NilValue;}