The R Project SVN R

Rev

Rev 42016 | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 42016 Rev 42208
1
/*
1
/*
2
 *  R : A Computer Language for Statistical Data Analysis
2
 *  R : A Computer Language for Statistical Data Analysis
3
 *  Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
3
 *  Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
4
 *  Copyright (C) 1997--2007  Robert Gentleman, Ross Ihaka and the
4
 *  Copyright (C) 1997--2007  Robert Gentleman, Ross Ihaka and the
5
 *			      R Development Core Team
5
 *			      R Development Core Team
6
 *  Copyright (C) 2002--2004  The R Foundation
6
 *  Copyright (C) 2002--2004  The R Foundation
7
 *
7
 *
8
 *  This program is free software; you can redistribute it and/or modify
8
 *  This program is free software; you can redistribute it and/or modify
9
 *  it under the terms of the GNU General Public License as published by
9
 *  it under the terms of the GNU General Public License as published by
10
 *  the Free Software Foundation; either version 2 of the License, or
10
 *  the Free Software Foundation; either version 2 of the License, or
11
 *  (at your option) any later version.
11
 *  (at your option) any later version.
12
 *
12
 *
13
 *  This program is distributed in the hope that it will be useful,
13
 *  This program is distributed in the hope that it will be useful,
14
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 *  GNU General Public License for more details.
16
 *  GNU General Public License for more details.
17
 *
17
 *
18
 *  A copy of the GNU General Public License is available via WWW at
18
 *  A copy of the GNU General Public License is available via WWW at
19
 *  http://www.gnu.org/copyleft/gpl.html.  You can also obtain it by
19
 *  http://www.gnu.org/copyleft/gpl.html.  You can also obtain it by
20
 *  writing to the Free Software Foundation, Inc., 51 Franklin Street
20
 *  writing to the Free Software Foundation, Inc., 51 Franklin Street
21
 *  Fifth Floor, Boston, MA 02110-1301  USA.
21
 *  Fifth Floor, Boston, MA 02110-1301  USA.
22
 */
22
 */
23
 
23
 
24
/* <UTF8> char here is either ASCII or handled as a whole */
24
/* <UTF8> char here is either ASCII or handled as a whole */
25
 
25
 
26
#ifdef HAVE_CONFIG_H
26
#ifdef HAVE_CONFIG_H
27
#include <config.h>
27
#include <config.h>
28
#endif
28
#endif
29
 
29
 
30
#include <Defn.h>
30
#include <Defn.h>
31
#include <Graphics.h>
31
#include <Graphics.h>
32
#include <Rdevices.h>
32
#include <Rdevices.h>
33
#include <Print.h>
33
#include <Print.h>
34
 
34
 
35
#define imax2(x, y) ((x < y) ? y : x)
35
#define imax2(x, y) ((x < y) ? y : x)
36
 
36
 
37
static R_INLINE double fmin2(double x, double y)
37
static R_INLINE double fmin2(double x, double y)
38
{
38
{
39
	if (ISNAN(x) || ISNAN(y))
39
	if (ISNAN(x) || ISNAN(y))
40
		return x + y;
40
		return x + y;
41
	return (x < y) ? x : y;
41
	return (x < y) ? x : y;
42
}
42
}
43
 
43
 
44
static R_INLINE double fmax2(double x, double y)
44
static R_INLINE double fmax2(double x, double y)
45
{
45
{
46
	if (ISNAN(x) || ISNAN(y))
46
	if (ISNAN(x) || ISNAN(y))
47
		return x + y;
47
		return x + y;
48
	return (x < y) ? y : x;
48
	return (x < y) ? y : x;
49
}
49
}
50
 
50
 
51
#ifndef HAVE_HYPOT
51
#ifndef HAVE_HYPOT
52
# define hypot pythag
52
# define hypot pythag
53
#endif
53
#endif
54
 
54
 
55
/* FIXME:  NewFrameConfirm should be a standard device function */
55
/* FIXME:  NewFrameConfirm should be a standard device function */
56
#ifdef Win32
56
#ifdef Win32
57
Rboolean winNewFrameConfirm(void);
57
Rboolean winNewFrameConfirm(void);
58
#endif
58
#endif
59
 
59
 
60
void NewFrameConfirm(void)
60
void NewFrameConfirm(void)
61
{
61
{
62
    unsigned char buf[16];
62
    unsigned char buf[16];
63
#ifdef Win32
63
#ifdef Win32
64
	int i;
64
	int i;
65
	Rboolean haveWindowsDevice;
65
	Rboolean haveWindowsDevice;
66
	SEXP dotDevices = findVar(install(".Devices"), R_BaseEnv); /* This is a pairlist! */
66
	SEXP dotDevices = findVar(install(".Devices"), R_BaseEnv); /* This is a pairlist! */
67
#endif
67
#endif
68
 
68
 
69
    if(!R_Interactive) return;
69
    if(!R_Interactive) return;
70
#ifdef Win32
70
#ifdef Win32
71
    for(i = 0; i < curDevice(); i++)  /* 0-based */
71
    for(i = 0; i < curDevice(); i++)  /* 0-based */
72
	dotDevices = CDR(dotDevices);
72
	dotDevices = CDR(dotDevices);
73
    haveWindowsDevice =
73
    haveWindowsDevice =
74
	strcmp(CHAR(STRING_ELT(CAR(dotDevices), 0)), "windows") == 0;
74
	strcmp(CHAR(STRING_ELT(CAR(dotDevices), 0)), "windows") == 0;
75
    
75
    
76
    if (!haveWindowsDevice || !winNewFrameConfirm())
76
    if (!haveWindowsDevice || !winNewFrameConfirm())
77
#endif
77
#endif
78
	R_ReadConsole(_("Hit <Return> to see next plot: "), buf, 16, 0);
78
	R_ReadConsole(_("Hit <Return> to see next plot: "), buf, 16, 0);
79
}
79
}
80
 
80
 
81
	/* Remember: +1 and/or -1 because C arrays are */
81
	/* Remember: +1 and/or -1 because C arrays are */
82
	/* zero-based and R-vectors are one-based. */
82
	/* zero-based and R-vectors are one-based. */
83
 
83
 
84
#define checkArity_length					\
84
#define checkArity_length					\
85
    checkArity(op, args);					\
85
    checkArity(op, args);					\
86
    if(!LENGTH(CAR(args)))					\
86
    if(!LENGTH(CAR(args)))					\
87
	error(_("argument must have positive length"))
87
	error(_("argument must have positive length"))
88
 
88
 
89
 
89
 
90
SEXP attribute_hidden do_devcontrol(SEXP call, SEXP op, SEXP args, SEXP env)
90
SEXP attribute_hidden do_devcontrol(SEXP call, SEXP op, SEXP args, SEXP env)
91
{
91
{
92
    int listFlag;
92
    int listFlag;
93
 
93
 
94
    checkArity(op, args);
94
    checkArity(op, args);
95
    if(PRIMVAL(op) == 0) { /* dev.control */
95
    if(PRIMVAL(op) == 0) { /* dev.control */
96
	listFlag = asLogical(CAR(args));
96
	listFlag = asLogical(CAR(args));
97
	if(listFlag == NA_LOGICAL) error(_("invalid argument"));
97
	if(listFlag == NA_LOGICAL) error(_("invalid argument"));
98
	if(listFlag)
98
	if(listFlag)
99
	    enableDisplayList(CurrentDevice());
99
	    enableDisplayList(CurrentDevice());
100
	else
100
	else
101
	    inhibitDisplayList(CurrentDevice());
101
	    inhibitDisplayList(CurrentDevice());
102
    } else { /* dev.displaylist */
102
    } else { /* dev.displaylist */
103
	GEDevDesc *dd = (GEDevDesc*)CurrentDevice();
103
	GEDevDesc *dd = (GEDevDesc*)CurrentDevice();
104
	listFlag = dd->dev->displayListOn;
104
	listFlag = dd->dev->displayListOn;
105
    }
105
    }
106
    return ScalarLogical(listFlag);
106
    return ScalarLogical(listFlag);
107
}
107
}
108
 
108
 
109
SEXP attribute_hidden do_devcopy(SEXP call, SEXP op, SEXP args, SEXP env)
109
SEXP attribute_hidden do_devcopy(SEXP call, SEXP op, SEXP args, SEXP env)
110
{
110
{
111
    checkArity_length;
111
    checkArity_length;
112
    GEcopyDisplayList(INTEGER(CAR(args))[0] - 1);
112
    GEcopyDisplayList(INTEGER(CAR(args))[0] - 1);
113
    return R_NilValue;
113
    return R_NilValue;
114
}
114
}
115
 
115
 
116
SEXP attribute_hidden do_devcur(SEXP call, SEXP op, SEXP args, SEXP env)
116
SEXP attribute_hidden do_devcur(SEXP call, SEXP op, SEXP args, SEXP env)
117
{
117
{
118
    checkArity(op, args);
118
    checkArity(op, args);
119
    return ScalarInteger(curDevice() + 1);
119
    return ScalarInteger(curDevice() + 1);
120
}
120
}
121
 
121
 
122
SEXP attribute_hidden do_devnext(SEXP call, SEXP op, SEXP args, SEXP env)
122
SEXP attribute_hidden do_devnext(SEXP call, SEXP op, SEXP args, SEXP env)
123
{
123
{
124
    checkArity_length;
124
    checkArity_length;
125
    return ScalarInteger( nextDevice(INTEGER(CAR(args))[0] - 1) + 1 );
125
    return ScalarInteger( nextDevice(INTEGER(CAR(args))[0] - 1) + 1 );
126
}
126
}
127
 
127
 
128
SEXP attribute_hidden do_devprev(SEXP call, SEXP op, SEXP args, SEXP env)
128
SEXP attribute_hidden do_devprev(SEXP call, SEXP op, SEXP args, SEXP env)
129
{
129
{
130
    checkArity_length;
130
    checkArity_length;
131
    return ScalarInteger( prevDevice(INTEGER(CAR(args))[0] - 1) + 1 );
131
    return ScalarInteger( prevDevice(INTEGER(CAR(args))[0] - 1) + 1 );
132
}
132
}
133
 
133
 
134
SEXP attribute_hidden do_devset(SEXP call, SEXP op, SEXP args, SEXP env)
134
SEXP attribute_hidden do_devset(SEXP call, SEXP op, SEXP args, SEXP env)
135
{
135
{
136
    int devNum = INTEGER(CAR(args))[0] - 1;
136
    int devNum = INTEGER(CAR(args))[0] - 1;
137
    checkArity(op, args);
137
    checkArity(op, args);
138
    return ScalarInteger( selectDevice(devNum) + 1 );
138
    return ScalarInteger( selectDevice(devNum) + 1 );
139
}
139
}
140
 
140
 
141
SEXP attribute_hidden do_devoff(SEXP call, SEXP op, SEXP args, SEXP env)
141
SEXP attribute_hidden do_devoff(SEXP call, SEXP op, SEXP args, SEXP env)
142
{
142
{
143
    checkArity_length;
143
    checkArity_length;
144
    killDevice(INTEGER(CAR(args))[0] - 1);
144
    killDevice(INTEGER(CAR(args))[0] - 1);
145
    return R_NilValue;
145
    return R_NilValue;
146
}
146
}
147
 
147
 
148
 
148
 
149
/*  P A R A M E T E R	 U T I L I T I E S  */
149
/*  P A R A M E T E R	 U T I L I T I E S  */
150
 
150
 
151
 
151
 
152
/* ProcessInLinePars handles inline par specifications in graphics functions.
152
/* ProcessInLinePars handles inline par specifications in graphics functions.
153
 * It does this by calling Specify2() from ./par.c */
153
 * It does this by calling Specify2() from ./par.c */
154
 
154
 
155
attribute_hidden
155
attribute_hidden
156
void ProcessInlinePars(SEXP s, DevDesc *dd, SEXP call)
156
void ProcessInlinePars(SEXP s, DevDesc *dd, SEXP call)
157
{
157
{
158
    if (isList(s)) {
158
    if (isList(s)) {
159
	while (s != R_NilValue) {
159
	while (s != R_NilValue) {
160
	    if (isList(CAR(s)))
160
	    if (isList(CAR(s)))
161
		ProcessInlinePars(CAR(s), dd, call);
161
		ProcessInlinePars(CAR(s), dd, call);
162
	    else if (TAG(s) != R_NilValue)
162
	    else if (TAG(s) != R_NilValue)
163
		Specify2(CHAR(PRINTNAME(TAG(s))), CAR(s), dd, call);
163
		Specify2(CHAR(PRINTNAME(TAG(s))), CAR(s), dd, call);
164
	    s = CDR(s);
164
	    s = CDR(s);
165
	}
165
	}
166
    }
166
    }
167
}
167
}
168
 
168
 
169
/*
169
/*
170
 * Extract specified par from list of inline pars
170
 * Extract specified par from list of inline pars
171
 */
171
 */
172
static SEXP getInlinePar(SEXP s, char *name)
172
static SEXP getInlinePar(SEXP s, char *name)
173
{
173
{
174
    SEXP result = R_NilValue;
174
    SEXP result = R_NilValue;
175
    int found = 0;
175
    int found = 0;
176
    if (isList(s) && !found) {
176
    if (isList(s) && !found) {
177
	while (s != R_NilValue) {
177
	while (s != R_NilValue) {
178
	    if (isList(CAR(s))) {
178
	    if (isList(CAR(s))) {
179
		result = getInlinePar(CAR(s), name);
179
		result = getInlinePar(CAR(s), name);
180
		if (result)
180
		if (result)
181
		    found = 1;
181
		    found = 1;
182
	    } else
182
	    } else
183
		if (TAG(s) != R_NilValue)
183
		if (TAG(s) != R_NilValue)
184
		    if (!strcmp(CHAR(PRINTNAME(TAG(s))), name)) {
184
		    if (!strcmp(CHAR(PRINTNAME(TAG(s))), name)) {
185
			result = CAR(s);
185
			result = CAR(s);
186
			found = 1;
186
			found = 1;
187
		    }
187
		    }
188
	    s = CDR(s);
188
	    s = CDR(s);
189
	}
189
	}
190
    }
190
    }
191
    return result;
191
    return result;
192
}
192
}
193
 
193
 
194
attribute_hidden
194
attribute_hidden
195
SEXP FixupPch(SEXP pch, int dflt)
195
SEXP FixupPch(SEXP pch, int dflt)
196
{
196
{
197
    int i, n;
197
    int i, n;
198
    SEXP ans = R_NilValue;/* -Wall*/
198
    SEXP ans = R_NilValue;/* -Wall*/
199
 
199
 
200
    n = length(pch);
200
    n = length(pch);
201
    if (n == 0) {
201
    if (n == 0) {
202
	ans = ScalarInteger(dflt);
202
	ans = ScalarInteger(dflt);
203
    }
203
    }
204
    else if (isList(pch)) {
204
    else if (isList(pch)) {
205
	ans = allocVector(INTSXP, n);
205
	ans = allocVector(INTSXP, n);
206
	for (i = 0; pch != R_NilValue;	pch = CDR(pch))
206
	for (i = 0; pch != R_NilValue;	pch = CDR(pch))
207
	    INTEGER(ans)[i++] = asInteger(CAR(pch));
207
	    INTEGER(ans)[i++] = asInteger(CAR(pch));
208
    }
208
    }
209
    else if (isInteger(pch)) {
209
    else if (isInteger(pch)) {
210
	ans = allocVector(INTSXP, n);
210
	ans = allocVector(INTSXP, n);
211
	for (i = 0; i < n; i++)
211
	for (i = 0; i < n; i++)
212
	    INTEGER(ans)[i] = INTEGER(pch)[i];
212
	    INTEGER(ans)[i] = INTEGER(pch)[i];
213
    }
213
    }
214
    else if (isReal(pch)) {
214
    else if (isReal(pch)) {
215
	ans = allocVector(INTSXP, n);
215
	ans = allocVector(INTSXP, n);
216
	for (i = 0; i < n; i++)
216
	for (i = 0; i < n; i++)
217
	    INTEGER(ans)[i] = R_FINITE(REAL(pch)[i]) ?
217
	    INTEGER(ans)[i] = R_FINITE(REAL(pch)[i]) ?
218
		REAL(pch)[i] : NA_INTEGER;
218
		REAL(pch)[i] : NA_INTEGER;
219
    }
219
    }
220
    else if (isString(pch)) {
220
    else if (isString(pch)) {
221
	ans = allocVector(INTSXP, n);
221
	ans = allocVector(INTSXP, n);
222
	for (i = 0; i < n; i++) {
222
	for (i = 0; i < n; i++) {
223
	    if(STRING_ELT(pch, i) == NA_STRING ||
223
	    if(STRING_ELT(pch, i) == NA_STRING ||
224
	       CHAR(STRING_ELT(pch, i))[0] == '\0') { /* pch = "" */
224
	       CHAR(STRING_ELT(pch, i))[0] == '\0') { /* pch = "" */
225
		INTEGER(ans)[i] = NA_INTEGER;
225
		INTEGER(ans)[i] = NA_INTEGER;
226
	    } else {
226
	    } else {
227
#ifdef SUPPORT_MBCS
227
#ifdef SUPPORT_MBCS
228
		if(mbcslocale) {
228
		if(mbcslocale) {
229
		    wchar_t wc;
229
		    wchar_t wc;
230
		    if(mbrtowc(&wc, translateChar(STRING_ELT(pch, i)),
230
		    if(mbrtowc(&wc, translateChar(STRING_ELT(pch, i)),
231
			       MB_CUR_MAX, NULL) > 0) INTEGER(ans)[i] = wc;
231
			       MB_CUR_MAX, NULL) > 0) INTEGER(ans)[i] = wc;
232
		    else
232
		    else
233
			error(_("invalid multibyte char in pch=\"c\""));
233
			error(_("invalid multibyte char in pch=\"c\""));
234
		} else
234
		} else
235
#endif
235
#endif
236
		    INTEGER(ans)[i] = translateChar(STRING_ELT(pch, i))[0];
236
		    INTEGER(ans)[i] = translateChar(STRING_ELT(pch, i))[0];
237
	    }
237
	    }
238
	}
238
	}
239
    }
239
    }
240
    else if (isLogical(pch)) {/* NA, but not TRUE/FALSE */
240
    else if (isLogical(pch)) {/* NA, but not TRUE/FALSE */
241
	ans = allocVector(INTSXP, n);
241
	ans = allocVector(INTSXP, n);
242
	for (i = 0; i < n; i++)
242
	for (i = 0; i < n; i++)
243
	    if(LOGICAL(pch)[i] == NA_LOGICAL)
243
	    if(LOGICAL(pch)[i] == NA_LOGICAL)
244
		INTEGER(ans)[i] = NA_INTEGER;
244
		INTEGER(ans)[i] = NA_INTEGER;
245
	    else error(_("only NA allowed in logical plotting symbol"));
245
	    else error(_("only NA allowed in logical plotting symbol"));
246
    }
246
    }
247
    else error(_("invalid plotting symbol"));
247
    else error(_("invalid plotting symbol"));
248
    for (i = 0; i < n; i++) {
248
    for (i = 0; i < n; i++) {
249
	if (INTEGER(ans)[i] < 0 && INTEGER(ans)[i] != NA_INTEGER)
249
	if (INTEGER(ans)[i] < 0 && INTEGER(ans)[i] != NA_INTEGER)
250
	    INTEGER(ans)[i] = dflt;
250
	    INTEGER(ans)[i] = dflt;
251
    }
251
    }
252
    return ans;
252
    return ans;
253
}
253
}
254
 
254
 
255
attribute_hidden
255
attribute_hidden
256
SEXP FixupLty(SEXP lty, int dflt)
256
SEXP FixupLty(SEXP lty, int dflt)
257
{
257
{
258
    int i, n;
258
    int i, n;
259
    SEXP ans;
259
    SEXP ans;
260
    n = length(lty);
260
    n = length(lty);
261
    if (n == 0) {
261
    if (n == 0) {
262
	ans = ScalarInteger(dflt);
262
	ans = ScalarInteger(dflt);
263
    }
263
    }
264
    else {
264
    else {
265
	ans = allocVector(INTSXP, n);
265
	ans = allocVector(INTSXP, n);
266
	for (i = 0; i < n; i++)
266
	for (i = 0; i < n; i++)
267
	    INTEGER(ans)[i] = LTYpar(lty, i);
267
	    INTEGER(ans)[i] = LTYpar(lty, i);
268
    }
268
    }
269
    return ans;
269
    return ans;
270
}
270
}
271
 
271
 
272
attribute_hidden
272
attribute_hidden
273
SEXP FixupLwd(SEXP lwd, double dflt)
273
SEXP FixupLwd(SEXP lwd, double dflt)
274
{
274
{
275
    int i, n;
275
    int i, n;
276
    double w;
276
    double w;
277
    SEXP ans = NULL;
277
    SEXP ans = NULL;
278
 
278
 
279
    n = length(lwd);
279
    n = length(lwd);
280
    if (n == 0)
280
    if (n == 0)
281
	ans = ScalarReal(dflt);
281
	ans = ScalarReal(dflt);
282
    else {
282
    else {
283
	PROTECT(lwd = coerceVector(lwd, REALSXP));
283
	PROTECT(lwd = coerceVector(lwd, REALSXP));
284
	n = length(lwd);
284
	n = length(lwd);
285
	ans = allocVector(REALSXP, n);
285
	ans = allocVector(REALSXP, n);
286
	for (i = 0; i < n; i++) {
286
	for (i = 0; i < n; i++) {
287
	    w = REAL(lwd)[i];
287
	    w = REAL(lwd)[i];
288
	    if (w < 0) w = NA_REAL;
288
	    if (w < 0) w = NA_REAL;
289
	    REAL(ans)[i] = w;
289
	    REAL(ans)[i] = w;
290
 
290
 
291
	}
291
	}
292
	UNPROTECT(1);
292
	UNPROTECT(1);
293
    }
293
    }
294
    return ans;
294
    return ans;
295
}
295
}
296
 
296
 
297
attribute_hidden
297
attribute_hidden
298
SEXP FixupFont(SEXP font, int dflt)
298
SEXP FixupFont(SEXP font, int dflt)
299
{
299
{
300
    int i, k, n;
300
    int i, k, n;
301
    SEXP ans = R_NilValue;/* -Wall*/
301
    SEXP ans = R_NilValue;/* -Wall*/
302
    n = length(font);
302
    n = length(font);
303
    if (n == 0) {
303
    if (n == 0) {
304
	ans = ScalarInteger(dflt);
304
	ans = ScalarInteger(dflt);
305
    }
305
    }
306
    else if (isLogical(font)) {
306
    else if (isLogical(font)) {
307
	ans = allocVector(INTSXP, n);
307
	ans = allocVector(INTSXP, n);
308
	for (i = 0; i < n; i++) {
308
	for (i = 0; i < n; i++) {
309
	    k = LOGICAL(font)[i];
309
	    k = LOGICAL(font)[i];
310
#ifndef Win32
310
#ifndef Win32
311
	    if (k < 1 || k > 5) k = NA_INTEGER;
311
	    if (k < 1 || k > 5) k = NA_INTEGER;
312
#else
312
#else
313
	    if (k < 1 || k > 32) k = NA_INTEGER;
313
	    if (k < 1 || k > 32) k = NA_INTEGER;
314
#endif
314
#endif
315
	    INTEGER(ans)[i] = k;
315
	    INTEGER(ans)[i] = k;
316
	}
316
	}
317
    }
317
    }
318
    else if (isInteger(font)) {
318
    else if (isInteger(font)) {
319
        ans = allocVector(INTSXP, n);
319
        ans = allocVector(INTSXP, n);
320
        for (i = 0; i < n; i++) {
320
        for (i = 0; i < n; i++) {
321
            k = INTEGER(font)[i];
321
            k = INTEGER(font)[i];
322
#ifndef Win32
322
#ifndef Win32
323
            if (k < 1 || k > 5) k = NA_INTEGER;
323
            if (k < 1 || k > 5) k = NA_INTEGER;
324
#else
324
#else
325
            if (k < 1 || k > 32) k = NA_INTEGER;
325
            if (k < 1 || k > 32) k = NA_INTEGER;
326
#endif
326
#endif
327
            INTEGER(ans)[i] = k;
327
            INTEGER(ans)[i] = k;
328
        }
328
        }
329
    }
329
    }
330
    else if (isReal(font)) {
330
    else if (isReal(font)) {
331
	ans = allocVector(INTSXP, n);
331
	ans = allocVector(INTSXP, n);
332
	for (i = 0; i < n; i++) {
332
	for (i = 0; i < n; i++) {
333
	    k = REAL(font)[i];
333
	    k = REAL(font)[i];
334
#ifndef Win32
334
#ifndef Win32
335
	    if (k < 1 || k > 5) k = NA_INTEGER;
335
	    if (k < 1 || k > 5) k = NA_INTEGER;
336
#else
336
#else
337
	    if (k < 1 || k > 32) k = NA_INTEGER;
337
	    if (k < 1 || k > 32) k = NA_INTEGER;
338
#endif
338
#endif
339
	    INTEGER(ans)[i] = k;
339
	    INTEGER(ans)[i] = k;
340
	}
340
	}
341
    }
341
    }
342
    else error(_("invalid font specification"));
342
    else error(_("invalid font specification"));
343
    return ans;
343
    return ans;
344
}
344
}
345
 
345
 
346
attribute_hidden
346
attribute_hidden
347
SEXP FixupCol(SEXP col, unsigned int dflt)
347
SEXP FixupCol(SEXP col, unsigned int dflt)
348
{
348
{
349
    int i, n;
349
    int i, n;
350
    SEXP ans;
350
    SEXP ans;
351
    n = length(col);
351
    n = length(col);
352
    if (n == 0) {
352
    if (n == 0) {
353
	PROTECT(ans = ScalarInteger(dflt));
353
	PROTECT(ans = ScalarInteger(dflt));
354
    }
354
    }
355
    else {
355
    else {
356
	ans = PROTECT(allocVector(INTSXP, n));
356
	ans = PROTECT(allocVector(INTSXP, n));
357
	if (isList(col))
357
	if (isList(col))
358
	    for (i = 0; i < n; i++) {
358
	    for (i = 0; i < n; i++) {
359
		INTEGER(ans)[i] = RGBpar(CAR(col), 0);
359
		INTEGER(ans)[i] = RGBpar(CAR(col), 0);
360
		col = CDR(col);
360
		col = CDR(col);
361
	    }
361
	    }
362
	else
362
	else
363
	    for (i = 0; i < n; i++)
363
	    for (i = 0; i < n; i++)
364
		INTEGER(ans)[i] = RGBpar(col, i);
364
		INTEGER(ans)[i] = RGBpar(col, i);
365
    }
365
    }
366
    UNPROTECT(1);
366
    UNPROTECT(1);
367
    return ans;
367
    return ans;
368
}
368
}
369
 
369
 
370
attribute_hidden
370
attribute_hidden
371
SEXP FixupCex(SEXP cex, double dflt)
371
SEXP FixupCex(SEXP cex, double dflt)
372
{
372
{
373
    SEXP ans;
373
    SEXP ans;
374
    int i, n;
374
    int i, n;
375
    n = length(cex);
375
    n = length(cex);
376
    if (n == 0) {
376
    if (n == 0) {
377
	ans = allocVector(REALSXP, 1);
377
	ans = allocVector(REALSXP, 1);
378
	if (R_FINITE(dflt) && dflt > 0)
378
	if (R_FINITE(dflt) && dflt > 0)
379
	    REAL(ans)[0] = dflt;
379
	    REAL(ans)[0] = dflt;
380
	else
380
	else
381
	    REAL(ans)[0] = NA_REAL;
381
	    REAL(ans)[0] = NA_REAL;
382
    }
382
    }
383
    else {
383
    else {
384
	double c;
384
	double c;
385
	ans = allocVector(REALSXP, n);
385
	ans = allocVector(REALSXP, n);
386
	if (isReal(cex))
386
	if (isReal(cex))
387
	    for (i = 0; i < n; i++) {
387
	    for (i = 0; i < n; i++) {
388
		c = REAL(cex)[i];
388
		c = REAL(cex)[i];
389
		if (R_FINITE(c) && c > 0)
389
		if (R_FINITE(c) && c > 0)
390
		    REAL(ans)[i] = c;
390
		    REAL(ans)[i] = c;
391
		else
391
		else
392
		    REAL(ans)[i] = NA_REAL;
392
		    REAL(ans)[i] = NA_REAL;
393
	    }
393
	    }
394
	else if (isInteger(cex) || isLogical(cex))
394
	else if (isInteger(cex) || isLogical(cex))
395
 
395
 
396
	    for (i = 0; i < n; i++) {
396
	    for (i = 0; i < n; i++) {
397
		c = INTEGER(cex)[i];
397
		c = INTEGER(cex)[i];
398
		if (c == NA_INTEGER || c <= 0)
398
		if (c == NA_INTEGER || c <= 0)
399
		    c = NA_REAL;
399
		    c = NA_REAL;
400
		REAL(ans)[i] = c;
400
		REAL(ans)[i] = c;
401
	    }
401
	    }
402
    }
402
    }
403
    return ans;
403
    return ans;
404
}
404
}
405
 
405
 
406
attribute_hidden
406
attribute_hidden
407
SEXP FixupVFont(SEXP vfont) {
407
SEXP FixupVFont(SEXP vfont) {
408
    SEXP ans = R_NilValue;
408
    SEXP ans = R_NilValue;
409
    if (!isNull(vfont)) {
409
    if (!isNull(vfont)) {
410
	SEXP vf;
410
	SEXP vf;
411
	int typeface, fontindex;
411
	int typeface, fontindex;
412
	int minindex, maxindex=0;/* -Wall*/
412
	int minindex, maxindex=0;/* -Wall*/
413
	int i;
413
	int i;
414
	PROTECT(vf = coerceVector(vfont, INTSXP));
414
	PROTECT(vf = coerceVector(vfont, INTSXP));
415
	if (length(vf) != 2)
415
	if (length(vf) != 2)
416
	    error(_("invalid '%s' value"), "vfont");
416
	    error(_("invalid '%s' value"), "vfont");
417
	typeface = INTEGER(vf)[0];
417
	typeface = INTEGER(vf)[0];
418
	if (typeface < 0 || typeface > 7)
418
	if (typeface < 0 || typeface > 7)
419
	    error(_("invalid 'vfont' value [typeface]"));
419
	    error(_("invalid 'vfont' value [typeface]"));
420
	/* For each of the typefaces {0..7}, there are several fontindices
420
	/* For each of the typefaces {0..7}, there are several fontindices
421
	   available; how many depends on the typeface.
421
	   available; how many depends on the typeface.
422
	   The possible combinations are "given" in ./g_fontdb.c
422
	   The possible combinations are "given" in ./g_fontdb.c
423
	   and also listed in help(Hershey).
423
	   and also listed in help(Hershey).
424
	 */
424
	 */
425
	minindex = 1;
425
	minindex = 1;
426
	switch (typeface) {
426
	switch (typeface) {
427
	case 0: /* serif */
427
	case 0: /* serif */
428
	    maxindex = 7;	    break;
428
	    maxindex = 7;	    break;
429
	case 1: /* sans serif */
429
	case 1: /* sans serif */
430
	case 6: /* serif symbol */
430
	case 6: /* serif symbol */
431
	    maxindex = 4;	    break;
431
	    maxindex = 4;	    break;
432
	case 2: /* script */
432
	case 2: /* script */
433
	    maxindex = 3;	    break;
433
	    maxindex = 3;	    break;
434
	case 3: /* gothic english */
434
	case 3: /* gothic english */
435
	case 4: /* gothic german */
435
	case 4: /* gothic german */
436
	case 5: /* gothic italian */
436
	case 5: /* gothic italian */
437
	    maxindex = 1;	    break;
437
	    maxindex = 1;	    break;
438
	case 7: /* sans serif symbol */
438
	case 7: /* sans serif symbol */
439
	    maxindex = 2;
439
	    maxindex = 2;
440
	}
440
	}
441
	fontindex = INTEGER(vf)[1];
441
	fontindex = INTEGER(vf)[1];
442
	if (fontindex < minindex || fontindex > maxindex)
442
	if (fontindex < minindex || fontindex > maxindex)
443
	    error(_("invalid 'vfont' value [fontindex]"));
443
	    error(_("invalid 'vfont' value [fontindex]"));
444
	ans = allocVector(INTSXP, 2);
444
	ans = allocVector(INTSXP, 2);
445
	for (i=0; i<2; i++)
445
	for (i=0; i<2; i++)
446
	    INTEGER(ans)[i] = INTEGER(vf)[i];
446
	    INTEGER(ans)[i] = INTEGER(vf)[i];
447
	UNPROTECT(1);
447
	UNPROTECT(1);
448
    }
448
    }
449
    return ans;
449
    return ans;
450
}
450
}
451
 
451
 
452
/* GetTextArg() : extract from call and possibly set text arguments
452
/* GetTextArg() : extract from call and possibly set text arguments
453
 *  ("label", col=, cex=, font=)
453
 *  ("label", col=, cex=, font=)
454
 *
454
 *
455
 * Main purpose: Treat things like  title(main = list("This Title", font= 4))
455
 * Main purpose: Treat things like  title(main = list("This Title", font= 4))
456
 *
456
 *
457
 * Called from	do_title()  [only, currently]
457
 * Called from	do_title()  [only, currently]
458
 */
458
 */
459
static void
459
static void
460
GetTextArg(SEXP call, SEXP spec, SEXP *ptxt,
460
GetTextArg(SEXP call, SEXP spec, SEXP *ptxt,
461
	   int *pcol, double *pcex, int *pfont)
461
	   int *pcol, double *pcex, int *pfont)
462
{
462
{
463
    int i, n, col, font, colspecd;
463
    int i, n, col, font, colspecd;
464
    double cex;
464
    double cex;
465
    SEXP txt, nms;
465
    SEXP txt, nms;
466
    PROTECT_INDEX pi;
466
    PROTECT_INDEX pi;
467
 
467
 
468
    txt	  = R_NilValue;
468
    txt	  = R_NilValue;
469
    cex	  = NA_REAL;
469
    cex	  = NA_REAL;
470
    col	  = R_TRANWHITE;
470
    col	  = R_TRANWHITE;
471
    colspecd = 0;
471
    colspecd = 0;
472
    font  = NA_INTEGER;
472
    font  = NA_INTEGER;
473
    /* It doesn't look as if this protection is needed */
473
    /* It doesn't look as if this protection is needed */
474
    PROTECT_WITH_INDEX(txt, &pi);
474
    PROTECT_WITH_INDEX(txt, &pi);
475
 
475
 
476
    switch (TYPEOF(spec)) {
476
    switch (TYPEOF(spec)) {
477
    case LANGSXP:
477
    case LANGSXP:
478
    case SYMSXP:
478
    case SYMSXP:
479
	REPROTECT(txt = coerceVector(spec, EXPRSXP), pi);
479
	REPROTECT(txt = coerceVector(spec, EXPRSXP), pi);
480
	break;
480
	break;
481
    case VECSXP:
481
    case VECSXP:
482
	if (length(spec) == 0) {
482
	if (length(spec) == 0) {
483
	    *ptxt = R_NilValue;
483
	    *ptxt = R_NilValue;
484
	}
484
	}
485
	else {
485
	else {
486
	    nms = getAttrib(spec, R_NamesSymbol);
486
	    nms = getAttrib(spec, R_NamesSymbol);
487
	    if (nms == R_NilValue){ /* PR#1939 */
487
	    if (nms == R_NilValue){ /* PR#1939 */
488
	       txt = VECTOR_ELT(spec, 0);
488
	       txt = VECTOR_ELT(spec, 0);
489
	       if (TYPEOF(txt) == LANGSXP || TYPEOF(txt) == SYMSXP )
489
	       if (TYPEOF(txt) == LANGSXP || TYPEOF(txt) == SYMSXP )
490
		    REPROTECT(txt = coerceVector(txt, EXPRSXP), pi);
490
		    REPROTECT(txt = coerceVector(txt, EXPRSXP), pi);
491
	       else if (!isExpression(txt))
491
	       else if (!isExpression(txt))
492
		    REPROTECT(txt = coerceVector(txt, STRSXP), pi);
492
		    REPROTECT(txt = coerceVector(txt, STRSXP), pi);
493
	    } else {
493
	    } else {
494
	       n = length(nms);
494
	       n = length(nms);
495
	       for (i = 0; i < n; i++) {
495
	       for (i = 0; i < n; i++) {
496
		if (!strcmp(CHAR(STRING_ELT(nms, i)), "cex")) {
496
		if (!strcmp(CHAR(STRING_ELT(nms, i)), "cex")) {
497
		    cex = asReal(VECTOR_ELT(spec, i));
497
		    cex = asReal(VECTOR_ELT(spec, i));
498
		}
498
		}
499
		else if (!strcmp(CHAR(STRING_ELT(nms, i)), "col")) {
499
		else if (!strcmp(CHAR(STRING_ELT(nms, i)), "col")) {
500
		    SEXP colsxp = VECTOR_ELT(spec, i);
500
		    SEXP colsxp = VECTOR_ELT(spec, i);
501
		    if (!isNAcol(colsxp, 0, LENGTH(colsxp))) {
501
		    if (!isNAcol(colsxp, 0, LENGTH(colsxp))) {
502
			col = asInteger(FixupCol(colsxp, R_TRANWHITE));
502
			col = asInteger(FixupCol(colsxp, R_TRANWHITE));
503
			colspecd = 1;
503
			colspecd = 1;
504
		    }
504
		    }
505
		}
505
		}
506
		else if (!strcmp(CHAR(STRING_ELT(nms, i)), "font")) {
506
		else if (!strcmp(CHAR(STRING_ELT(nms, i)), "font")) {
507
		    font = asInteger(FixupFont(VECTOR_ELT(spec, i), NA_INTEGER));
507
		    font = asInteger(FixupFont(VECTOR_ELT(spec, i), NA_INTEGER));
508
		}
508
		}
509
		else if (!strcmp(CHAR(STRING_ELT(nms, i)), "")) {
509
		else if (!strcmp(CHAR(STRING_ELT(nms, i)), "")) {
510
		    txt = VECTOR_ELT(spec, i);
510
		    txt = VECTOR_ELT(spec, i);
511
		    if (TYPEOF(txt) == LANGSXP || TYPEOF(txt) == SYMSXP)
511
		    if (TYPEOF(txt) == LANGSXP || TYPEOF(txt) == SYMSXP)
512
			REPROTECT(txt = coerceVector(txt, EXPRSXP), pi);
512
			REPROTECT(txt = coerceVector(txt, EXPRSXP), pi);
513
		    else if (!isExpression(txt))
513
		    else if (!isExpression(txt))
514
			REPROTECT(txt = coerceVector(txt, STRSXP), pi);
514
			REPROTECT(txt = coerceVector(txt, STRSXP), pi);
515
		}
515
		}
516
		else error(_("invalid graphics parameter"));
516
		else error(_("invalid graphics parameter"));
517
	       }
517
	       }
518
	    }
518
	    }
519
	}
519
	}
520
	break;
520
	break;
521
    case STRSXP:
521
    case STRSXP:
522
    case EXPRSXP:
522
    case EXPRSXP:
523
	txt = spec;
523
	txt = spec;
524
	break;
524
	break;
525
    default:
525
    default:
526
	REPROTECT(txt = coerceVector(spec, STRSXP), pi);
526
	REPROTECT(txt = coerceVector(spec, STRSXP), pi);
527
	break;
527
	break;
528
    }
528
    }
529
    UNPROTECT(1);
529
    UNPROTECT(1);
530
    if (txt != R_NilValue) {
530
    if (txt != R_NilValue) {
531
	*ptxt = txt;
531
	*ptxt = txt;
532
	if (R_FINITE(cex))	 *pcex	 = cex;
532
	if (R_FINITE(cex))	 *pcex	 = cex;
533
	if (colspecd)	         *pcol	 = col;
533
	if (colspecd)	         *pcol	 = col;
534
	if (font != NA_INTEGER)	 *pfont	 = font;
534
	if (font != NA_INTEGER)	 *pfont	 = font;
535
    }
535
    }
536
}/* GetTextArg */
536
}/* GetTextArg */
537
 
537
 
538
 
538
 
539
    /* GRAPHICS FUNCTION ENTRY POINTS */
539
    /* GRAPHICS FUNCTION ENTRY POINTS */
540
 
540
 
541
 
541
 
542
SEXP attribute_hidden do_plot_new(SEXP call, SEXP op, SEXP args, SEXP env)
542
SEXP attribute_hidden do_plot_new(SEXP call, SEXP op, SEXP args, SEXP env)
543
{
543
{
544
    /* plot.new() - create a new plot "frame" */
544
    /* plot.new() - create a new plot "frame" */
545
 
545
 
546
    DevDesc *dd;
546
    DevDesc *dd;
547
 
547
 
548
    checkArity(op, args);
548
    checkArity(op, args);
549
 
549
 
550
    dd = CurrentDevice();
550
    dd = CurrentDevice();
551
    /*
551
    /*
552
     * If user is prompted before new page, user has opportunity
552
     * If user is prompted before new page, user has opportunity
553
     * to kill current device.  GNewPlot returns (potentially new)
553
     * to kill current device.  GNewPlot returns (potentially new)
554
     * current device.
554
     * current device.
555
     */
555
     */
556
    dd = GNewPlot(GRecording(call, dd));
556
    dd = GNewPlot(GRecording(call, dd));
557
 
557
 
558
    Rf_dpptr(dd)->xlog = Rf_gpptr(dd)->xlog = FALSE;
558
    Rf_dpptr(dd)->xlog = Rf_gpptr(dd)->xlog = FALSE;
559
    Rf_dpptr(dd)->ylog = Rf_gpptr(dd)->ylog = FALSE;
559
    Rf_dpptr(dd)->ylog = Rf_gpptr(dd)->ylog = FALSE;
560
 
560
 
561
    GScale(0.0, 1.0, 1, dd);
561
    GScale(0.0, 1.0, 1, dd);
562
    GScale(0.0, 1.0, 2, dd);
562
    GScale(0.0, 1.0, 2, dd);
563
    GMapWin2Fig(dd);
563
    GMapWin2Fig(dd);
564
    GSetState(1, dd);
564
    GSetState(1, dd);
565
 
565
 
566
    if (GRecording(call, dd))
566
    if (GRecording(call, dd))
567
	recordGraphicOperation(op, args, dd);
567
	recordGraphicOperation(op, args, dd);
568
    return R_NilValue;
568
    return R_NilValue;
569
}
569
}
570
 
570
 
571
 
571
 
572
/*
572
/*
573
 *  SYNOPSIS
573
 *  SYNOPSIS
574
 *
574
 *
575
 *	plot.window(xlim, ylim, log="", asp=NA)
575
 *	plot.window(xlim, ylim, log="", asp=NA)
576
 *
576
 *
577
 *  DESCRIPTION
577
 *  DESCRIPTION
578
 *
578
 *
579
 *	This function sets up the world coordinates for a graphics
579
 *	This function sets up the world coordinates for a graphics
580
 *	window.	 Note that if asp is a finite positive value then
580
 *	window.	 Note that if asp is a finite positive value then
581
 *	the window is set up so that one data unit in the y direction
581
 *	the window is set up so that one data unit in the y direction
582
 *	is equal in length to one data unit in the x direction divided
582
 *	is equal in length to one data unit in the x direction divided
583
 *	by asp.
583
 *	by asp.
584
 *
584
 *
585
 *	The special case asp == 1 produces plots where distances
585
 *	The special case asp == 1 produces plots where distances
586
 *	between points are represented accurately on screen.
586
 *	between points are represented accurately on screen.
587
 *
587
 *
588
 *  NOTE
588
 *  NOTE
589
 *
589
 *
590
 *	The use of asp can have weird effects when axis is an
590
 *	The use of asp can have weird effects when axis is an
591
 *	interpreted function.  It has to be internal so that the
591
 *	interpreted function.  It has to be internal so that the
592
 *	full computation is captured in the display list.
592
 *	full computation is captured in the display list.
593
 */
593
 */
594
 
594
 
595
SEXP attribute_hidden do_plot_window(SEXP call, SEXP op, SEXP args, SEXP env)
595
SEXP attribute_hidden do_plot_window(SEXP call, SEXP op, SEXP args, SEXP env)
596
{
596
{
597
    SEXP xlim, ylim, logarg;
597
    SEXP xlim, ylim, logarg;
598
    double asp, xmin, xmax, ymin, ymax;
598
    double asp, xmin, xmax, ymin, ymax;
599
    Rboolean logscale;
599
    Rboolean logscale;
600
    const char *p;
600
    const char *p;
601
    SEXP originalArgs = args;
601
    SEXP originalArgs = args;
602
    DevDesc *dd = CurrentDevice();
602
    DevDesc *dd = CurrentDevice();
603
 
603
 
604
    if (length(args) < 3)
604
    if (length(args) < 3)
605
	error(_("at least 3 arguments required"));
605
	error(_("at least 3 arguments required"));
606
 
606
 
607
    xlim = CAR(args);
607
    xlim = CAR(args);
608
    if (!isNumeric(xlim) || LENGTH(xlim) != 2)
608
    if (!isNumeric(xlim) || LENGTH(xlim) != 2)
609
	error(_("invalid '%s' value"), "xlim");
609
	error(_("invalid '%s' value"), "xlim");
610
    args = CDR(args);
610
    args = CDR(args);
611
 
611
 
612
    ylim = CAR(args);
612
    ylim = CAR(args);
613
    if (!isNumeric(ylim) || LENGTH(ylim) != 2)
613
    if (!isNumeric(ylim) || LENGTH(ylim) != 2)
614
	error(_("invalid '%s' value"), "ylim");
614
	error(_("invalid '%s' value"), "ylim");
615
    args = CDR(args);
615
    args = CDR(args);
616
 
616
 
617
    logscale = FALSE;
617
    logscale = FALSE;
618
    logarg = CAR(args);
618
    logarg = CAR(args);
619
    if (!isString(logarg))
619
    if (!isString(logarg))
620
	error(_("\"log=\" specification must be character"));
620
	error(_("\"log=\" specification must be character"));
621
    p = CHAR(STRING_ELT(logarg, 0));
621
    p = CHAR(STRING_ELT(logarg, 0));
622
    while (*p) {
622
    while (*p) {
623
	switch (*p) {
623
	switch (*p) {
624
	case 'x':
624
	case 'x':
625
	    Rf_dpptr(dd)->xlog = Rf_gpptr(dd)->xlog = logscale = TRUE;
625
	    Rf_dpptr(dd)->xlog = Rf_gpptr(dd)->xlog = logscale = TRUE;
626
	    break;
626
	    break;
627
	case 'y':
627
	case 'y':
628
	    Rf_dpptr(dd)->ylog = Rf_gpptr(dd)->ylog = logscale = TRUE;
628
	    Rf_dpptr(dd)->ylog = Rf_gpptr(dd)->ylog = logscale = TRUE;
629
	    break;
629
	    break;
630
	default:
630
	default:
631
	    error(_("invalid \"log=%s\" specification"), p);
631
	    error(_("invalid \"log=%s\" specification"), p);
632
	}
632
	}
633
	p++;
633
	p++;
634
    }
634
    }
635
    args = CDR(args);
635
    args = CDR(args);
636
 
636
 
637
    asp = (logscale) ? NA_REAL : asReal(CAR(args));;
637
    asp = (logscale) ? NA_REAL : asReal(CAR(args));;
638
    args = CDR(args);
638
    args = CDR(args);
639
 
639
 
640
    /* This reads [xy]axs and lab, used in GScale */
640
    /* This reads [xy]axs and lab, used in GScale */
641
    GSavePars(dd);
641
    GSavePars(dd);
642
    ProcessInlinePars(args, dd, call);
642
    ProcessInlinePars(args, dd, call);
643
 
643
 
644
    if (isInteger(xlim)) {
644
    if (isInteger(xlim)) {
645
	if (INTEGER(xlim)[0] == NA_INTEGER || INTEGER(xlim)[1] == NA_INTEGER)
645
	if (INTEGER(xlim)[0] == NA_INTEGER || INTEGER(xlim)[1] == NA_INTEGER)
646
	    error(_("NAs not allowed in 'xlim'"));
646
	    error(_("NAs not allowed in 'xlim'"));
647
	xmin = INTEGER(xlim)[0];
647
	xmin = INTEGER(xlim)[0];
648
	xmax = INTEGER(xlim)[1];
648
	xmax = INTEGER(xlim)[1];
649
    }
649
    }
650
    else {
650
    else {
651
	if (!R_FINITE(REAL(xlim)[0]) || !R_FINITE(REAL(xlim)[1]))
651
	if (!R_FINITE(REAL(xlim)[0]) || !R_FINITE(REAL(xlim)[1]))
652
	    error(_("need finite 'xlim' values"));
652
	    error(_("need finite 'xlim' values"));
653
	xmin = REAL(xlim)[0];
653
	xmin = REAL(xlim)[0];
654
	xmax = REAL(xlim)[1];
654
	xmax = REAL(xlim)[1];
655
    }
655
    }
656
    if (isInteger(ylim)) {
656
    if (isInteger(ylim)) {
657
	if (INTEGER(ylim)[0] == NA_INTEGER || INTEGER(ylim)[1] == NA_INTEGER)
657
	if (INTEGER(ylim)[0] == NA_INTEGER || INTEGER(ylim)[1] == NA_INTEGER)
658
	    error(_("NAs not allowed in 'ylim'"));
658
	    error(_("NAs not allowed in 'ylim'"));
659
	ymin = INTEGER(ylim)[0];
659
	ymin = INTEGER(ylim)[0];
660
	ymax = INTEGER(ylim)[1];
660
	ymax = INTEGER(ylim)[1];
661
    }
661
    }
662
    else {
662
    else {
663
	if (!R_FINITE(REAL(ylim)[0]) || !R_FINITE(REAL(ylim)[1]))
663
	if (!R_FINITE(REAL(ylim)[0]) || !R_FINITE(REAL(ylim)[1]))
664
	    error(_("need finite 'ylim' values"));
664
	    error(_("need finite 'ylim' values"));
665
	ymin = REAL(ylim)[0];
665
	ymin = REAL(ylim)[0];
666
	ymax = REAL(ylim)[1];
666
	ymax = REAL(ylim)[1];
667
    }
667
    }
668
    if ((Rf_dpptr(dd)->xlog && (xmin < 0 || xmax < 0)) ||
668
    if ((Rf_dpptr(dd)->xlog && (xmin < 0 || xmax < 0)) ||
669
       (Rf_dpptr(dd)->ylog && (ymin < 0 || ymax < 0)))
669
       (Rf_dpptr(dd)->ylog && (ymin < 0 || ymax < 0)))
670
	    error(_("Logarithmic axis must have positive limits"));
670
	    error(_("Logarithmic axis must have positive limits"));
671
 
671
 
672
    if (R_FINITE(asp) && asp > 0) {
672
    if (R_FINITE(asp) && asp > 0) {
673
	double pin1, pin2, scale, xdelta, ydelta, xscale, yscale, xadd, yadd;
673
	double pin1, pin2, scale, xdelta, ydelta, xscale, yscale, xadd, yadd;
674
	pin1 = GConvertXUnits(1.0, NPC, INCHES, dd);
674
	pin1 = GConvertXUnits(1.0, NPC, INCHES, dd);
675
	pin2 = GConvertYUnits(1.0, NPC, INCHES, dd);
675
	pin2 = GConvertYUnits(1.0, NPC, INCHES, dd);
676
	xdelta = fabs(xmax - xmin) / asp;
676
	xdelta = fabs(xmax - xmin) / asp;
677
	ydelta = fabs(ymax - ymin);
677
	ydelta = fabs(ymax - ymin);
678
	if(xdelta == 0.0 && ydelta == 0.0) {
678
	if(xdelta == 0.0 && ydelta == 0.0) {
679
	    /* We really do mean zero: small non-zero values work.
679
	    /* We really do mean zero: small non-zero values work.
680
	       Mimic the behaviour of GScale for the x axis. */
680
	       Mimic the behaviour of GScale for the x axis. */
681
	    xadd = yadd = ((xmin == 0.0) ? 1 : 0.4) * asp;
681
	    xadd = yadd = ((xmin == 0.0) ? 1 : 0.4) * asp;
682
	    xadd *= asp;
682
	    xadd *= asp;
683
	} else {
683
	} else {
684
	    xscale = pin1 / xdelta;
684
	    xscale = pin1 / xdelta;
685
	    yscale = pin2 / ydelta;
685
	    yscale = pin2 / ydelta;
686
	    scale = (xscale < yscale) ? xscale : yscale;
686
	    scale = (xscale < yscale) ? xscale : yscale;
687
	    xadd = .5 * (pin1 / scale - xdelta) * asp;
687
	    xadd = .5 * (pin1 / scale - xdelta) * asp;
688
	    yadd = .5 * (pin2 / scale - ydelta);
688
	    yadd = .5 * (pin2 / scale - ydelta);
689
	}
689
	}
690
	if(xmax < xmin) xadd *= -1;
690
	if(xmax < xmin) xadd *= -1;
691
	if(ymax < ymin) yadd *= -1;
691
	if(ymax < ymin) yadd *= -1;
692
	GScale(xmin - xadd, xmax + xadd, 1, dd);
692
	GScale(xmin - xadd, xmax + xadd, 1, dd);
693
	GScale(ymin - yadd, ymax + yadd, 2, dd);
693
	GScale(ymin - yadd, ymax + yadd, 2, dd);
694
    }
694
    }
695
    else { /* asp <= 0 or not finite -- includes logscale ! */
695
    else { /* asp <= 0 or not finite -- includes logscale ! */
696
	GScale(xmin, xmax, 1, dd);
696
	GScale(xmin, xmax, 1, dd);
697
	GScale(ymin, ymax, 2, dd);
697
	GScale(ymin, ymax, 2, dd);
698
    }
698
    }
699
    /* GScale set the [xy]axp parameters */
699
    /* GScale set the [xy]axp parameters */
700
    GMapWin2Fig(dd);
700
    GMapWin2Fig(dd);
701
    GRestorePars(dd);
701
    GRestorePars(dd);
702
    /* This has now clobbered the Rf_ggptr settings for coord system */
702
    /* This has now clobbered the Rf_ggptr settings for coord system */
703
 
703
 
704
    /* NOTE: the operation is only recorded if there was no "error" */
704
    /* NOTE: the operation is only recorded if there was no "error" */
705
    if (GRecording(call, dd))
705
    if (GRecording(call, dd))
706
	recordGraphicOperation(op, originalArgs, dd);
706
	recordGraphicOperation(op, originalArgs, dd);
707
    return R_NilValue;
707
    return R_NilValue;
708
}
708
}
709
 
709
 
710
void GetAxisLimits(double left, double right, double *low, double *high)
710
void GetAxisLimits(double left, double right, double *low, double *high)
711
{
711
{
712
/*	Called from do_axis()	such as
712
/*	Called from do_axis()	such as
713
 *	GetAxisLimits(Rf_gpptr(dd)->usr[0], Rf_gpptr(dd)->usr[1], &low, &high)
713
 *	GetAxisLimits(Rf_gpptr(dd)->usr[0], Rf_gpptr(dd)->usr[1], &low, &high)
714
 *
714
 *
715
 *	Computes  *low < left, right < *high  (even if left=right)
715
 *	Computes  *low < left, right < *high  (even if left=right)
716
 */
716
 */
717
    double eps;
717
    double eps;
718
    if (left > right) {/* swap */
718
    if (left > right) {/* swap */
719
	eps = left; left = right; right = eps;
719
	eps = left; left = right; right = eps;
720
    }
720
    }
721
    eps = right - left;
721
    eps = right - left;
722
    if (eps == 0.)
722
    if (eps == 0.)
723
	eps = 0.5 * FLT_EPSILON;
723
	eps = 0.5 * FLT_EPSILON;
724
    else
724
    else
725
	eps *= FLT_EPSILON;
725
	eps *= FLT_EPSILON;
726
    *low = left - eps;
726
    *low = left - eps;
727
    *high = right + eps;
727
    *high = right + eps;
728
}
728
}
729
 
729
 
730
 
730
 
731
/* axis(side, at, labels, ...) */
731
/* axis(side, at, labels, ...) */
732
 
732
 
733
SEXP attribute_hidden labelformat(SEXP labels)
733
SEXP attribute_hidden labelformat(SEXP labels)
734
{
734
{
735
    /* format(labels): i.e. from numbers to strings */
735
    /* format(labels): i.e. from numbers to strings */
736
    SEXP ans = R_NilValue;/* -Wall*/
736
    SEXP ans = R_NilValue;/* -Wall*/
737
    int i, n, w, d, e, wi, di, ei;
737
    int i, n, w, d, e, wi, di, ei;
738
    const char *strp;
738
    const char *strp;
739
    n = length(labels);
739
    n = length(labels);
740
    R_print.digits = 7;/* maximally 7 digits -- ``burnt in'';
740
    R_print.digits = 7;/* maximally 7 digits -- ``burnt in'';
741
			  S-PLUS <= 5.x has about 6
741
			  S-PLUS <= 5.x has about 6
742
			  (but really uses single precision..) */
742
			  (but really uses single precision..) */
743
    switch(TYPEOF(labels)) {
743
    switch(TYPEOF(labels)) {
744
    case LGLSXP:
744
    case LGLSXP:
745
	PROTECT(ans = allocVector(STRSXP, n));
745
	PROTECT(ans = allocVector(STRSXP, n));
746
	for (i = 0; i < n; i++) {
746
	for (i = 0; i < n; i++) {
747
	    strp = EncodeLogical(LOGICAL(labels)[i], 0);
747
	    strp = EncodeLogical(LOGICAL(labels)[i], 0);
748
	    SET_STRING_ELT(ans, i, mkChar(strp));
748
	    SET_STRING_ELT(ans, i, mkChar(strp));
749
	}
749
	}
750
	UNPROTECT(1);
750
	UNPROTECT(1);
751
	break;
751
	break;
752
    case INTSXP:
752
    case INTSXP:
753
	PROTECT(ans = allocVector(STRSXP, n));
753
	PROTECT(ans = allocVector(STRSXP, n));
754
	for (i = 0; i < n; i++) {
754
	for (i = 0; i < n; i++) {
755
	    strp = EncodeInteger(INTEGER(labels)[i], 0);
755
	    strp = EncodeInteger(INTEGER(labels)[i], 0);
756
	    SET_STRING_ELT(ans, i, mkChar(strp));
756
	    SET_STRING_ELT(ans, i, mkChar(strp));
757
	}
757
	}
758
	UNPROTECT(1);
758
	UNPROTECT(1);
759
	break;
759
	break;
760
    case REALSXP:
760
    case REALSXP:
761
	formatReal(REAL(labels), n, &w, &d, &e, 0);
761
	formatReal(REAL(labels), n, &w, &d, &e, 0);
762
	PROTECT(ans = allocVector(STRSXP, n));
762
	PROTECT(ans = allocVector(STRSXP, n));
763
	for (i = 0; i < n; i++) {
763
	for (i = 0; i < n; i++) {
764
	    strp = EncodeReal(REAL(labels)[i], 0, d, e, OutDec);
764
	    strp = EncodeReal(REAL(labels)[i], 0, d, e, OutDec);
765
	    SET_STRING_ELT(ans, i, mkChar(strp));
765
	    SET_STRING_ELT(ans, i, mkChar(strp));
766
	}
766
	}
767
	UNPROTECT(1);
767
	UNPROTECT(1);
768
	break;
768
	break;
769
    case CPLXSXP:
769
    case CPLXSXP:
770
	formatComplex(COMPLEX(labels), n, &w, &d, &e, &wi, &di, &ei, 0);
770
	formatComplex(COMPLEX(labels), n, &w, &d, &e, &wi, &di, &ei, 0);
771
	PROTECT(ans = allocVector(STRSXP, n));
771
	PROTECT(ans = allocVector(STRSXP, n));
772
	for (i = 0; i < n; i++) {
772
	for (i = 0; i < n; i++) {
773
	    strp = EncodeComplex(COMPLEX(labels)[i], 0, d, e, 0, di, ei,
773
	    strp = EncodeComplex(COMPLEX(labels)[i], 0, d, e, 0, di, ei,
774
				 OutDec);
774
				 OutDec);
775
	    SET_STRING_ELT(ans, i, mkChar(strp));
775
	    SET_STRING_ELT(ans, i, mkChar(strp));
776
	}
776
	}
777
	UNPROTECT(1);
777
	UNPROTECT(1);
778
	break;
778
	break;
779
    case STRSXP:
779
    case STRSXP:
780
	PROTECT(ans = allocVector(STRSXP, n));
780
	PROTECT(ans = allocVector(STRSXP, n));
781
	for (i = 0; i < n; i++) {
781
	for (i = 0; i < n; i++) {
782
	    SET_STRING_ELT(ans, i, STRING_ELT(labels, i));
782
	    SET_STRING_ELT(ans, i, STRING_ELT(labels, i));
783
	}
783
	}
784
	UNPROTECT(1);
784
	UNPROTECT(1);
785
	break;
785
	break;
786
    default:
786
    default:
787
	error(_("invalid type for axis labels"));
787
	error(_("invalid type for axis labels"));
788
    }
788
    }
789
    return ans;
789
    return ans;
790
}
790
}
791
 
791
 
792
SEXP CreateAtVector(double *axp, double *usr, int nint, Rboolean logflag)
792
SEXP CreateAtVector(double *axp, double *usr, int nint, Rboolean logflag)
793
{
793
{
794
/*	Create an  'at = ...' vector for  axis(.) / do_axis,
794
/*	Create an  'at = ...' vector for  axis(.) / do_axis,
795
 *	i.e., the vector of tick mark locations,
795
 *	i.e., the vector of tick mark locations,
796
 *	when none has been specified (= default).
796
 *	when none has been specified (= default).
797
 *
797
 *
798
 *	axp[0:2] = (x1, x2, nInt), where x1..x2 are the extreme tick marks
798
 *	axp[0:2] = (x1, x2, nInt), where x1..x2 are the extreme tick marks
799
 *		   {unless in log case, where nint \in {1,2,3 ; -1,-2,....}
799
 *		   {unless in log case, where nint \in {1,2,3 ; -1,-2,....}
800
 *		    and the `nint' argument is used.}
800
 *		    and the `nint' argument is used.}
801
 
801
 
802
 *	The resulting REAL vector must have length >= 1, ideally >= 2
802
 *	The resulting REAL vector must have length >= 1, ideally >= 2
803
 */
803
 */
804
    SEXP at = R_NilValue;/* -Wall*/
804
    SEXP at = R_NilValue;/* -Wall*/
805
    double umin, umax, dn, rng, small;
805
    double umin, umax, dn, rng, small;
806
    int i, n, ne;
806
    int i, n, ne;
807
    if (!logflag || axp[2] < 0) { /* --- linear axis --- Only use axp[] arg. */
807
    if (!logflag || axp[2] < 0) { /* --- linear axis --- Only use axp[] arg. */
808
	n = fabs(axp[2]) + 0.25;/* >= 0 */
808
	n = fabs(axp[2]) + 0.25;/* >= 0 */
809
	dn = imax2(1, n);
809
	dn = imax2(1, n);
810
	rng = axp[1] - axp[0];
810
	rng = axp[1] - axp[0];
811
	small = fabs(rng)/(100.*dn);
811
	small = fabs(rng)/(100.*dn);
812
	at = allocVector(REALSXP, n + 1);
812
	at = allocVector(REALSXP, n + 1);
813
	for (i = 0; i <= n; i++) {
813
	for (i = 0; i <= n; i++) {
814
	    REAL(at)[i] = axp[0] + (i / dn) * rng;
814
	    REAL(at)[i] = axp[0] + (i / dn) * rng;
815
	    if (fabs(REAL(at)[i]) < small)
815
	    if (fabs(REAL(at)[i]) < small)
816
		REAL(at)[i] = 0;
816
		REAL(at)[i] = 0;
817
	}
817
	}
818
    }
818
    }
819
    else { /* ------ log axis ----- */
819
    else { /* ------ log axis ----- */
820
	Rboolean reversed = FALSE;
820
	Rboolean reversed = FALSE;
821
 
821
 
822
	n = (axp[2] + 0.5);
822
	n = (axp[2] + 0.5);
823
	/* {xy}axp[2] for 'log': GLpretty() [./graphics.c] sets
823
	/* {xy}axp[2] for 'log': GLpretty() [./graphics.c] sets
824
	   n < 0: very small scale ==> linear axis, above, or
824
	   n < 0: very small scale ==> linear axis, above, or
825
	   n = 1,2,3.  see switch() below */
825
	   n = 1,2,3.  see switch() below */
826
	umin = usr[0];
826
	umin = usr[0];
827
	umax = usr[1];
827
	umax = usr[1];
828
	if (umin > umax) {
828
	if (umin > umax) {
829
	    reversed = (axp[0] > axp[1]);
829
	    reversed = (axp[0] > axp[1]);
830
	    if (reversed) {
830
	    if (reversed) {
831
		/* have *reversed* log axis -- whereas
831
		/* have *reversed* log axis -- whereas
832
		 * the switch(n) { .. } below assumes *increasing* values
832
		 * the switch(n) { .. } below assumes *increasing* values
833
		 * --> reverse axis direction here, and reverse back at end */
833
		 * --> reverse axis direction here, and reverse back at end */
834
		umin = usr[1];
834
		umin = usr[1];
835
		umax = usr[0];
835
		umax = usr[0];
836
		dn = axp[0]; axp[0] = axp[1]; axp[1] = dn;
836
		dn = axp[0]; axp[0] = axp[1]; axp[1] = dn;
837
	    }
837
	    }
838
	    else {
838
	    else {
839
		/* can the following still happen... ? */
839
		/* can the following still happen... ? */
840
		warning("CreateAtVector \"log\"(from axis()): "
840
		warning("CreateAtVector \"log\"(from axis()): "
841
			"usr[0] = %g > %g = usr[1] !", umin, umax);
841
			"usr[0] = %g > %g = usr[1] !", umin, umax);
842
	    }
842
	    }
843
	}
843
	}
844
 
844
 
845
	dn = axp[0];
845
	dn = axp[0];
846
	if (dn < DBL_MIN) {/* was 1e-300; now seems too cautious */
846
	if (dn < DBL_MIN) {/* was 1e-300; now seems too cautious */
847
	    warning("CreateAtVector \"log\"(from axis()): axp[0] = %g !", dn);
847
	    warning("CreateAtVector \"log\"(from axis()): axp[0] = %g !", dn);
848
	    if (dn <= 0) /* real trouble (once for Solaris) later on */
848
	    if (dn <= 0) /* real trouble (once for Solaris) later on */
849
		error("CreateAtVector [log-axis()]: axp[0] = %g < 0!", dn);
849
		error("CreateAtVector [log-axis()]: axp[0] = %g < 0!", dn);
850
	}
850
	}
851
 
851
 
852
	/* You get the 3 cases below by
852
	/* You get the 3 cases below by
853
	 *  for (y in 1e-5*c(1,2,8))  plot(y, log = "y")
853
	 *  for (y in 1e-5*c(1,2,8))  plot(y, log = "y")
854
	 */
854
	 */
855
	switch(n) {
855
	switch(n) {
856
	case 1: /* large range:	1	 * 10^k */
856
	case 1: /* large range:	1	 * 10^k */
857
	    i = floor(log10(axp[1])) - ceil(log10(axp[0])) + 0.25;
857
	    i = floor(log10(axp[1])) - ceil(log10(axp[0])) + 0.25;
858
	    ne = i / nint + 1;
858
	    ne = i / nint + 1;
859
	    if (ne < 1)
859
	    if (ne < 1)
860
		error("log - axis(), 'at' creation, _LARGE_ range: "
860
		error("log - axis(), 'at' creation, _LARGE_ range: "
861
		      "ne = %d <= 0 !!\n"
861
		      "ne = %d <= 0 !!\n"
862
		      "\t axp[0:1]=(%g,%g) ==> i = %d;	nint = %d",
862
		      "\t axp[0:1]=(%g,%g) ==> i = %d;	nint = %d",
863
		      ne, axp[0],axp[1], i, nint);
863
		      ne, axp[0],axp[1], i, nint);
864
	    rng = pow(10., (double)ne);/* >= 10 */
864
	    rng = pow(10., (double)ne);/* >= 10 */
865
	    n = 0;
865
	    n = 0;
866
	    while (dn < umax) {
866
	    while (dn < umax) {
867
		n++;
867
		n++;
868
		dn *= rng;
868
		dn *= rng;
869
	    }
869
	    }
870
	    if (!n)
870
	    if (!n)
871
		error("log - axis(), 'at' creation, _LARGE_ range: "
871
		error("log - axis(), 'at' creation, _LARGE_ range: "
872
		      "invalid {xy}axp or par; nint=%d\n"
872
		      "invalid {xy}axp or par; nint=%d\n"
873
		      "	 axp[0:1]=(%g,%g), usr[0:1]=(%g,%g); i=%d, ni=%d",
873
		      "	 axp[0:1]=(%g,%g), usr[0:1]=(%g,%g); i=%d, ni=%d",
874
		      nint, axp[0],axp[1], umin,umax, i,ne);
874
		      nint, axp[0],axp[1], umin,umax, i,ne);
875
	    at = allocVector(REALSXP, n);
875
	    at = allocVector(REALSXP, n);
876
	    dn = axp[0];
876
	    dn = axp[0];
877
	    n = 0;
877
	    n = 0;
878
	    while (dn < umax) {
878
	    while (dn < umax) {
879
		REAL(at)[n++] = dn;
879
		REAL(at)[n++] = dn;
880
		dn *= rng;
880
		dn *= rng;
881
	    }
881
	    }
882
	    break;
882
	    break;
883
 
883
 
884
	case 2: /* medium range:  1, 5	  * 10^k */
884
	case 2: /* medium range:  1, 5	  * 10^k */
885
	    n = 0;
885
	    n = 0;
886
	    if (0.5 * dn >= umin) n++;
886
	    if (0.5 * dn >= umin) n++;
887
	    for (;;) {
887
	    for (;;) {
888
		if (dn > umax) break;		n++;
888
		if (dn > umax) break;		n++;
889
		if (5 * dn > umax) break;	n++;
889
		if (5 * dn > umax) break;	n++;
890
		dn *= 10;
890
		dn *= 10;
891
	    }
891
	    }
892
	    if (!n)
892
	    if (!n)
893
		error("log - axis(), 'at' creation, _MEDIUM_ range: "
893
		error("log - axis(), 'at' creation, _MEDIUM_ range: "
894
		      "invalid {xy}axp or par;\n"
894
		      "invalid {xy}axp or par;\n"
895
		      "	 axp[0]= %g, usr[0:1]=(%g,%g)",
895
		      "	 axp[0]= %g, usr[0:1]=(%g,%g)",
896
		      axp[0], umin,umax);
896
		      axp[0], umin,umax);
897
 
897
 
898
	    at = allocVector(REALSXP, n);
898
	    at = allocVector(REALSXP, n);
899
	    dn = axp[0];
899
	    dn = axp[0];
900
	    n = 0;
900
	    n = 0;
901
	    if (0.5 * dn >= umin) REAL(at)[n++] = 0.5 * dn;
901
	    if (0.5 * dn >= umin) REAL(at)[n++] = 0.5 * dn;
902
	    for (;;) {
902
	    for (;;) {
903
		if (dn > umax) break;		REAL(at)[n++] = dn;
903
		if (dn > umax) break;		REAL(at)[n++] = dn;
904
		if (5 * dn > umax) break;	REAL(at)[n++] = 5 * dn;
904
		if (5 * dn > umax) break;	REAL(at)[n++] = 5 * dn;
905
		dn *= 10;
905
		dn *= 10;
906
	    }
906
	    }
907
	    break;
907
	    break;
908
 
908
 
909
	case 3: /* small range:	 1,2,5,10 * 10^k */
909
	case 3: /* small range:	 1,2,5,10 * 10^k */
910
	    n = 0;
910
	    n = 0;
911
	    if (0.2 * dn >= umin) n++;
911
	    if (0.2 * dn >= umin) n++;
912
	    if (0.5 * dn >= umin) n++;
912
	    if (0.5 * dn >= umin) n++;
913
	    for (;;) {
913
	    for (;;) {
914
		if (dn > umax) break;		n++;
914
		if (dn > umax) break;		n++;
915
		if (2 * dn > umax) break;	n++;
915
		if (2 * dn > umax) break;	n++;
916
		if (5 * dn > umax) break;	n++;
916
		if (5 * dn > umax) break;	n++;
917
		dn *= 10;
917
		dn *= 10;
918
	    }
918
	    }
919
	    if (!n)
919
	    if (!n)
920
		error("log - axis(), 'at' creation, _SMALL_ range: "
920
		error("log - axis(), 'at' creation, _SMALL_ range: "
921
		      "invalid {xy}axp or par;\n"
921
		      "invalid {xy}axp or par;\n"
922
		      "	 axp[0]= %g, usr[0:1]=(%g,%g)",
922
		      "	 axp[0]= %g, usr[0:1]=(%g,%g)",
923
		      axp[0], umin,umax);
923
		      axp[0], umin,umax);
924
	    at = allocVector(REALSXP, n);
924
	    at = allocVector(REALSXP, n);
925
	    dn = axp[0];
925
	    dn = axp[0];
926
	    n = 0;
926
	    n = 0;
927
	    if (0.2 * dn >= umin) REAL(at)[n++] = 0.2 * dn;
927
	    if (0.2 * dn >= umin) REAL(at)[n++] = 0.2 * dn;
928
	    if (0.5 * dn >= umin) REAL(at)[n++] = 0.5 * dn;
928
	    if (0.5 * dn >= umin) REAL(at)[n++] = 0.5 * dn;
929
	    for (;;) {
929
	    for (;;) {
930
		if (dn > umax) break;		REAL(at)[n++] = dn;
930
		if (dn > umax) break;		REAL(at)[n++] = dn;
931
		if (2 * dn > umax) break;	REAL(at)[n++] = 2 * dn;
931
		if (2 * dn > umax) break;	REAL(at)[n++] = 2 * dn;
932
		if (5 * dn > umax) break;	REAL(at)[n++] = 5 * dn;
932
		if (5 * dn > umax) break;	REAL(at)[n++] = 5 * dn;
933
		dn *= 10;
933
		dn *= 10;
934
	    }
934
	    }
935
	    break;
935
	    break;
936
	default:
936
	default:
937
	    error("log - axis(), 'at' creation: INVALID {xy}axp[3] = %g",
937
	    error("log - axis(), 'at' creation: INVALID {xy}axp[3] = %g",
938
		  axp[2]);
938
		  axp[2]);
939
	}
939
	}
940
 
940
 
941
	if (reversed) {/* reverse back again - last assignment was at[n++]= . */
941
	if (reversed) {/* reverse back again - last assignment was at[n++]= . */
942
	    for (i = 0; i < n/2; i++) { /* swap( at[i], at[n-i-1] ) : */
942
	    for (i = 0; i < n/2; i++) { /* swap( at[i], at[n-i-1] ) : */
943
		dn = REAL(at)[i];
943
		dn = REAL(at)[i];
944
		REAL(at)[i] = REAL(at)[n-i-1];
944
		REAL(at)[i] = REAL(at)[n-i-1];
945
		REAL(at)[n-i-1] = dn;
945
		REAL(at)[n-i-1] = dn;
946
	    }
946
	    }
947
	}
947
	}
948
    } /* linear / log */
948
    } /* linear / log */
949
    return at;
949
    return at;
950
}
950
}
951
 
951
 
952
static double ComputePAdjValue(double padj, int side, int las)
952
static double ComputePAdjValue(double padj, int side, int las)
953
{
953
{
954
    if (!R_FINITE(padj)) {
954
    if (!R_FINITE(padj)) {
955
    switch(las) {
955
    switch(las) {
956
    case 0:/* parallel to axis */
956
    case 0:/* parallel to axis */
957
        padj = 0.0; break;
957
        padj = 0.0; break;
958
    case 1:/* horizontal */
958
    case 1:/* horizontal */
959
        switch(side) {
959
        switch(side) {
960
        case 1:
960
        case 1:
961
        case 3: padj = 0.0; break;
961
        case 3: padj = 0.0; break;
962
        case 2:
962
        case 2:
963
        case 4: padj = 0.5; break;
963
        case 4: padj = 0.5; break;
964
        }
964
        }
965
        break;
965
        break;
966
    case 2:/* perpendicular to axis */
966
    case 2:/* perpendicular to axis */
967
        padj = 0.5; break;
967
        padj = 0.5; break;
968
    case 3:/* vertical */
968
    case 3:/* vertical */
969
        switch(side) {
969
        switch(side) {
970
        case 1:
970
        case 1:
971
        case 3: padj = 0.5; break;
971
        case 3: padj = 0.5; break;
972
        case 2:
972
        case 2:
973
        case 4: padj = 0.0; break;
973
        case 4: padj = 0.0; break;
974
        }
974
        }
975
        break;
975
        break;
976
    }
976
    }
977
    }
977
    }
978
    return padj;
978
    return padj;
979
}
979
}
980
 
980
 
981
SEXP attribute_hidden do_axis(SEXP call, SEXP op, SEXP args, SEXP env)
981
SEXP attribute_hidden do_axis(SEXP call, SEXP op, SEXP args, SEXP env)
982
{
982
{
983
    /* axis(side, at, labels, tick, line, pos,
983
    /* axis(side, at, labels, tick, line, pos,
984
     *	    outer, font, lty, lwd, col, padj, ...) */
984
     *	    outer, font, lty, lwd, col, padj, ...) */
985
 
985
 
986
    SEXP at, lab, padj;
986
    SEXP at, lab, padj;
987
    int col, font, lty, npadj;
987
    int col, font, lty, npadj;
988
    int i, n, nint = 0, ntmp, side, *ind, outer, lineoff = 0;
988
    int i, n, nint = 0, ntmp, side, *ind, outer, lineoff = 0;
989
    int istart, iend, incr;
989
    int istart, iend, incr;
990
    Rboolean dolabels, doticks, logflag = FALSE;
990
    Rboolean dolabels, doticks, logflag = FALSE;
991
    Rboolean create_at;
991
    Rboolean create_at;
992
    double x, y, temp, tnew, tlast;
992
    double x, y, temp, tnew, tlast;
993
    double axp[3], usr[2];
993
    double axp[3], usr[2];
994
    double gap, labw, low, high, line, pos, lwd, hadj;
994
    double gap, labw, low, high, line, pos, lwd, hadj;
995
    double axis_base, axis_tick, axis_lab, axis_low, axis_high;
995
    double axis_base, axis_tick, axis_lab, axis_low, axis_high;
996
 
996
 
997
    SEXP originalArgs = args, label;
997
    SEXP originalArgs = args, label;
998
    DevDesc *dd = CurrentDevice();
998
    DevDesc *dd = CurrentDevice();
999
 
999
 
1000
    /* Arity Check */
1000
    /* Arity Check */
1001
    /* This is a builtin function, so it should always have */
1001
    /* This is a builtin function, so it should always have */
1002
    /* the correct arity, but it doesn't hurt to be defensive. */
1002
    /* the correct arity, but it doesn't hurt to be defensive. */
1003
 
1003
 
1004
    if (length(args) < 12)
1004
    if (length(args) < 12)
1005
	error(_("too few arguments"));
1005
	error(_("too few arguments"));
1006
    GCheckState(dd);
1006
    GCheckState(dd);
1007
 
1007
 
1008
    /* Required argument: "side" */
1008
    /* Required argument: "side" */
1009
    /* Which side of the plot the axis is to appear on. */
1009
    /* Which side of the plot the axis is to appear on. */
1010
    /* side = 1 | 2 | 3 | 4. */
1010
    /* side = 1 | 2 | 3 | 4. */
1011
 
1011
 
1012
    side = asInteger(CAR(args));
1012
    side = asInteger(CAR(args));
1013
    if (side < 1 || side > 4)
1013
    if (side < 1 || side > 4)
1014
	error(_("invalid axis number %d"), side);
1014
	error(_("invalid axis number %d"), side);
1015
    args = CDR(args);
1015
    args = CDR(args);
1016
 
1016
 
1017
    /* Required argument: "at" */
1017
    /* Required argument: "at" */
1018
    /* This gives the tick-label locations. */
1018
    /* This gives the tick-label locations. */
1019
    /* Note that these are coerced to the correct type below. */
1019
    /* Note that these are coerced to the correct type below. */
1020
 
1020
 
1021
    at = CAR(args); args = CDR(args);
1021
    at = CAR(args); args = CDR(args);
1022
 
1022
 
1023
    /* Required argument: "labels" */
1023
    /* Required argument: "labels" */
1024
    /* Labels can be a logical, indicating whether or not */
1024
    /* Labels can be a logical, indicating whether or not */
1025
    /* to label the axis; or it can be a vector of character */
1025
    /* to label the axis; or it can be a vector of character */
1026
    /* strings or expressions which give the labels explicitly. */
1026
    /* strings or expressions which give the labels explicitly. */
1027
    /* The expressions are used to set mathematical labelling. */
1027
    /* The expressions are used to set mathematical labelling. */
1028
 
1028
 
1029
    dolabels = TRUE;
1029
    dolabels = TRUE;
1030
    lab = CAR(args);
1030
    lab = CAR(args);
1031
    if (isLogical(lab) && length(lab) > 0) {
1031
    if (isLogical(lab) && length(lab) > 0) {
1032
	i = asLogical(lab);
1032
	i = asLogical(lab);
1033
	if (i == 0 || i == NA_LOGICAL)
1033
	if (i == 0 || i == NA_LOGICAL)
1034
	    dolabels = FALSE;
1034
	    dolabels = FALSE;
1035
	PROTECT(lab = R_NilValue);
1035
	PROTECT(lab = R_NilValue);
1036
    } else if (TYPEOF(lab) == LANGSXP || TYPEOF(lab) == SYMSXP) {
1036
    } else if (TYPEOF(lab) == LANGSXP || TYPEOF(lab) == SYMSXP) {
1037
	PROTECT(lab = coerceVector(lab, EXPRSXP));
1037
	PROTECT(lab = coerceVector(lab, EXPRSXP));
1038
    } else if (isExpression(lab)) {
1038
    } else if (isExpression(lab)) {
1039
	PROTECT(lab);
1039
	PROTECT(lab);
1040
    } else {
1040
    } else {
1041
	PROTECT(lab = coerceVector(lab, STRSXP));
1041
	PROTECT(lab = coerceVector(lab, STRSXP));
1042
    }
1042
    }
1043
    args = CDR(args);
1043
    args = CDR(args);
1044
 
1044
 
1045
    /* Required argument: "tick" */
1045
    /* Required argument: "tick" */
1046
    /* This indicates whether or not ticks and the axis line */
1046
    /* This indicates whether or not ticks and the axis line */
1047
    /* should be plotted: TRUE => show, FALSE => don't show. */
1047
    /* should be plotted: TRUE => show, FALSE => don't show. */
1048
 
1048
 
1049
    doticks = asLogical(CAR(args));
1049
    doticks = asLogical(CAR(args));
1050
    doticks = (doticks == NA_LOGICAL) ? TRUE : (Rboolean) doticks;
1050
    doticks = (doticks == NA_LOGICAL) ? TRUE : (Rboolean) doticks;
1051
    args = CDR(args);
1051
    args = CDR(args);
1052
 
1052
 
1053
    /* Optional argument: "line" */
1053
    /* Optional argument: "line" */
1054
 
1054
 
1055
    /* Specifies an offset outward from the plot for the axis.
1055
    /* Specifies an offset outward from the plot for the axis.
1056
     * The values in the par value "mgp" are interpreted
1056
     * The values in the par value "mgp" are interpreted
1057
     * relative to this value. */
1057
     * relative to this value. */
1058
    line = asReal(CAR(args));
1058
    line = asReal(CAR(args));
1059
    if (!R_FINITE(line)) {
1059
    if (!R_FINITE(line)) {
1060
	/* Except that here mgp values are not relative to themselves */
1060
	/* Except that here mgp values are not relative to themselves */
1061
	line = Rf_gpptr(dd)->mgp[2];
1061
	line = Rf_gpptr(dd)->mgp[2];
1062
	lineoff = line;
1062
	lineoff = line;
1063
    }
1063
    }
1064
    args = CDR(args);
1064
    args = CDR(args);
1065
 
1065
 
1066
    /* Optional argument: "pos" */
1066
    /* Optional argument: "pos" */
1067
    /* Specifies a user coordinate at which the axis should be drawn. */
1067
    /* Specifies a user coordinate at which the axis should be drawn. */
1068
    /* This overrides the value of "line".  Again the "mgp" par values */
1068
    /* This overrides the value of "line".  Again the "mgp" par values */
1069
    /* are interpreted relative to this value. */
1069
    /* are interpreted relative to this value. */
1070
 
1070
 
1071
    pos = asReal(CAR(args));
1071
    pos = asReal(CAR(args));
1072
    if (!R_FINITE(pos)) pos = NA_REAL; else lineoff = 0;
1072
    if (!R_FINITE(pos)) pos = NA_REAL; else lineoff = 0;
1073
    args = CDR(args);
1073
    args = CDR(args);
1074
 
1074
 
1075
    /* Optional argument: "outer" */
1075
    /* Optional argument: "outer" */
1076
    /* Should the axis be drawn in the outer margin. */
1076
    /* Should the axis be drawn in the outer margin. */
1077
    /* This only affects the computation of axis_base. */
1077
    /* This only affects the computation of axis_base. */
1078
 
1078
 
1079
    outer = asLogical(CAR(args));
1079
    outer = asLogical(CAR(args));
1080
    if (outer == NA_LOGICAL || outer == 0)
1080
    if (outer == NA_LOGICAL || outer == 0)
1081
	outer = NPC;
1081
	outer = NPC;
1082
    else
1082
    else
1083
	outer = NIC;
1083
	outer = NIC;
1084
    args = CDR(args);
1084
    args = CDR(args);
1085
 
1085
 
1086
    /* Optional argument: "font" */
1086
    /* Optional argument: "font" */
1087
    font = asInteger(FixupFont(CAR(args), NA_INTEGER));
1087
    font = asInteger(FixupFont(CAR(args), NA_INTEGER));
1088
    args = CDR(args);
1088
    args = CDR(args);
1089
 
1089
 
1090
    /* Optional argument: "lty" */
1090
    /* Optional argument: "lty" */
1091
    lty = asInteger(FixupLty(CAR(args), NA_INTEGER));
1091
    lty = asInteger(FixupLty(CAR(args), NA_INTEGER));
1092
    args = CDR(args);
1092
    args = CDR(args);
1093
 
1093
 
1094
    /* Optional argument: "lwd" */
1094
    /* Optional argument: "lwd" */
1095
    lwd = asReal(FixupLwd(CAR(args), 1));
1095
    lwd = asReal(FixupLwd(CAR(args), 1));
1096
    args = CDR(args);
1096
    args = CDR(args);
1097
 
1097
 
1098
    /* Optional argument: "col" */
1098
    /* Optional argument: "col" */
1099
    col = asInteger(FixupCol(CAR(args), Rf_gpptr(dd)->fg));
1099
    col = asInteger(FixupCol(CAR(args), Rf_gpptr(dd)->fg));
1100
    args = CDR(args);
1100
    args = CDR(args);
1101
 
1101
 
1102
    /* Optional argument: "hadj" */
1102
    /* Optional argument: "hadj" */
1103
    if (length(CAR(args)) != 1)
1103
    if (length(CAR(args)) != 1)
1104
	error(_("'hadj' must be of length one"));
1104
	error(_("'hadj' must be of length one"));
1105
    hadj = asReal(CAR(args));
1105
    hadj = asReal(CAR(args));
1106
    args = CDR(args);
1106
    args = CDR(args);
1107
 
1107
 
1108
    /* Optional argument: "padj" */
1108
    /* Optional argument: "padj" */
1109
    PROTECT(padj = coerceVector(CAR(args), REALSXP));
1109
    PROTECT(padj = coerceVector(CAR(args), REALSXP));
1110
    npadj = length(padj);
1110
    npadj = length(padj);
1111
    if (npadj <= 0) error(_("zero length 'padj' specified"));
1111
    if (npadj <= 0) error(_("zero length 'padj' specified"));
1112
 
1112
 
1113
    /* Now we process all the remaining inline par values:
1113
    /* Now we process all the remaining inline par values:
1114
       we need to do it now as x/yaxp are retrieved next.
1114
       we need to do it now as x/yaxp are retrieved next.
1115
       That will set Rf_gpptr, so we update that first - do_plotwindow
1115
       That will set Rf_gpptr, so we update that first - do_plotwindow
1116
       clobbered the Rf_gpptr settings. */
1116
       clobbered the Rf_gpptr settings. */
1117
    GSavePars(dd);
1117
    GSavePars(dd);
1118
    Rf_gpptr(dd)->xaxp[0] = Rf_dpptr(dd)->xaxp[0];
1118
    Rf_gpptr(dd)->xaxp[0] = Rf_dpptr(dd)->xaxp[0];
1119
    Rf_gpptr(dd)->xaxp[1] = Rf_dpptr(dd)->xaxp[1];
1119
    Rf_gpptr(dd)->xaxp[1] = Rf_dpptr(dd)->xaxp[1];
1120
    Rf_gpptr(dd)->xaxp[2] = Rf_dpptr(dd)->xaxp[2];
1120
    Rf_gpptr(dd)->xaxp[2] = Rf_dpptr(dd)->xaxp[2];
1121
    Rf_gpptr(dd)->yaxp[0] = Rf_dpptr(dd)->yaxp[0];
1121
    Rf_gpptr(dd)->yaxp[0] = Rf_dpptr(dd)->yaxp[0];
1122
    Rf_gpptr(dd)->yaxp[1] = Rf_dpptr(dd)->yaxp[1];
1122
    Rf_gpptr(dd)->yaxp[1] = Rf_dpptr(dd)->yaxp[1];
1123
    Rf_gpptr(dd)->yaxp[2] = Rf_dpptr(dd)->yaxp[2];
1123
    Rf_gpptr(dd)->yaxp[2] = Rf_dpptr(dd)->yaxp[2];
1124
    ProcessInlinePars(args, dd, call);
1124
    ProcessInlinePars(args, dd, call);
1125
 
1125
 
1126
    /* Retrieve relevant "par" values. */
1126
    /* Retrieve relevant "par" values. */
1127
 
1127
 
1128
    switch(side) {
1128
    switch(side) {
1129
    case 1:
1129
    case 1:
1130
    case 3:
1130
    case 3:
1131
	axp[0] = Rf_gpptr(dd)->xaxp[0];
1131
	axp[0] = Rf_gpptr(dd)->xaxp[0];
1132
	axp[1] = Rf_gpptr(dd)->xaxp[1];
1132
	axp[1] = Rf_gpptr(dd)->xaxp[1];
1133
	axp[2] = Rf_gpptr(dd)->xaxp[2];
1133
	axp[2] = Rf_gpptr(dd)->xaxp[2];
1134
	usr[0] = Rf_dpptr(dd)->usr[0];
1134
	usr[0] = Rf_dpptr(dd)->usr[0];
1135
	usr[1] = Rf_dpptr(dd)->usr[1];
1135
	usr[1] = Rf_dpptr(dd)->usr[1];
1136
	logflag = Rf_dpptr(dd)->xlog;
1136
	logflag = Rf_dpptr(dd)->xlog;
1137
	nint = Rf_dpptr(dd)->lab[0];
1137
	nint = Rf_dpptr(dd)->lab[0];
1138
	break;
1138
	break;
1139
    case 2:
1139
    case 2:
1140
    case 4:
1140
    case 4:
1141
	axp[0] = Rf_gpptr(dd)->yaxp[0];
1141
	axp[0] = Rf_gpptr(dd)->yaxp[0];
1142
	axp[1] = Rf_gpptr(dd)->yaxp[1];
1142
	axp[1] = Rf_gpptr(dd)->yaxp[1];
1143
	axp[2] = Rf_gpptr(dd)->yaxp[2];
1143
	axp[2] = Rf_gpptr(dd)->yaxp[2];
1144
	usr[0] = Rf_dpptr(dd)->usr[2];
1144
	usr[0] = Rf_dpptr(dd)->usr[2];
1145
	usr[1] = Rf_dpptr(dd)->usr[3];
1145
	usr[1] = Rf_dpptr(dd)->usr[3];
1146
	logflag = Rf_dpptr(dd)->ylog;
1146
	logflag = Rf_dpptr(dd)->ylog;
1147
	nint = Rf_dpptr(dd)->lab[1];
1147
	nint = Rf_dpptr(dd)->lab[1];
1148
	break;
1148
	break;
1149
    }
1149
    }
1150
 
1150
 
1151
    /* Determine the tickmark positions.  Note that these may fall */
1151
    /* Determine the tickmark positions.  Note that these may fall */
1152
    /* outside the plot window. We will clip them in the code below. */
1152
    /* outside the plot window. We will clip them in the code below. */
1153
 
1153
 
1154
    create_at = (length(at) == 0);
1154
    create_at = (length(at) == 0);
1155
    if (create_at) {
1155
    if (create_at) {
1156
	PROTECT(at = CreateAtVector(axp, usr, nint, logflag));
1156
	PROTECT(at = CreateAtVector(axp, usr, nint, logflag));
1157
    }
1157
    }
1158
    else {
1158
    else {
1159
	if (isReal(at)) PROTECT(at = duplicate(at));
1159
	if (isReal(at)) PROTECT(at = duplicate(at));
1160
	else PROTECT(at = coerceVector(at, REALSXP));
1160
	else PROTECT(at = coerceVector(at, REALSXP));
1161
    }
1161
    }
1162
    n = length(at);
1162
    n = length(at);
1163
 
1163
 
1164
    /* Check/setup the tick labels.  This can mean using user-specified */
1164
    /* Check/setup the tick labels.  This can mean using user-specified */
1165
    /* labels, or encoding the "at" positions as strings. */
1165
    /* labels, or encoding the "at" positions as strings. */
1166
 
1166
 
1167
    if (dolabels) {
1167
    if (dolabels) {
1168
	if (length(lab) == 0)
1168
	if (length(lab) == 0)
1169
	    lab = labelformat(at);
1169
	    lab = labelformat(at);
1170
	else {
1170
	else {
1171
	    if (create_at)
1171
	    if (create_at)
1172
		error(_("'labels' is supplied and not 'at'"));
1172
		error(_("'labels' is supplied and not 'at'"));
1173
	    if (!isExpression(lab)) lab = labelformat(lab);
1173
	    if (!isExpression(lab)) lab = labelformat(lab);
1174
	}
1174
	}
1175
	if (length(at) != length(lab))
1175
	if (length(at) != length(lab))
1176
	    error(_("'at' and 'labels' lengths differ, %d != %d"),
1176
	    error(_("'at' and 'labels' lengths differ, %d != %d"),
1177
		      length(at), length(lab));
1177
		      length(at), length(lab));
1178
    }
1178
    }
1179
    PROTECT(lab);
1179
    PROTECT(lab);
1180
 
1180
 
1181
    /* Check there are no NA, Inf or -Inf values for tick positions. */
1181
    /* Check there are no NA, Inf or -Inf values for tick positions. */
1182
    /* The code here is long-winded.  Couldn't we just inline things */
1182
    /* The code here is long-winded.  Couldn't we just inline things */
1183
    /* below.  Hmmm - we need the min and max of the finite values ... */
1183
    /* below.  Hmmm - we need the min and max of the finite values ... */
1184
 
1184
 
1185
    ind = (int *) R_alloc(n, sizeof(int));
1185
    ind = (int *) R_alloc(n, sizeof(int));
1186
    for(i = 0; i < n; i++) ind[i] = i;
1186
    for(i = 0; i < n; i++) ind[i] = i;
1187
    rsort_with_index(REAL(at), ind, n);
1187
    rsort_with_index(REAL(at), ind, n);
1188
    ntmp = 0;
1188
    ntmp = 0;
1189
    for(i = 0; i < n; i++) {
1189
    for(i = 0; i < n; i++) {
1190
	if(R_FINITE(REAL(at)[i])) ntmp = i+1;
1190
	if(R_FINITE(REAL(at)[i])) ntmp = i+1;
1191
    }
1191
    }
1192
    n = ntmp;
1192
    n = ntmp;
1193
    if (n == 0)
1193
    if (n == 0)
1194
	error(_("no locations are finite"));
1194
	error(_("no locations are finite"));
1195
 
1195
 
1196
    /* Ok, all systems are "GO".  Let's get to it. */
1196
    /* Ok, all systems are "GO".  Let's get to it. */
1197
 
1197
 
1198
    /* At this point we know the value of "xaxt" and "yaxt",
1198
    /* At this point we know the value of "xaxt" and "yaxt",
1199
     * so we test to see whether the relevant one is "n".
1199
     * so we test to see whether the relevant one is "n".
1200
     * If it is, we just bail out at this point. */
1200
     * If it is, we just bail out at this point. */
1201
 
1201
 
1202
    if (((side == 1 || side == 3) && Rf_gpptr(dd)->xaxt == 'n') ||
1202
    if (((side == 1 || side == 3) && Rf_gpptr(dd)->xaxt == 'n') ||
1203
	((side == 2 || side == 4) && Rf_gpptr(dd)->yaxt == 'n')) {
1203
	((side == 2 || side == 4) && Rf_gpptr(dd)->yaxt == 'n')) {
1204
	GRestorePars(dd);
1204
	GRestorePars(dd);
1205
	UNPROTECT(4);
1205
	UNPROTECT(4);
1206
	return R_NilValue;
1206
	return R_NilValue;
1207
    }
1207
    }
1208
 
1208
 
1209
 
1209
 
1210
    /* no! we do allow an `lty' argument -- will not be used often though
1210
    /* no! we do allow an `lty' argument -- will not be used often though
1211
     *	Rf_gpptr(dd)->lty = LTY_SOLID; */
1211
     *	Rf_gpptr(dd)->lty = LTY_SOLID; */
1212
    Rf_gpptr(dd)->lty = lty;
1212
    Rf_gpptr(dd)->lty = lty;
1213
    Rf_gpptr(dd)->lwd = lwd;
1213
    Rf_gpptr(dd)->lwd = lwd;
1214
 
1214
 
1215
    /* Override par("xpd") and force clipping to device region. */
1215
    /* Override par("xpd") and force clipping to device region. */
1216
    Rf_gpptr(dd)->xpd = 2;
1216
    Rf_gpptr(dd)->xpd = 2;
1217
 
1217
 
1218
    Rf_gpptr(dd)->adj = R_FINITE(hadj) ? hadj : 0.5;
1218
    Rf_gpptr(dd)->adj = R_FINITE(hadj) ? hadj : 0.5;
1219
    Rf_gpptr(dd)->font = (font == NA_INTEGER)? Rf_gpptr(dd)->fontaxis : font;
1219
    Rf_gpptr(dd)->font = (font == NA_INTEGER)? Rf_gpptr(dd)->fontaxis : font;
1220
    Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase * Rf_gpptr(dd)->cexaxis;
1220
    Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase * Rf_gpptr(dd)->cexaxis;
1221
    /* no!   col = Rf_gpptr(dd)->col; */
1221
    /* no!   col = Rf_gpptr(dd)->col; */
1222
 
1222
 
1223
    /* Draw the axis */
1223
    /* Draw the axis */
1224
    GMode(1, dd);
1224
    GMode(1, dd);
1225
    switch (side) {
1225
    switch (side) {
1226
    case 1: /*--- x-axis -- horizontal --- */
1226
    case 1: /*--- x-axis -- horizontal --- */
1227
    case 3:
1227
    case 3:
1228
	GetAxisLimits(Rf_gpptr(dd)->usr[0], Rf_gpptr(dd)->usr[1], &low, &high);
1228
	GetAxisLimits(Rf_gpptr(dd)->usr[0], Rf_gpptr(dd)->usr[1], &low, &high);
1229
	axis_low  = GConvertX(fmax2(low, REAL(at)[0]), USER, NFC, dd);
1229
	axis_low  = GConvertX(fmax2(low, REAL(at)[0]), USER, NFC, dd);
1230
	axis_high = GConvertX(fmin2(high, REAL(at)[n-1]), USER, NFC, dd);
1230
	axis_high = GConvertX(fmin2(high, REAL(at)[n-1]), USER, NFC, dd);
1231
	if (side == 1) {
1231
	if (side == 1) {
1232
	    if (R_FINITE(pos))
1232
	    if (R_FINITE(pos))
1233
		axis_base = GConvertY(pos, USER, NFC, dd);
1233
		axis_base = GConvertY(pos, USER, NFC, dd);
1234
	    else
1234
	    else
1235
		axis_base = GConvertY(0.0, outer, NFC, dd)
1235
		axis_base = GConvertY(0.0, outer, NFC, dd)
1236
		    - GConvertYUnits(line, LINES, NFC, dd);
1236
		    - GConvertYUnits(line, LINES, NFC, dd);
1237
	    if (R_FINITE(Rf_gpptr(dd)->tck)) {
1237
	    if (R_FINITE(Rf_gpptr(dd)->tck)) {
1238
		double len, xu, yu;
1238
		double len, xu, yu;
1239
		if(Rf_gpptr(dd)->tck > 0.5)
1239
		if(Rf_gpptr(dd)->tck > 0.5)
1240
		    len = GConvertYUnits(Rf_gpptr(dd)->tck, NPC, NFC, dd);
1240
		    len = GConvertYUnits(Rf_gpptr(dd)->tck, NPC, NFC, dd);
1241
		else {
1241
		else {
1242
		    xu = GConvertXUnits(Rf_gpptr(dd)->tck, NPC, INCHES, dd);
1242
		    xu = GConvertXUnits(Rf_gpptr(dd)->tck, NPC, INCHES, dd);
1243
		    yu = GConvertYUnits(Rf_gpptr(dd)->tck, NPC, INCHES, dd);
1243
		    yu = GConvertYUnits(Rf_gpptr(dd)->tck, NPC, INCHES, dd);
1244
		    xu = (fabs(xu) < fabs(yu)) ? xu : yu;
1244
		    xu = (fabs(xu) < fabs(yu)) ? xu : yu;
1245
		    len = GConvertYUnits(xu, INCHES, NFC, dd);
1245
		    len = GConvertYUnits(xu, INCHES, NFC, dd);
1246
		}
1246
		}
1247
		axis_tick = axis_base + len;
1247
		axis_tick = axis_base + len;
1248
 
1248
 
1249
	    } else
1249
	    } else
1250
		axis_tick = axis_base +
1250
		axis_tick = axis_base +
1251
			GConvertYUnits(Rf_gpptr(dd)->tcl, LINES, NFC, dd);
1251
			GConvertYUnits(Rf_gpptr(dd)->tcl, LINES, NFC, dd);
1252
	}
1252
	}
1253
	else {
1253
	else {
1254
	    if (R_FINITE(pos))
1254
	    if (R_FINITE(pos))
1255
		axis_base = GConvertY(pos, USER, NFC, dd);
1255
		axis_base = GConvertY(pos, USER, NFC, dd);
1256
	    else
1256
	    else
1257
		axis_base =  GConvertY(1.0, outer, NFC, dd)
1257
		axis_base =  GConvertY(1.0, outer, NFC, dd)
1258
		    + GConvertYUnits(line, LINES, NFC, dd);
1258
		    + GConvertYUnits(line, LINES, NFC, dd);
1259
	    if (R_FINITE(Rf_gpptr(dd)->tck)) {
1259
	    if (R_FINITE(Rf_gpptr(dd)->tck)) {
1260
		double len, xu, yu;
1260
		double len, xu, yu;
1261
		if(Rf_gpptr(dd)->tck > 0.5)
1261
		if(Rf_gpptr(dd)->tck > 0.5)
1262
		    len = GConvertYUnits(Rf_gpptr(dd)->tck, NPC, NFC, dd);
1262
		    len = GConvertYUnits(Rf_gpptr(dd)->tck, NPC, NFC, dd);
1263
		else {
1263
		else {
1264
		    xu = GConvertXUnits(Rf_gpptr(dd)->tck, NPC, INCHES, dd);
1264
		    xu = GConvertXUnits(Rf_gpptr(dd)->tck, NPC, INCHES, dd);
1265
		    yu = GConvertYUnits(Rf_gpptr(dd)->tck, NPC, INCHES, dd);
1265
		    yu = GConvertYUnits(Rf_gpptr(dd)->tck, NPC, INCHES, dd);
1266
		    xu = (fabs(xu) < fabs(yu)) ? xu : yu;
1266
		    xu = (fabs(xu) < fabs(yu)) ? xu : yu;
1267
		    len = GConvertYUnits(xu, INCHES, NFC, dd);
1267
		    len = GConvertYUnits(xu, INCHES, NFC, dd);
1268
		}
1268
		}
1269
		axis_tick = axis_base - len;
1269
		axis_tick = axis_base - len;
1270
	    } else
1270
	    } else
1271
		axis_tick = axis_base -
1271
		axis_tick = axis_base -
1272
		    GConvertYUnits(Rf_gpptr(dd)->tcl, LINES, NFC, dd);
1272
		    GConvertYUnits(Rf_gpptr(dd)->tcl, LINES, NFC, dd);
1273
	}
1273
	}
1274
	if (doticks) {
1274
	if (doticks) {
1275
	    Rf_gpptr(dd)->col = col;/*was fg */
1275
	    Rf_gpptr(dd)->col = col;/*was fg */
1276
	    GLine(axis_low, axis_base, axis_high, axis_base, NFC, dd);
1276
	    GLine(axis_low, axis_base, axis_high, axis_base, NFC, dd);
1277
	    for (i = 0; i < n; i++) {
1277
	    for (i = 0; i < n; i++) {
1278
		x = REAL(at)[i];
1278
		x = REAL(at)[i];
1279
		if (low <= x && x <= high) {
1279
		if (low <= x && x <= high) {
1280
		    x = GConvertX(x, USER, NFC, dd);
1280
		    x = GConvertX(x, USER, NFC, dd);
1281
		    GLine(x, axis_base, x, axis_tick, NFC, dd);
1281
		    GLine(x, axis_base, x, axis_tick, NFC, dd);
1282
		}
1282
		}
1283
	    }
1283
	    }
1284
	}
1284
	}
1285
	/* Tickmark labels. */
1285
	/* Tickmark labels. */
1286
	Rf_gpptr(dd)->col = Rf_gpptr(dd)->colaxis;
1286
	Rf_gpptr(dd)->col = Rf_gpptr(dd)->colaxis;
1287
	gap = GStrWidth("m", NFC, dd);	/* FIXUP x/y distance */
1287
	gap = GStrWidth("m", NFC, dd);	/* FIXUP x/y distance */
1288
	tlast = -1.0;
1288
	tlast = -1.0;
1289
	if (!R_FINITE(hadj)) {
1289
	if (!R_FINITE(hadj)) {
1290
	    if (Rf_gpptr(dd)->las == 2 || Rf_gpptr(dd)->las == 3) {
1290
	    if (Rf_gpptr(dd)->las == 2 || Rf_gpptr(dd)->las == 3) {
1291
		Rf_gpptr(dd)->adj = (side == 1) ? 1 : 0;
1291
		Rf_gpptr(dd)->adj = (side == 1) ? 1 : 0;
1292
	    }
1292
	    }
1293
	    else Rf_gpptr(dd)->adj = 0.5;
1293
	    else Rf_gpptr(dd)->adj = 0.5;
1294
	}
1294
	}
1295
	if (side == 1) {
1295
	if (side == 1) {
1296
	    axis_lab = - axis_base
1296
	    axis_lab = - axis_base
1297
		+ GConvertYUnits(Rf_gpptr(dd)->mgp[1]-lineoff, LINES, NFC, dd)
1297
		+ GConvertYUnits(Rf_gpptr(dd)->mgp[1]-lineoff, LINES, NFC, dd)
1298
		+ GConvertY(0.0, NPC, NFC, dd);
1298
		+ GConvertY(0.0, NPC, NFC, dd);
1299
	}
1299
	}
1300
	else { /* side == 3 */
1300
	else { /* side == 3 */
1301
	    axis_lab = axis_base
1301
	    axis_lab = axis_base
1302
		+ GConvertYUnits(Rf_gpptr(dd)->mgp[1]-lineoff, LINES, NFC, dd)
1302
		+ GConvertYUnits(Rf_gpptr(dd)->mgp[1]-lineoff, LINES, NFC, dd)
1303
		- GConvertY(1.0, NPC, NFC, dd);
1303
		- GConvertY(1.0, NPC, NFC, dd);
1304
	}
1304
	}
1305
	axis_lab = GConvertYUnits(axis_lab, NFC, LINES, dd);
1305
	axis_lab = GConvertYUnits(axis_lab, NFC, LINES, dd);
1306
 
1306
 
1307
	/* The order of processing is important here. */
1307
	/* The order of processing is important here. */
1308
	/* We must ensure that the labels are drawn left-to-right. */
1308
	/* We must ensure that the labels are drawn left-to-right. */
1309
	/* The logic here is getting way too convoluted. */
1309
	/* The logic here is getting way too convoluted. */
1310
	/* This needs a serious rewrite. */
1310
	/* This needs a serious rewrite. */
1311
 
1311
 
1312
	if (Rf_gpptr(dd)->usr[0] > Rf_gpptr(dd)->usr[1]) {
1312
	if (Rf_gpptr(dd)->usr[0] > Rf_gpptr(dd)->usr[1]) {
1313
	    istart = n - 1;
1313
	    istart = n - 1;
1314
	    iend = -1;
1314
	    iend = -1;
1315
	    incr = -1;
1315
	    incr = -1;
1316
	}
1316
	}
1317
	else {
1317
	else {
1318
	    istart = 0;
1318
	    istart = 0;
1319
	    iend = n;
1319
	    iend = n;
1320
	    incr = 1;
1320
	    incr = 1;
1321
	}
1321
	}
1322
	for (i = istart; i != iend; i += incr) {
1322
	for (i = istart; i != iend; i += incr) {
1323
	    double padjval = REAL(padj)[i%npadj];
1323
	    double padjval = REAL(padj)[i%npadj];
1324
	    padjval = ComputePAdjValue(padjval, side, Rf_gpptr(dd)->las);
1324
	    padjval = ComputePAdjValue(padjval, side, Rf_gpptr(dd)->las);
1325
	    x = REAL(at)[i];
1325
	    x = REAL(at)[i];
1326
	    if (!R_FINITE(x)) continue;
1326
	    if (!R_FINITE(x)) continue;
1327
	    temp = GConvertX(x, USER, NFC, dd);
1327
	    temp = GConvertX(x, USER, NFC, dd);
1328
	    if (dolabels) {
1328
	    if (dolabels) {
1329
		/* Clip tick labels to user coordinates. */
1329
		/* Clip tick labels to user coordinates. */
1330
		if (x > low && x < high) {
1330
		if (x > low && x < high) {
1331
		    if (isExpression(lab)) {
1331
		    if (isExpression(lab)) {
1332
			GMMathText(VECTOR_ELT(lab, ind[i]), side,
1332
			GMMathText(VECTOR_ELT(lab, ind[i]), side,
1333
				   axis_lab, 0, x, Rf_gpptr(dd)->las,
1333
				   axis_lab, 0, x, Rf_gpptr(dd)->las,
1334
				   padjval, dd);
1334
				   padjval, dd);
1335
		    }
1335
		    }
1336
		    else {
1336
		    else {
1337
			label = STRING_ELT(lab, ind[i]);
1337
			label = STRING_ELT(lab, ind[i]);
1338
			if(label != NA_STRING) {
1338
			if(label != NA_STRING) {
1339
			    const char *ss = translateChar(label);
1339
			    const char *ss = translateChar(label);
1340
			    labw = GStrWidth(ss, NFC, dd);
1340
			    labw = GStrWidth(ss, NFC, dd);
1341
			    tnew = temp - 0.5 * labw;
1341
			    tnew = temp - 0.5 * labw;
1342
			    /* Check room for perpendicular labels. */
1342
			    /* Check room for perpendicular labels. */
1343
			    if (Rf_gpptr(dd)->las == 2 ||
1343
			    if (Rf_gpptr(dd)->las == 2 ||
1344
				Rf_gpptr(dd)->las == 3 ||
1344
				Rf_gpptr(dd)->las == 3 ||
1345
				tnew - tlast >= gap) {
1345
				tnew - tlast >= gap) {
1346
				GMtext(ss, side, axis_lab, 0, x,
1346
				GMtext(ss, side, axis_lab, 0, x,
1347
				       Rf_gpptr(dd)->las, padjval, dd);
1347
				       Rf_gpptr(dd)->las, padjval, dd);
1348
				tlast = temp + 0.5 *labw;
1348
				tlast = temp + 0.5 *labw;
1349
			    }
1349
			    }
1350
			}
1350
			}
1351
		    }
1351
		    }
1352
		}
1352
		}
1353
	    }
1353
	    }
1354
	}
1354
	}
1355
	break;
1355
	break;
1356
 
1356
 
1357
    case 2: /*--- y-axis -- vertical --- */
1357
    case 2: /*--- y-axis -- vertical --- */
1358
    case 4:
1358
    case 4:
1359
	GetAxisLimits(Rf_gpptr(dd)->usr[2], Rf_gpptr(dd)->usr[3], &low, &high);
1359
	GetAxisLimits(Rf_gpptr(dd)->usr[2], Rf_gpptr(dd)->usr[3], &low, &high);
1360
	axis_low = GConvertY(fmax2(low, REAL(at)[0]), USER, NFC, dd);
1360
	axis_low = GConvertY(fmax2(low, REAL(at)[0]), USER, NFC, dd);
1361
	axis_high = GConvertY(fmin2(high, REAL(at)[n-1]), USER, NFC, dd);
1361
	axis_high = GConvertY(fmin2(high, REAL(at)[n-1]), USER, NFC, dd);
1362
	if (side == 2) {
1362
	if (side == 2) {
1363
	    if (R_FINITE(pos))
1363
	    if (R_FINITE(pos))
1364
		axis_base = GConvertX(pos, USER, NFC, dd);
1364
		axis_base = GConvertX(pos, USER, NFC, dd);
1365
	    else
1365
	    else
1366
		axis_base =  GConvertX(0.0, outer, NFC, dd)
1366
		axis_base =  GConvertX(0.0, outer, NFC, dd)
1367
		    - GConvertXUnits(line, LINES, NFC, dd);
1367
		    - GConvertXUnits(line, LINES, NFC, dd);
1368
	    if (R_FINITE(Rf_gpptr(dd)->tck)) {
1368
	    if (R_FINITE(Rf_gpptr(dd)->tck)) {
1369
		double len, xu, yu;
1369
		double len, xu, yu;
1370
		if(Rf_gpptr(dd)->tck > 0.5)
1370
		if(Rf_gpptr(dd)->tck > 0.5)
1371
		    len = GConvertXUnits(Rf_gpptr(dd)->tck, NPC, NFC, dd);
1371
		    len = GConvertXUnits(Rf_gpptr(dd)->tck, NPC, NFC, dd);
1372
		else {
1372
		else {
1373
		    xu = GConvertXUnits(Rf_gpptr(dd)->tck, NPC, INCHES, dd);
1373
		    xu = GConvertXUnits(Rf_gpptr(dd)->tck, NPC, INCHES, dd);
1374
		    yu = GConvertYUnits(Rf_gpptr(dd)->tck, NPC, INCHES, dd);
1374
		    yu = GConvertYUnits(Rf_gpptr(dd)->tck, NPC, INCHES, dd);
1375
		    xu = (fabs(xu) < fabs(yu)) ? xu : yu;
1375
		    xu = (fabs(xu) < fabs(yu)) ? xu : yu;
1376
		    len = GConvertXUnits(xu, INCHES, NFC, dd);
1376
		    len = GConvertXUnits(xu, INCHES, NFC, dd);
1377
		}
1377
		}
1378
		axis_tick = axis_base + len;
1378
		axis_tick = axis_base + len;
1379
	    } else
1379
	    } else
1380
		axis_tick = axis_base +
1380
		axis_tick = axis_base +
1381
		    GConvertXUnits(Rf_gpptr(dd)->tcl, LINES, NFC, dd);
1381
		    GConvertXUnits(Rf_gpptr(dd)->tcl, LINES, NFC, dd);
1382
	}
1382
	}
1383
	else {
1383
	else {
1384
	    if (R_FINITE(pos))
1384
	    if (R_FINITE(pos))
1385
		axis_base = GConvertX(pos, USER, NFC, dd);
1385
		axis_base = GConvertX(pos, USER, NFC, dd);
1386
	    else
1386
	    else
1387
		axis_base =  GConvertX(1.0, outer, NFC, dd)
1387
		axis_base =  GConvertX(1.0, outer, NFC, dd)
1388
		    + GConvertXUnits(line, LINES, NFC, dd);
1388
		    + GConvertXUnits(line, LINES, NFC, dd);
1389
	    if (R_FINITE(Rf_gpptr(dd)->tck)) {
1389
	    if (R_FINITE(Rf_gpptr(dd)->tck)) {
1390
		double len, xu, yu;
1390
		double len, xu, yu;
1391
		if(Rf_gpptr(dd)->tck > 0.5)
1391
		if(Rf_gpptr(dd)->tck > 0.5)
1392
		    len = GConvertXUnits(Rf_gpptr(dd)->tck, NPC, NFC, dd);
1392
		    len = GConvertXUnits(Rf_gpptr(dd)->tck, NPC, NFC, dd);
1393
		else {
1393
		else {
1394
		    xu = GConvertXUnits(Rf_gpptr(dd)->tck, NPC, INCHES, dd);
1394
		    xu = GConvertXUnits(Rf_gpptr(dd)->tck, NPC, INCHES, dd);
1395
		    yu = GConvertYUnits(Rf_gpptr(dd)->tck, NPC, INCHES, dd);
1395
		    yu = GConvertYUnits(Rf_gpptr(dd)->tck, NPC, INCHES, dd);
1396
		    xu = (fabs(xu) < fabs(yu)) ? xu : yu;
1396
		    xu = (fabs(xu) < fabs(yu)) ? xu : yu;
1397
		    len = GConvertXUnits(xu, INCHES, NFC, dd);
1397
		    len = GConvertXUnits(xu, INCHES, NFC, dd);
1398
		}
1398
		}
1399
		axis_tick = axis_base - len;
1399
		axis_tick = axis_base - len;
1400
	    } else
1400
	    } else
1401
		axis_tick = axis_base -
1401
		axis_tick = axis_base -
1402
		    GConvertXUnits(Rf_gpptr(dd)->tcl, LINES, NFC, dd);
1402
		    GConvertXUnits(Rf_gpptr(dd)->tcl, LINES, NFC, dd);
1403
	}
1403
	}
1404
	if (doticks) {
1404
	if (doticks) {
1405
	    Rf_gpptr(dd)->col = col;/*was fg */
1405
	    Rf_gpptr(dd)->col = col;/*was fg */
1406
	    GLine(axis_base, axis_low, axis_base, axis_high, NFC, dd);
1406
	    GLine(axis_base, axis_low, axis_base, axis_high, NFC, dd);
1407
	    for (i = 0; i < n; i++) {
1407
	    for (i = 0; i < n; i++) {
1408
		y = REAL(at)[i];
1408
		y = REAL(at)[i];
1409
		if (low <= y && y <= high) {
1409
		if (low <= y && y <= high) {
1410
		    y = GConvertY(y, USER, NFC, dd);
1410
		    y = GConvertY(y, USER, NFC, dd);
1411
		    GLine(axis_base, y, axis_tick, y, NFC, dd);
1411
		    GLine(axis_base, y, axis_tick, y, NFC, dd);
1412
		}
1412
		}
1413
	    }
1413
	    }
1414
	}
1414
	}
1415
	/* Tickmark labels. */
1415
	/* Tickmark labels. */
1416
	Rf_gpptr(dd)->col = Rf_gpptr(dd)->colaxis;
1416
	Rf_gpptr(dd)->col = Rf_gpptr(dd)->colaxis;
1417
	gap = GStrWidth("m", INCHES, dd);
1417
	gap = GStrWidth("m", INCHES, dd);
1418
	gap = GConvertYUnits(gap, INCHES, NFC, dd);
1418
	gap = GConvertYUnits(gap, INCHES, NFC, dd);
1419
	tlast = -1.0;
1419
	tlast = -1.0;
1420
	if (!R_FINITE(hadj)) {
1420
	if (!R_FINITE(hadj)) {
1421
	    if (Rf_gpptr(dd)->las == 1 || Rf_gpptr(dd)->las == 2) {
1421
	    if (Rf_gpptr(dd)->las == 1 || Rf_gpptr(dd)->las == 2) {
1422
		Rf_gpptr(dd)->adj = (side == 2) ? 1 : 0;
1422
		Rf_gpptr(dd)->adj = (side == 2) ? 1 : 0;
1423
	    }
1423
	    }
1424
	    else Rf_gpptr(dd)->adj = 0.5;
1424
	    else Rf_gpptr(dd)->adj = 0.5;
1425
	}
1425
	}
1426
	if (side == 2) {
1426
	if (side == 2) {
1427
	    axis_lab = - axis_base
1427
	    axis_lab = - axis_base
1428
		+ GConvertXUnits(Rf_gpptr(dd)->mgp[1]-lineoff, LINES, NFC, dd)
1428
		+ GConvertXUnits(Rf_gpptr(dd)->mgp[1]-lineoff, LINES, NFC, dd)
1429
		+ GConvertX(0.0, NPC, NFC, dd);
1429
		+ GConvertX(0.0, NPC, NFC, dd);
1430
	}
1430
	}
1431
	else { /* side == 4 */
1431
	else { /* side == 4 */
1432
	    axis_lab = axis_base
1432
	    axis_lab = axis_base
1433
		+ GConvertXUnits(Rf_gpptr(dd)->mgp[1]-lineoff, LINES, NFC, dd)
1433
		+ GConvertXUnits(Rf_gpptr(dd)->mgp[1]-lineoff, LINES, NFC, dd)
1434
		- GConvertX(1.0, NPC, NFC, dd);
1434
		- GConvertX(1.0, NPC, NFC, dd);
1435
	}
1435
	}
1436
	axis_lab = GConvertXUnits(axis_lab, NFC, LINES, dd);
1436
	axis_lab = GConvertXUnits(axis_lab, NFC, LINES, dd);
1437
 
1437
 
1438
	/* The order of processing is important here. */
1438
	/* The order of processing is important here. */
1439
	/* We must ensure that the labels are drawn left-to-right. */
1439
	/* We must ensure that the labels are drawn left-to-right. */
1440
	/* The logic here is getting way too convoluted. */
1440
	/* The logic here is getting way too convoluted. */
1441
	/* This needs a serious rewrite. */
1441
	/* This needs a serious rewrite. */
1442
 
1442
 
1443
	if (Rf_gpptr(dd)->usr[2] > Rf_gpptr(dd)->usr[3]) {
1443
	if (Rf_gpptr(dd)->usr[2] > Rf_gpptr(dd)->usr[3]) {
1444
	    istart = n - 1;
1444
	    istart = n - 1;
1445
	    iend = -1;
1445
	    iend = -1;
1446
	    incr = -1;
1446
	    incr = -1;
1447
	}
1447
	}
1448
	else {
1448
	else {
1449
	    istart = 0;
1449
	    istart = 0;
1450
	    iend = n;
1450
	    iend = n;
1451
	    incr = 1;
1451
	    incr = 1;
1452
	}
1452
	}
1453
	for (i = istart; i != iend; i += incr) {
1453
	for (i = istart; i != iend; i += incr) {
1454
	    double padjval = REAL(padj)[i%npadj];
1454
	    double padjval = REAL(padj)[i%npadj];
1455
	    padjval = ComputePAdjValue(padjval, side, Rf_gpptr(dd)->las);
1455
	    padjval = ComputePAdjValue(padjval, side, Rf_gpptr(dd)->las);
1456
	    y = REAL(at)[i];
1456
	    y = REAL(at)[i];
1457
	    if (!R_FINITE(y)) continue;
1457
	    if (!R_FINITE(y)) continue;
1458
	    temp = GConvertY(y, USER, NFC, dd);
1458
	    temp = GConvertY(y, USER, NFC, dd);
1459
	    if (dolabels) {
1459
	    if (dolabels) {
1460
		/* Clip tick labels to user coordinates. */
1460
		/* Clip tick labels to user coordinates. */
1461
		if (y > low && y < high) {
1461
		if (y > low && y < high) {
1462
		    if (isExpression(lab)) {
1462
		    if (isExpression(lab)) {
1463
			GMMathText(VECTOR_ELT(lab, ind[i]), side,
1463
			GMMathText(VECTOR_ELT(lab, ind[i]), side,
1464
				   axis_lab, 0, y, Rf_gpptr(dd)->las,
1464
				   axis_lab, 0, y, Rf_gpptr(dd)->las,
1465
				   padjval, dd);
1465
				   padjval, dd);
1466
		    }
1466
		    }
1467
		    else {
1467
		    else {
1468
			label = STRING_ELT(lab, ind[i]);
1468
			label = STRING_ELT(lab, ind[i]);
1469
			if(label != NA_STRING) {
1469
			if(label != NA_STRING) {
1470
			    const char *ss = translateChar(label);
1470
			    const char *ss = translateChar(label);
1471
			    labw = GStrWidth(ss, INCHES, dd);
1471
			    labw = GStrWidth(ss, INCHES, dd);
1472
			    labw = GConvertYUnits(labw, INCHES, NFC, dd);
1472
			    labw = GConvertYUnits(labw, INCHES, NFC, dd);
1473
			    tnew = temp - 0.5 * labw;
1473
			    tnew = temp - 0.5 * labw;
1474
			    /* Check room for perpendicular labels. */
1474
			    /* Check room for perpendicular labels. */
1475
			    if (Rf_gpptr(dd)->las == 1 ||
1475
			    if (Rf_gpptr(dd)->las == 1 ||
1476
				Rf_gpptr(dd)->las == 2 ||
1476
				Rf_gpptr(dd)->las == 2 ||
1477
				tnew - tlast >= gap) {
1477
				tnew - tlast >= gap) {
1478
				GMtext(ss, side, axis_lab, 0, y,
1478
				GMtext(ss, side, axis_lab, 0, y,
1479
				       Rf_gpptr(dd)->las, padjval, dd);
1479
				       Rf_gpptr(dd)->las, padjval, dd);
1480
				tlast = temp + 0.5 *labw;
1480
				tlast = temp + 0.5 *labw;
1481
			    }
1481
			    }
1482
			}
1482
			}
1483
		    }
1483
		    }
1484
		}
1484
		}
1485
	    }
1485
	    }
1486
	}
1486
	}
1487
	break;
1487
	break;
1488
    } /* end  switch(side, ..) */
1488
    } /* end  switch(side, ..) */
1489
    GMode(0, dd);
1489
    GMode(0, dd);
1490
    GRestorePars(dd);
1490
    GRestorePars(dd);
1491
    /* NOTE: only record operation if no "error"  */
1491
    /* NOTE: only record operation if no "error"  */
1492
    if (GRecording(call, dd))
1492
    if (GRecording(call, dd))
1493
	recordGraphicOperation(op, originalArgs, dd);
1493
	recordGraphicOperation(op, originalArgs, dd);
1494
    UNPROTECT(4); /* lab, at, lab, padj again */
1494
    UNPROTECT(4); /* lab, at, lab, padj again */
1495
    return at;
1495
    return at;
1496
}/* do_axis */
1496
}/* do_axis */
1497
 
1497
 
1498
 
1498
 
1499
SEXP attribute_hidden do_plot_xy(SEXP call, SEXP op, SEXP args, SEXP env)
1499
SEXP attribute_hidden do_plot_xy(SEXP call, SEXP op, SEXP args, SEXP env)
1500
{
1500
{
1501
/*	plot.xy(xy, type, pch, lty, col, bg, cex, lwd, ...)
1501
/*	plot.xy(xy, type, pch, lty, col, bg, cex, lwd, ...)
1502
 
1502
 
1503
 *	plot points or lines of various types
1503
 *	plot points or lines of various types
1504
 */
1504
 */
1505
    SEXP sxy, sx, sy, pch, cex, col, bg, lty, lwd;
1505
    SEXP sxy, sx, sy, pch, cex, col, bg, lty, lwd;
1506
    double *x, *y, xold, yold, xx, yy, thiscex, thislwd;
1506
    double *x, *y, xold, yold, xx, yy, thiscex, thislwd;
1507
    int i, n, npch, ncex, ncol, nbg, /*nlty,*/ nlwd,
1507
    int i, n, npch, ncex, ncol, nbg, /*nlty,*/ nlwd,
1508
	type=0, start=0, thispch, thiscol;
1508
	type=0, start=0, thispch, thiscol;
1509
    void *vmax = NULL /* -Wall */;
1509
    void *vmax = NULL /* -Wall */;
1510
 
1510
 
1511
    SEXP originalArgs = args;
1511
    SEXP originalArgs = args;
1512
    DevDesc *dd = CurrentDevice();
1512
    DevDesc *dd = CurrentDevice();
1513
 
1513
 
1514
    /* Basic Checks */
1514
    /* Basic Checks */
1515
    GCheckState(dd);
1515
    GCheckState(dd);
1516
    if (length(args) < 7)
1516
    if (length(args) < 7)
1517
	error(_("too few arguments"));
1517
	error(_("too few arguments"));
1518
 
1518
 
1519
    /* Required Arguments */
1519
    /* Required Arguments */
1520
#define PLOT_XY_DEALING(subname)				\
1520
#define PLOT_XY_DEALING(subname)				\
1521
    sx = R_NilValue;		/* -Wall */			\
1521
    sx = R_NilValue;		/* -Wall */			\
1522
    sy = R_NilValue;		/* -Wall */			\
1522
    sy = R_NilValue;		/* -Wall */			\
1523
    sxy = CAR(args);						\
1523
    sxy = CAR(args);						\
1524
    if (isNewList(sxy) && length(sxy) >= 2) {			\
1524
    if (isNewList(sxy) && length(sxy) >= 2) {			\
1525
	internalTypeCheck(call, sx = VECTOR_ELT(sxy, 0), REALSXP); \
1525
	internalTypeCheck(call, sx = VECTOR_ELT(sxy, 0), REALSXP); \
1526
	internalTypeCheck(call, sy = VECTOR_ELT(sxy, 1), REALSXP); \
1526
	internalTypeCheck(call, sy = VECTOR_ELT(sxy, 1), REALSXP); \
1527
    }								\
1527
    }								\
1528
    else if (isList(sxy) && length(sxy) >= 2) {			\
1528
    else if (isList(sxy) && length(sxy) >= 2) {			\
1529
	internalTypeCheck(call, sx = CAR(sxy), REALSXP);	\
1529
	internalTypeCheck(call, sx = CAR(sxy), REALSXP);	\
1530
	internalTypeCheck(call, sy = CADR(sxy), REALSXP);	\
1530
	internalTypeCheck(call, sy = CADR(sxy), REALSXP);	\
1531
    }								\
1531
    }								\
1532
    else							\
1532
    else							\
1533
	error(_("invalid plotting structure"));	\
1533
	error(_("invalid plotting structure"));	\
1534
    if (LENGTH(sx) != LENGTH(sy))				\
1534
    if (LENGTH(sx) != LENGTH(sy))				\
1535
	error(_("'x' and 'y' lengths differ in %s()"), subname);\
1535
	error(_("'x' and 'y' lengths differ in %s()"), subname);\
1536
    n = LENGTH(sx);						\
1536
    n = LENGTH(sx);						\
1537
    args = CDR(args)
1537
    args = CDR(args)
1538
 
1538
 
1539
    PLOT_XY_DEALING("plot.xy");
1539
    PLOT_XY_DEALING("plot.xy");
1540
 
1540
 
1541
    if (isNull(CAR(args))) type = 'p';
1541
    if (isNull(CAR(args))) type = 'p';
1542
    else {
1542
    else {
1543
	if (isString(CAR(args)) && LENGTH(CAR(args)) == 1 &&
1543
	if (isString(CAR(args)) && LENGTH(CAR(args)) == 1 &&
1544
	    LENGTH(pch = STRING_ELT(CAR(args), 0)) >= 1) {
1544
	    LENGTH(pch = STRING_ELT(CAR(args), 0)) >= 1) {
1545
	    if(LENGTH(pch) > 1)
1545
	    if(LENGTH(pch) > 1)
1546
		warning(_("plot type '%s' will be truncated to first character"),
1546
		warning(_("plot type '%s' will be truncated to first character"),
1547
			CHAR(pch));
1547
			CHAR(pch));
1548
	    type = CHAR(pch)[0];
1548
	    type = CHAR(pch)[0];
1549
	}
1549
	}
1550
	else error(_("invalid plot type"));
1550
	else error(_("invalid plot type"));
1551
    }
1551
    }
1552
    args = CDR(args);
1552
    args = CDR(args);
1553
 
1553
 
1554
    PROTECT(pch = FixupPch(CAR(args), Rf_gpptr(dd)->pch));	args = CDR(args);
1554
    PROTECT(pch = FixupPch(CAR(args), Rf_gpptr(dd)->pch));	args = CDR(args);
1555
    npch = length(pch);
1555
    npch = length(pch);
1556
 
1556
 
1557
    PROTECT(lty = FixupLty(CAR(args), Rf_gpptr(dd)->lty));	args = CDR(args);
1557
    PROTECT(lty = FixupLty(CAR(args), Rf_gpptr(dd)->lty));	args = CDR(args);
1558
    /* nlty = length(lty);*/
1558
    /* nlty = length(lty);*/
1559
 
1559
 
1560
    /* Default col was NA_INTEGER (0x80000000) which was interpreted
1560
    /* Default col was NA_INTEGER (0x80000000) which was interpreted
1561
       as zero (black) or "don't draw" depending on line/rect/circle
1561
       as zero (black) or "don't draw" depending on line/rect/circle
1562
       situation. Now we set the default to zero and don't plot at all
1562
       situation. Now we set the default to zero and don't plot at all
1563
       if col==NA.
1563
       if col==NA.
1564
 
1564
 
1565
       FIXME: bg needs similar change, but that requires changes to
1565
       FIXME: bg needs similar change, but that requires changes to
1566
       the specific drivers. */
1566
       the specific drivers. */
1567
 
1567
 
1568
    PROTECT(col = FixupCol(CAR(args), 0));		args = CDR(args);
1568
    PROTECT(col = FixupCol(CAR(args), 0));		args = CDR(args);
1569
    ncol = LENGTH(col);
1569
    ncol = LENGTH(col);
1570
 
1570
 
1571
    PROTECT(bg = FixupCol(CAR(args), R_TRANWHITE));	args = CDR(args);
1571
    PROTECT(bg = FixupCol(CAR(args), R_TRANWHITE));	args = CDR(args);
1572
    nbg = LENGTH(bg);
1572
    nbg = LENGTH(bg);
1573
 
1573
 
1574
    PROTECT(cex = FixupCex(CAR(args), 1.0));		args = CDR(args);
1574
    PROTECT(cex = FixupCex(CAR(args), 1.0));		args = CDR(args);
1575
    ncex = LENGTH(cex);
1575
    ncex = LENGTH(cex);
1576
 
1576
 
1577
    PROTECT(lwd = FixupLwd(CAR(args), Rf_gpptr(dd)->lwd)); args = CDR(args);
1577
    PROTECT(lwd = FixupLwd(CAR(args), Rf_gpptr(dd)->lwd)); args = CDR(args);
1578
    nlwd = LENGTH(lwd);
1578
    nlwd = LENGTH(lwd);
1579
 
1579
 
1580
    /* Miscellaneous Graphical Parameters */
1580
    /* Miscellaneous Graphical Parameters */
1581
    GSavePars(dd);
1581
    GSavePars(dd);
1582
    ProcessInlinePars(args, dd, call);
1582
    ProcessInlinePars(args, dd, call);
1583
 
1583
 
1584
    x = REAL(sx);
1584
    x = REAL(sx);
1585
    y = REAL(sy);
1585
    y = REAL(sy);
1586
 
1586
 
1587
    if (INTEGER(lty)[0] != NA_INTEGER)
1587
    if (INTEGER(lty)[0] != NA_INTEGER)
1588
	Rf_gpptr(dd)->lty = INTEGER(lty)[0];
1588
	Rf_gpptr(dd)->lty = INTEGER(lty)[0];
1589
    if (R_FINITE( (thislwd = REAL(lwd)[0]) ))
1589
    if (R_FINITE( (thislwd = REAL(lwd)[0]) ))
1590
	Rf_gpptr(dd)->lwd = thislwd; /* but do recycle for "p" etc */
1590
	Rf_gpptr(dd)->lwd = thislwd; /* but do recycle for "p" etc */
1591
 
1591
 
1592
    GMode(1, dd);
1592
    GMode(1, dd);
1593
    /* removed by paul 26/5/99 because all clipping now happens in graphics.c
1593
    /* removed by paul 26/5/99 because all clipping now happens in graphics.c
1594
     * GClip(dd);
1594
     * GClip(dd);
1595
     */
1595
     */
1596
 
1596
 
1597
    /* Line drawing :*/
1597
    /* Line drawing :*/
1598
    switch(type) {
1598
    switch(type) {
1599
    case 'l':
1599
    case 'l':
1600
    case 'o':
1600
    case 'o':
1601
	/* lines and overplotted lines and points */
1601
	/* lines and overplotted lines and points */
1602
	Rf_gpptr(dd)->col = INTEGER(col)[0];
1602
	Rf_gpptr(dd)->col = INTEGER(col)[0];
1603
	xold = NA_REAL;
1603
	xold = NA_REAL;
1604
	yold = NA_REAL;
1604
	yold = NA_REAL;
1605
	for (i = 0; i < n; i++) {
1605
	for (i = 0; i < n; i++) {
1606
	    xx = x[i];
1606
	    xx = x[i];
1607
	    yy = y[i];
1607
	    yy = y[i];
1608
	    /* do the conversion now to check for non-finite */
1608
	    /* do the conversion now to check for non-finite */
1609
	    GConvert(&xx, &yy, USER, DEVICE, dd);
1609
	    GConvert(&xx, &yy, USER, DEVICE, dd);
1610
	    if ((R_FINITE(xx) && R_FINITE(yy)) &&
1610
	    if ((R_FINITE(xx) && R_FINITE(yy)) &&
1611
		!(R_FINITE(xold) && R_FINITE(yold)))
1611
		!(R_FINITE(xold) && R_FINITE(yold)))
1612
		start = i;
1612
		start = i;
1613
	    else if ((R_FINITE(xold) && R_FINITE(yold)) &&
1613
	    else if ((R_FINITE(xold) && R_FINITE(yold)) &&
1614
		     !(R_FINITE(xx) && R_FINITE(yy))) {
1614
		     !(R_FINITE(xx) && R_FINITE(yy))) {
1615
		if (i-start > 1)
1615
		if (i-start > 1)
1616
		    GPolyline(i-start, x+start, y+start, USER, dd);
1616
		    GPolyline(i-start, x+start, y+start, USER, dd);
1617
	    }
1617
	    }
1618
	    else if ((R_FINITE(xold) && R_FINITE(yold)) && (i == n-1))
1618
	    else if ((R_FINITE(xold) && R_FINITE(yold)) && (i == n-1))
1619
		GPolyline(n-start, x+start, y+start, USER, dd);
1619
		GPolyline(n-start, x+start, y+start, USER, dd);
1620
	    xold = xx;
1620
	    xold = xx;
1621
	    yold = yy;
1621
	    yold = yy;
1622
	}
1622
	}
1623
	break;
1623
	break;
1624
 
1624
 
1625
    case 'b':
1625
    case 'b':
1626
    case 'c': /* broken lines (with points in between if 'b') */
1626
    case 'c': /* broken lines (with points in between if 'b') */
1627
    {
1627
    {
1628
	double d, f;
1628
	double d, f;
1629
	d = GConvertYUnits(0.5, CHARS, INCHES, dd);
1629
	d = GConvertYUnits(0.5, CHARS, INCHES, dd);
1630
	Rf_gpptr(dd)->col = INTEGER(col)[0];
1630
	Rf_gpptr(dd)->col = INTEGER(col)[0];
1631
	xold = NA_REAL;
1631
	xold = NA_REAL;
1632
	yold = NA_REAL;
1632
	yold = NA_REAL;
1633
	for (i = 0; i < n; i++) {
1633
	for (i = 0; i < n; i++) {
1634
	    xx = x[i];
1634
	    xx = x[i];
1635
	    yy = y[i];
1635
	    yy = y[i];
1636
	    GConvert(&xx, &yy, USER, INCHES, dd);
1636
	    GConvert(&xx, &yy, USER, INCHES, dd);
1637
	    if (R_FINITE(xold) && R_FINITE(yold) &&
1637
	    if (R_FINITE(xold) && R_FINITE(yold) &&
1638
		R_FINITE(xx) && R_FINITE(yy)) {
1638
		R_FINITE(xx) && R_FINITE(yy)) {
1639
		if ((f = d/hypot(xx-xold, yy-yold)) < 0.5) {
1639
		if ((f = d/hypot(xx-xold, yy-yold)) < 0.5) {
1640
		    GLine(xold + f * (xx - xold),
1640
		    GLine(xold + f * (xx - xold),
1641
			  yold + f * (yy - yold),
1641
			  yold + f * (yy - yold),
1642
			  xx + f * (xold - xx),
1642
			  xx + f * (xold - xx),
1643
			  yy + f * (yold - yy),
1643
			  yy + f * (yold - yy),
1644
			  INCHES, dd);
1644
			  INCHES, dd);
1645
		}
1645
		}
1646
	    }
1646
	    }
1647
	    xold = xx;
1647
	    xold = xx;
1648
	    yold = yy;
1648
	    yold = yy;
1649
	}
1649
	}
1650
    }
1650
    }
1651
    break;
1651
    break;
1652
 
1652
 
1653
    case 's': /* step function	I */
1653
    case 's': /* step function	I */
1654
    {
1654
    {
1655
	double *xtemp, *ytemp;
1655
	double *xtemp, *ytemp;
1656
	int n0 = 0;
1656
	int n0 = 0;
1657
	if(n <= 1000) {
1657
	if(n <= 1000) {
1658
	    xtemp = (double *) alloca(2*n*sizeof(double));
1658
	    xtemp = (double *) alloca(2*n*sizeof(double));
1659
	    ytemp = (double *) alloca(2*n*sizeof(double));
1659
	    ytemp = (double *) alloca(2*n*sizeof(double));
1660
	    R_CheckStack();
1660
	    R_CheckStack();
1661
	} else {
1661
	} else {
1662
	    vmax = vmaxget();
1662
	    vmax = vmaxget();
1663
	    xtemp = (double *) R_alloc(2*n, sizeof(double));
1663
	    xtemp = (double *) R_alloc(2*n, sizeof(double));
1664
	    ytemp = (double *) R_alloc(2*n, sizeof(double));
1664
	    ytemp = (double *) R_alloc(2*n, sizeof(double));
1665
	}
1665
	}
1666
	Rf_gpptr(dd)->col = INTEGER(col)[0];
1666
	Rf_gpptr(dd)->col = INTEGER(col)[0];
1667
	xold = NA_REAL;
1667
	xold = NA_REAL;
1668
	yold = NA_REAL;
1668
	yold = NA_REAL;
1669
	for (i = 0; i < n; i++) {
1669
	for (i = 0; i < n; i++) {
1670
	    xx = x[i];
1670
	    xx = x[i];
1671
	    yy = y[i];
1671
	    yy = y[i];
1672
	    GConvert(&xx, &yy, USER, DEVICE, dd);
1672
	    GConvert(&xx, &yy, USER, DEVICE, dd);
1673
	    if ((R_FINITE(xx) && R_FINITE(yy)) &&
1673
	    if ((R_FINITE(xx) && R_FINITE(yy)) &&
1674
		(R_FINITE(xold) && R_FINITE(yold))) {
1674
		(R_FINITE(xold) && R_FINITE(yold))) {
1675
		if(n0 == 0) { xtemp[n0] = xold; ytemp[n0++] = yold; }
1675
		if(n0 == 0) { xtemp[n0] = xold; ytemp[n0++] = yold; }
1676
		xtemp[n0] = xx; ytemp[n0++] = yold;/* <-only diff 's' <-> 'S' */
1676
		xtemp[n0] = xx; ytemp[n0++] = yold;/* <-only diff 's' <-> 'S' */
1677
		xtemp[n0] = xx;	ytemp[n0++] = yy;
1677
		xtemp[n0] = xx;	ytemp[n0++] = yy;
1678
	    } else if( (R_FINITE(xold) && R_FINITE(yold)) &&
1678
	    } else if( (R_FINITE(xold) && R_FINITE(yold)) &&
1679
		       !(R_FINITE(xx)  && R_FINITE(yy))	  && n0 > 0) {
1679
		       !(R_FINITE(xx)  && R_FINITE(yy))	  && n0 > 0) {
1680
		GPolyline(n0, xtemp, ytemp, DEVICE, dd);
1680
		GPolyline(n0, xtemp, ytemp, DEVICE, dd);
1681
		n0 = 0;
1681
		n0 = 0;
1682
	    }
1682
	    }
1683
	    xold = xx;
1683
	    xold = xx;
1684
	    yold = yy;
1684
	    yold = yy;
1685
	}
1685
	}
1686
	if(n0 > 0) GPolyline(n0, xtemp, ytemp, DEVICE, dd);
1686
	if(n0 > 0) GPolyline(n0, xtemp, ytemp, DEVICE, dd);
1687
	if(n > 1000) vmaxset(vmax);
1687
	if(n > 1000) vmaxset(vmax);
1688
    }
1688
    }
1689
    break;
1689
    break;
1690
 
1690
 
1691
    case 'S': /* step function	II */
1691
    case 'S': /* step function	II */
1692
    {
1692
    {
1693
	double *xtemp, *ytemp;
1693
	double *xtemp, *ytemp;
1694
	int n0 = 0;
1694
	int n0 = 0;
1695
	if(n < 1000) {
1695
	if(n < 1000) {
1696
	    xtemp = (double *) alloca(2*n*sizeof(double));
1696
	    xtemp = (double *) alloca(2*n*sizeof(double));
1697
	    ytemp = (double *) alloca(2*n*sizeof(double));
1697
	    ytemp = (double *) alloca(2*n*sizeof(double));
1698
	    R_CheckStack();
1698
	    R_CheckStack();
1699
	} else {
1699
	} else {
1700
	    vmax = vmaxget();
1700
	    vmax = vmaxget();
1701
	    xtemp = (double *) R_alloc(2*n, sizeof(double));
1701
	    xtemp = (double *) R_alloc(2*n, sizeof(double));
1702
	    ytemp = (double *) R_alloc(2*n, sizeof(double));
1702
	    ytemp = (double *) R_alloc(2*n, sizeof(double));
1703
	}
1703
	}
1704
	Rf_gpptr(dd)->col = INTEGER(col)[0];
1704
	Rf_gpptr(dd)->col = INTEGER(col)[0];
1705
	xold = NA_REAL;
1705
	xold = NA_REAL;
1706
	yold = NA_REAL;
1706
	yold = NA_REAL;
1707
	for (i = 0; i < n; i++) {
1707
	for (i = 0; i < n; i++) {
1708
	    xx = x[i];
1708
	    xx = x[i];
1709
	    yy = y[i];
1709
	    yy = y[i];
1710
	    GConvert(&xx, &yy, USER, DEVICE, dd);
1710
	    GConvert(&xx, &yy, USER, DEVICE, dd);
1711
	    if ((R_FINITE(xx) && R_FINITE(yy)) &&
1711
	    if ((R_FINITE(xx) && R_FINITE(yy)) &&
1712
		(R_FINITE(xold) && R_FINITE(yold))) {
1712
		(R_FINITE(xold) && R_FINITE(yold))) {
1713
		if(n0 == 0) {xtemp[n0] = xold; ytemp[n0++] = yold;}
1713
		if(n0 == 0) {xtemp[n0] = xold; ytemp[n0++] = yold;}
1714
		xtemp[n0] = xold; ytemp[n0++] = yy;
1714
		xtemp[n0] = xold; ytemp[n0++] = yy;
1715
		xtemp[n0] = xx;	ytemp[n0++] = yy;
1715
		xtemp[n0] = xx;	ytemp[n0++] = yy;
1716
	    } else if( (R_FINITE(xold) && R_FINITE(yold)) &&
1716
	    } else if( (R_FINITE(xold) && R_FINITE(yold)) &&
1717
		       !(R_FINITE(xx)  && R_FINITE(yy))	  && n0 > 0) {
1717
		       !(R_FINITE(xx)  && R_FINITE(yy))	  && n0 > 0) {
1718
		GPolyline(n0, xtemp, ytemp, DEVICE, dd);
1718
		GPolyline(n0, xtemp, ytemp, DEVICE, dd);
1719
		n0 = 0;
1719
		n0 = 0;
1720
	    }
1720
	    }
1721
	    xold = xx;
1721
	    xold = xx;
1722
	    yold = yy;
1722
	    yold = yy;
1723
	}
1723
	}
1724
	if(n0 > 0) GPolyline(n0, xtemp, ytemp, DEVICE, dd);
1724
	if(n0 > 0) GPolyline(n0, xtemp, ytemp, DEVICE, dd);
1725
	if(n > 1000) vmaxset(vmax);
1725
	if(n > 1000) vmaxset(vmax);
1726
    }
1726
    }
1727
    break;
1727
    break;
1728
 
1728
 
1729
    case 'h': /* h[istogram] (bar plot) */
1729
    case 'h': /* h[istogram] (bar plot) */
1730
	if (Rf_gpptr(dd)->ylog)
1730
	if (Rf_gpptr(dd)->ylog)
1731
	    yold = Rf_gpptr(dd)->usr[2];/* DBL_MIN fails.. why ???? */
1731
	    yold = Rf_gpptr(dd)->usr[2];/* DBL_MIN fails.. why ???? */
1732
	else
1732
	else
1733
	    yold = 0.0;
1733
	    yold = 0.0;
1734
	yold = GConvertY(yold, USER, DEVICE, dd);
1734
	yold = GConvertY(yold, USER, DEVICE, dd);
1735
	for (i = 0; i < n; i++) {
1735
	for (i = 0; i < n; i++) {
1736
	    xx = x[i];
1736
	    xx = x[i];
1737
	    yy = y[i];
1737
	    yy = y[i];
1738
	    GConvert(&xx, &yy, USER, DEVICE, dd);
1738
	    GConvert(&xx, &yy, USER, DEVICE, dd);
1739
	    if (R_FINITE(xx) && R_FINITE(yy)
1739
	    if (R_FINITE(xx) && R_FINITE(yy)
1740
		&& !R_TRANSPARENT(thiscol = INTEGER(col)[i % ncol])) {
1740
		&& !R_TRANSPARENT(thiscol = INTEGER(col)[i % ncol])) {
1741
		Rf_gpptr(dd)->col = thiscol;
1741
		Rf_gpptr(dd)->col = thiscol;
1742
		GLine(xx, yold, xx, yy, DEVICE, dd);
1742
		GLine(xx, yold, xx, yy, DEVICE, dd);
1743
	    }
1743
	    }
1744
	}
1744
	}
1745
	break;
1745
	break;
1746
 
1746
 
1747
    case 'p':
1747
    case 'p':
1748
    case 'n': /* nothing here */
1748
    case 'n': /* nothing here */
1749
	break;
1749
	break;
1750
 
1750
 
1751
    default:/* OTHERWISE */
1751
    default:/* OTHERWISE */
1752
	error(_("invalid plot type '%c'"), type);
1752
	error(_("invalid plot type '%c'"), type);
1753
 
1753
 
1754
    } /* End {switch(type)} - for lines */
1754
    } /* End {switch(type)} - for lines */
1755
 
1755
 
1756
    /* Points : */
1756
    /* Points : */
1757
    if (type == 'p' || type == 'b' || type == 'o') {
1757
    if (type == 'p' || type == 'b' || type == 'o') {
1758
	for (i = 0; i < n; i++) {
1758
	for (i = 0; i < n; i++) {
1759
	    xx = x[i];
1759
	    xx = x[i];
1760
	    yy = y[i];
1760
	    yy = y[i];
1761
	    GConvert(&xx, &yy, USER, DEVICE, dd);
1761
	    GConvert(&xx, &yy, USER, DEVICE, dd);
1762
	    if (R_FINITE(xx) && R_FINITE(yy)) {
1762
	    if (R_FINITE(xx) && R_FINITE(yy)) {
1763
		if (R_FINITE( (thiscex = REAL(cex)[i % ncex]) )
1763
		if (R_FINITE( (thiscex = REAL(cex)[i % ncex]) )
1764
		    && (thispch = INTEGER(pch)[i % npch]) != NA_INTEGER
1764
		    && (thispch = INTEGER(pch)[i % npch]) != NA_INTEGER
1765
		    && !R_TRANSPARENT(thiscol = INTEGER(col)[i % ncol]))
1765
		    && !R_TRANSPARENT(thiscol = INTEGER(col)[i % ncol]))
1766
		{
1766
		{
1767
		    Rf_gpptr(dd)->cex = thiscex * Rf_gpptr(dd)->cexbase;
1767
		    Rf_gpptr(dd)->cex = thiscex * Rf_gpptr(dd)->cexbase;
1768
		    Rf_gpptr(dd)->col = thiscol;
1768
		    Rf_gpptr(dd)->col = thiscol;
1769
		    if(nlwd > 1 && R_FINITE((thislwd = REAL(lwd)[i % nlwd])))
1769
		    if(nlwd > 1 && R_FINITE((thislwd = REAL(lwd)[i % nlwd])))
1770
			Rf_gpptr(dd)->lwd = thislwd;
1770
			Rf_gpptr(dd)->lwd = thislwd;
1771
		    Rf_gpptr(dd)->bg = INTEGER(bg)[i % nbg];
1771
		    Rf_gpptr(dd)->bg = INTEGER(bg)[i % nbg];
1772
		    GSymbol(xx, yy, DEVICE, thispch, dd);
1772
		    GSymbol(xx, yy, DEVICE, thispch, dd);
1773
		}
1773
		}
1774
	    }
1774
	    }
1775
	}
1775
	}
1776
    }
1776
    }
1777
    GMode(0, dd);
1777
    GMode(0, dd);
1778
    GRestorePars(dd);
1778
    GRestorePars(dd);
1779
    UNPROTECT(6);
1779
    UNPROTECT(6);
1780
    /* NOTE: only record operation if no "error"  */
1780
    /* NOTE: only record operation if no "error"  */
1781
    if (GRecording(call, dd))
1781
    if (GRecording(call, dd))
1782
	recordGraphicOperation(op, originalArgs, dd);
1782
	recordGraphicOperation(op, originalArgs, dd);
1783
    return R_NilValue;
1783
    return R_NilValue;
1784
}/* do_plot_xy */
1784
}/* do_plot_xy */
1785
 
1785
 
1786
/* Checks for ... , x0, y0, x1, y1 ... */
1786
/* Checks for ... , x0, y0, x1, y1 ... */
1787
 
1787
 
1788
static void xypoints(SEXP call, SEXP args, int *n)
1788
static void xypoints(SEXP call, SEXP args, int *n)
1789
{
1789
{
1790
    int k=0;/* -Wall */
1790
    int k=0;/* -Wall */
1791
 
1791
 
1792
    if (!isNumeric(CAR(args)) || (k = LENGTH(CAR(args))) <= 0)
1792
    if (!isNumeric(CAR(args)) || (k = LENGTH(CAR(args))) <= 0)
1793
	error(_("invalid first argument"));
1793
	error(_("invalid first argument"));
1794
    SETCAR(args, coerceVector(CAR(args), REALSXP));
1794
    SETCAR(args, coerceVector(CAR(args), REALSXP));
1795
    *n = k;
1795
    *n = k;
1796
    args = CDR(args);
1796
    args = CDR(args);
1797
 
1797
 
1798
    if (!isNumeric(CAR(args)) || (k = LENGTH(CAR(args))) <= 0)
1798
    if (!isNumeric(CAR(args)) || (k = LENGTH(CAR(args))) <= 0)
1799
	error(_("invalid second argument"));
1799
	error(_("invalid second argument"));
1800
    SETCAR(args, coerceVector(CAR(args), REALSXP));
1800
    SETCAR(args, coerceVector(CAR(args), REALSXP));
1801
    if (k > *n) *n = k;
1801
    if (k > *n) *n = k;
1802
    args = CDR(args);
1802
    args = CDR(args);
1803
 
1803
 
1804
    if (!isNumeric(CAR(args)) || (k = LENGTH(CAR(args))) <= 0)
1804
    if (!isNumeric(CAR(args)) || (k = LENGTH(CAR(args))) <= 0)
1805
	error(_("invalid third argument"));
1805
	error(_("invalid third argument"));
1806
    SETCAR(args, coerceVector(CAR(args), REALSXP));
1806
    SETCAR(args, coerceVector(CAR(args), REALSXP));
1807
    if (k > *n) *n = k;
1807
    if (k > *n) *n = k;
1808
    args = CDR(args);
1808
    args = CDR(args);
1809
 
1809
 
1810
    if (!isNumeric(CAR(args)) || (k = LENGTH(CAR(args))) <= 0)
1810
    if (!isNumeric(CAR(args)) || (k = LENGTH(CAR(args))) <= 0)
1811
	error(_("invalid fourth argument"));
1811
	error(_("invalid fourth argument"));
1812
    SETCAR(args, coerceVector(CAR(args), REALSXP));
1812
    SETCAR(args, coerceVector(CAR(args), REALSXP));
1813
    if (k > *n) *n = k;
1813
    if (k > *n) *n = k;
1814
    args = CDR(args);
1814
    args = CDR(args);
1815
}
1815
}
1816
 
1816
 
1817
 
1817
 
1818
SEXP attribute_hidden do_segments(SEXP call, SEXP op, SEXP args, SEXP env)
1818
SEXP attribute_hidden do_segments(SEXP call, SEXP op, SEXP args, SEXP env)
1819
{
1819
{
1820
    /* segments(x0, y0, x1, y1, col, lty, lwd, ...) */
1820
    /* segments(x0, y0, x1, y1, col, lty, lwd, ...) */
1821
    SEXP sx0, sx1, sy0, sy1, col, lty, lwd;
1821
    SEXP sx0, sx1, sy0, sy1, col, lty, lwd;
1822
    double *x0, *x1, *y0, *y1;
1822
    double *x0, *x1, *y0, *y1;
1823
    double xx[2], yy[2];
1823
    double xx[2], yy[2];
1824
    int nx0, nx1, ny0, ny1, i, n, ncol, nlty, nlwd;
1824
    int nx0, nx1, ny0, ny1, i, n, ncol, nlty, nlwd;
1825
    SEXP originalArgs = args;
1825
    SEXP originalArgs = args;
1826
    DevDesc *dd = CurrentDevice();
1826
    DevDesc *dd = CurrentDevice();
1827
 
1827
 
1828
    if (length(args) < 4) error(_("too few arguments"));
1828
    if (length(args) < 4) error(_("too few arguments"));
1829
    GCheckState(dd);
1829
    GCheckState(dd);
1830
 
1830
 
1831
    xypoints(call, args, &n);
1831
    xypoints(call, args, &n);
1832
 
1832
 
1833
    sx0 = CAR(args); nx0 = length(sx0); args = CDR(args);
1833
    sx0 = CAR(args); nx0 = length(sx0); args = CDR(args);
1834
    sy0 = CAR(args); ny0 = length(sy0); args = CDR(args);
1834
    sy0 = CAR(args); ny0 = length(sy0); args = CDR(args);
1835
    sx1 = CAR(args); nx1 = length(sx1); args = CDR(args);
1835
    sx1 = CAR(args); nx1 = length(sx1); args = CDR(args);
1836
    sy1 = CAR(args); ny1 = length(sy1); args = CDR(args);
1836
    sy1 = CAR(args); ny1 = length(sy1); args = CDR(args);
1837
 
1837
 
1838
    PROTECT(col = FixupCol(CAR(args), R_TRANWHITE));
1838
    PROTECT(col = FixupCol(CAR(args), R_TRANWHITE));
1839
    ncol = LENGTH(col); args = CDR(args);
1839
    ncol = LENGTH(col); args = CDR(args);
1840
 
1840
 
1841
    PROTECT(lty = FixupLty(CAR(args), Rf_gpptr(dd)->lty));
1841
    PROTECT(lty = FixupLty(CAR(args), Rf_gpptr(dd)->lty));
1842
    nlty = length(lty); args = CDR(args);
1842
    nlty = length(lty); args = CDR(args);
1843
 
1843
 
1844
    PROTECT(lwd = FixupLwd(CAR(args), Rf_gpptr(dd)->lwd));
1844
    PROTECT(lwd = FixupLwd(CAR(args), Rf_gpptr(dd)->lwd));
1845
    nlwd = length(lwd); args = CDR(args);
1845
    nlwd = length(lwd); args = CDR(args);
1846
 
1846
 
1847
    GSavePars(dd);
1847
    GSavePars(dd);
1848
    ProcessInlinePars(args, dd, call);
1848
    ProcessInlinePars(args, dd, call);
1849
 
1849
 
1850
    x0 = REAL(sx0);
1850
    x0 = REAL(sx0);
1851
    y0 = REAL(sy0);
1851
    y0 = REAL(sy0);
1852
    x1 = REAL(sx1);
1852
    x1 = REAL(sx1);
1853
    y1 = REAL(sy1);
1853
    y1 = REAL(sy1);
1854
 
1854
 
1855
    GMode(1, dd);
1855
    GMode(1, dd);
1856
    for (i = 0; i < n; i++) {
1856
    for (i = 0; i < n; i++) {
1857
	xx[0] = x0[i%nx0];
1857
	xx[0] = x0[i%nx0];
1858
	yy[0] = y0[i%ny0];
1858
	yy[0] = y0[i%ny0];
1859
	xx[1] = x1[i%nx1];
1859
	xx[1] = x1[i%nx1];
1860
	yy[1] = y1[i%ny1];
1860
	yy[1] = y1[i%ny1];
1861
	GConvert(xx, yy, USER, DEVICE, dd);
1861
	GConvert(xx, yy, USER, DEVICE, dd);
1862
	GConvert(xx+1, yy+1, USER, DEVICE, dd);
1862
	GConvert(xx+1, yy+1, USER, DEVICE, dd);
1863
	if (R_FINITE(xx[0]) && R_FINITE(yy[0]) &&
1863
	if (R_FINITE(xx[0]) && R_FINITE(yy[0]) &&
1864
	    R_FINITE(xx[1]) && R_FINITE(yy[1]))
1864
	    R_FINITE(xx[1]) && R_FINITE(yy[1]))
1865
	{
1865
	{
1866
	    int thiscol = INTEGER(col)[i % ncol];
1866
	    int thiscol = INTEGER(col)[i % ncol];
1867
	    if(!R_TRANSPARENT(thiscol)) {
1867
	    if(!R_TRANSPARENT(thiscol)) {
1868
		Rf_gpptr(dd)->col = thiscol;
1868
		Rf_gpptr(dd)->col = thiscol;
1869
		Rf_gpptr(dd)->lty = INTEGER(lty)[i % nlty];
1869
		Rf_gpptr(dd)->lty = INTEGER(lty)[i % nlty];
1870
		Rf_gpptr(dd)->lwd = REAL(lwd)[i % nlwd];
1870
		Rf_gpptr(dd)->lwd = REAL(lwd)[i % nlwd];
1871
		GLine(xx[0], yy[0], xx[1], yy[1], DEVICE, dd);
1871
		GLine(xx[0], yy[0], xx[1], yy[1], DEVICE, dd);
1872
	    }
1872
	    }
1873
	}
1873
	}
1874
    }
1874
    }
1875
    GMode(0, dd);
1875
    GMode(0, dd);
1876
    GRestorePars(dd);
1876
    GRestorePars(dd);
1877
 
1877
 
1878
    UNPROTECT(3);
1878
    UNPROTECT(3);
1879
    /* NOTE: only record operation if no "error"  */
1879
    /* NOTE: only record operation if no "error"  */
1880
    if (GRecording(call, dd))
1880
    if (GRecording(call, dd))
1881
	recordGraphicOperation(op, originalArgs, dd);
1881
	recordGraphicOperation(op, originalArgs, dd);
1882
    return R_NilValue;
1882
    return R_NilValue;
1883
}
1883
}
1884
 
1884
 
1885
 
1885
 
1886
SEXP attribute_hidden do_rect(SEXP call, SEXP op, SEXP args, SEXP env)
1886
SEXP attribute_hidden do_rect(SEXP call, SEXP op, SEXP args, SEXP env)
1887
{
1887
{
1888
    /* rect(xl, yb, xr, yt, col, border, lty, ...) */
1888
    /* rect(xl, yb, xr, yt, col, border, lty, ...) */
1889
    SEXP sxl, sxr, syb, syt, col, lty, lwd, border;
1889
    SEXP sxl, sxr, syb, syt, col, lty, lwd, border;
1890
    double *xl, *xr, *yb, *yt, x0, y0, x1, y1;
1890
    double *xl, *xr, *yb, *yt, x0, y0, x1, y1;
1891
    int i, n, nxl, nxr, nyb, nyt, ncol, nlty, nlwd, nborder;
1891
    int i, n, nxl, nxr, nyb, nyt, ncol, nlty, nlwd, nborder;
1892
    SEXP originalArgs = args;
1892
    SEXP originalArgs = args;
1893
    DevDesc *dd = CurrentDevice();
1893
    DevDesc *dd = CurrentDevice();
1894
 
1894
 
1895
    if (length(args) < 4) error(_("too few arguments"));
1895
    if (length(args) < 4) error(_("too few arguments"));
1896
    GCheckState(dd);
1896
    GCheckState(dd);
1897
 
1897
 
1898
    xypoints(call, args, &n);
1898
    xypoints(call, args, &n);
1899
    sxl = CAR(args); nxl = length(sxl); args = CDR(args);/* x_left */
1899
    sxl = CAR(args); nxl = length(sxl); args = CDR(args);/* x_left */
1900
    syb = CAR(args); nyb = length(syb); args = CDR(args);/* y_bottom */
1900
    syb = CAR(args); nyb = length(syb); args = CDR(args);/* y_bottom */
1901
    sxr = CAR(args); nxr = length(sxr); args = CDR(args);/* x_right */
1901
    sxr = CAR(args); nxr = length(sxr); args = CDR(args);/* x_right */
1902
    syt = CAR(args); nyt = length(syt); args = CDR(args);/* y_top */
1902
    syt = CAR(args); nyt = length(syt); args = CDR(args);/* y_top */
1903
 
1903
 
1904
    PROTECT(col = FixupCol(CAR(args), R_TRANWHITE));
1904
    PROTECT(col = FixupCol(CAR(args), R_TRANWHITE));
1905
    ncol = LENGTH(col);
1905
    ncol = LENGTH(col);
1906
    args = CDR(args);
1906
    args = CDR(args);
1907
 
1907
 
1908
    PROTECT(border =  FixupCol(CAR(args), Rf_gpptr(dd)->fg));
1908
    PROTECT(border =  FixupCol(CAR(args), Rf_gpptr(dd)->fg));
1909
    nborder = LENGTH(border);
1909
    nborder = LENGTH(border);
1910
    args = CDR(args);
1910
    args = CDR(args);
1911
 
1911
 
1912
    PROTECT(lty = FixupLty(CAR(args), Rf_gpptr(dd)->lty));
1912
    PROTECT(lty = FixupLty(CAR(args), Rf_gpptr(dd)->lty));
1913
    nlty = length(lty);
1913
    nlty = length(lty);
1914
    args = CDR(args);
1914
    args = CDR(args);
1915
 
1915
 
1916
    PROTECT(lwd = FixupLwd(CAR(args), Rf_gpptr(dd)->lwd));
1916
    PROTECT(lwd = FixupLwd(CAR(args), Rf_gpptr(dd)->lwd));
1917
    nlwd = length(lwd);
1917
    nlwd = length(lwd);
1918
    args = CDR(args);
1918
    args = CDR(args);
1919
 
1919
 
1920
    GSavePars(dd);
1920
    GSavePars(dd);
1921
    ProcessInlinePars(args, dd, call);
1921
    ProcessInlinePars(args, dd, call);
1922
 
1922
 
1923
    xl = REAL(sxl);
1923
    xl = REAL(sxl);
1924
    xr = REAL(sxr);
1924
    xr = REAL(sxr);
1925
    yb = REAL(syb);
1925
    yb = REAL(syb);
1926
    yt = REAL(syt);
1926
    yt = REAL(syt);
1927
 
1927
 
1928
    GMode(1, dd);
1928
    GMode(1, dd);
1929
    for (i = 0; i < n; i++) {
1929
    for (i = 0; i < n; i++) {
1930
	if (nlty && INTEGER(lty)[i % nlty] != NA_INTEGER)
1930
	if (nlty && INTEGER(lty)[i % nlty] != NA_INTEGER)
1931
	    Rf_gpptr(dd)->lty = INTEGER(lty)[i % nlty];
1931
	    Rf_gpptr(dd)->lty = INTEGER(lty)[i % nlty];
1932
	else
1932
	else
1933
	    Rf_gpptr(dd)->lty = Rf_dpptr(dd)->lty;
1933
	    Rf_gpptr(dd)->lty = Rf_dpptr(dd)->lty;
1934
	if (nlwd && REAL(lwd)[i % nlwd] != NA_REAL)
1934
	if (nlwd && REAL(lwd)[i % nlwd] != NA_REAL)
1935
	    Rf_gpptr(dd)->lwd = REAL(lwd)[i % nlwd];
1935
	    Rf_gpptr(dd)->lwd = REAL(lwd)[i % nlwd];
1936
	else
1936
	else
1937
	    Rf_gpptr(dd)->lwd = Rf_dpptr(dd)->lwd;
1937
	    Rf_gpptr(dd)->lwd = Rf_dpptr(dd)->lwd;
1938
	x0 = xl[i%nxl];
1938
	x0 = xl[i%nxl];
1939
	y0 = yb[i%nyb];
1939
	y0 = yb[i%nyb];
1940
	x1 = xr[i%nxr];
1940
	x1 = xr[i%nxr];
1941
	y1 = yt[i%nyt];
1941
	y1 = yt[i%nyt];
1942
	GConvert(&x0, &y0, USER, DEVICE, dd);
1942
	GConvert(&x0, &y0, USER, DEVICE, dd);
1943
	GConvert(&x1, &y1, USER, DEVICE, dd);
1943
	GConvert(&x1, &y1, USER, DEVICE, dd);
1944
	if (R_FINITE(x0) && R_FINITE(y0) && R_FINITE(x1) && R_FINITE(y1))
1944
	if (R_FINITE(x0) && R_FINITE(y0) && R_FINITE(x1) && R_FINITE(y1))
1945
	    GRect(x0, y0, x1, y1, DEVICE, INTEGER(col)[i % ncol],
1945
	    GRect(x0, y0, x1, y1, DEVICE, INTEGER(col)[i % ncol],
1946
		  INTEGER(border)[i % nborder], dd);
1946
		  INTEGER(border)[i % nborder], dd);
1947
    }
1947
    }
1948
    GMode(0, dd);
1948
    GMode(0, dd);
1949
 
1949
 
1950
    GRestorePars(dd);
1950
    GRestorePars(dd);
1951
    UNPROTECT(4);
1951
    UNPROTECT(4);
1952
    /* NOTE: only record operation if no "error"  */
1952
    /* NOTE: only record operation if no "error"  */
1953
    if (GRecording(call, dd))
1953
    if (GRecording(call, dd))
1954
	recordGraphicOperation(op, originalArgs, dd);
1954
	recordGraphicOperation(op, originalArgs, dd);
1955
    return R_NilValue;
1955
    return R_NilValue;
1956
}
1956
}
1957
 
1957
 
1958
 
1958
 
1959
SEXP attribute_hidden do_arrows(SEXP call, SEXP op, SEXP args, SEXP env)
1959
SEXP attribute_hidden do_arrows(SEXP call, SEXP op, SEXP args, SEXP env)
1960
{
1960
{
1961
    /* arrows(x0, y0, x1, y1, length, angle, code, col, lty, lwd, ...) */
1961
    /* arrows(x0, y0, x1, y1, length, angle, code, col, lty, lwd, ...) */
1962
    SEXP sx0, sx1, sy0, sy1, col, lty, lwd;
1962
    SEXP sx0, sx1, sy0, sy1, col, lty, lwd;
1963
    double *x0, *x1, *y0, *y1;
1963
    double *x0, *x1, *y0, *y1;
1964
    double xx0, yy0, xx1, yy1;
1964
    double xx0, yy0, xx1, yy1;
1965
    double hlength, angle;
1965
    double hlength, angle;
1966
    int code;
1966
    int code;
1967
    int nx0, nx1, ny0, ny1, i, n, ncol, nlty, nlwd, thiscol;
1967
    int nx0, nx1, ny0, ny1, i, n, ncol, nlty, nlwd, thiscol;
1968
    SEXP originalArgs = args;
1968
    SEXP originalArgs = args;
1969
    DevDesc *dd = CurrentDevice();
1969
    DevDesc *dd = CurrentDevice();
1970
 
1970
 
1971
    if (length(args) < 4) error(_("too few arguments"));
1971
    if (length(args) < 4) error(_("too few arguments"));
1972
    GCheckState(dd);
1972
    GCheckState(dd);
1973
 
1973
 
1974
    xypoints(call, args, &n);
1974
    xypoints(call, args, &n);
1975
 
1975
 
1976
    sx0 = CAR(args); nx0 = length(sx0); args = CDR(args);
1976
    sx0 = CAR(args); nx0 = length(sx0); args = CDR(args);
1977
    sy0 = CAR(args); ny0 = length(sy0); args = CDR(args);
1977
    sy0 = CAR(args); ny0 = length(sy0); args = CDR(args);
1978
    sx1 = CAR(args); nx1 = length(sx1); args = CDR(args);
1978
    sx1 = CAR(args); nx1 = length(sx1); args = CDR(args);
1979
    sy1 = CAR(args); ny1 = length(sy1); args = CDR(args);
1979
    sy1 = CAR(args); ny1 = length(sy1); args = CDR(args);
1980
 
1980
 
1981
    hlength = asReal(CAR(args));
1981
    hlength = asReal(CAR(args));
1982
    if (!R_FINITE(hlength) || hlength < 0)
1982
    if (!R_FINITE(hlength) || hlength < 0)
1983
	error(_("invalid arrow head length"));
1983
	error(_("invalid arrow head length"));
1984
    args = CDR(args);
1984
    args = CDR(args);
1985
 
1985
 
1986
    angle = asReal(CAR(args));
1986
    angle = asReal(CAR(args));
1987
    if (!R_FINITE(angle))
1987
    if (!R_FINITE(angle))
1988
	error(_("invalid arrow head angle"));
1988
	error(_("invalid arrow head angle"));
1989
    args = CDR(args);
1989
    args = CDR(args);
1990
 
1990
 
1991
    code = asInteger(CAR(args));
1991
    code = asInteger(CAR(args));
1992
    if (code == NA_INTEGER || code < 0 || code > 3)
1992
    if (code == NA_INTEGER || code < 0 || code > 3)
1993
	error(_("invalid arrow head specification"));
1993
	error(_("invalid arrow head specification"));
1994
    args = CDR(args);
1994
    args = CDR(args);
1995
 
1995
 
1996
    PROTECT(col = FixupCol(CAR(args), R_TRANWHITE));
1996
    PROTECT(col = FixupCol(CAR(args), R_TRANWHITE));
1997
    ncol = LENGTH(col);
1997
    ncol = LENGTH(col);
1998
    args = CDR(args);
1998
    args = CDR(args);
1999
 
1999
 
2000
    PROTECT(lty = FixupLty(CAR(args), Rf_gpptr(dd)->lty));
2000
    PROTECT(lty = FixupLty(CAR(args), Rf_gpptr(dd)->lty));
2001
    nlty = length(lty);
2001
    nlty = length(lty);
2002
    args = CDR(args);
2002
    args = CDR(args);
2003
 
2003
 
2004
    PROTECT(lwd = FixupLwd(CAR(args), Rf_gpptr(dd)->lwd));
2004
    PROTECT(lwd = FixupLwd(CAR(args), Rf_gpptr(dd)->lwd));
2005
    nlwd = length(lwd);
2005
    nlwd = length(lwd);
2006
    args = CDR(args);
2006
    args = CDR(args);
2007
 
2007
 
2008
    GSavePars(dd);
2008
    GSavePars(dd);
2009
    ProcessInlinePars(args, dd, call);
2009
    ProcessInlinePars(args, dd, call);
2010
 
2010
 
2011
    x0 = REAL(sx0);
2011
    x0 = REAL(sx0);
2012
    y0 = REAL(sy0);
2012
    y0 = REAL(sy0);
2013
    x1 = REAL(sx1);
2013
    x1 = REAL(sx1);
2014
    y1 = REAL(sy1);
2014
    y1 = REAL(sy1);
2015
 
2015
 
2016
    GMode(1, dd);
2016
    GMode(1, dd);
2017
    for (i = 0; i < n; i++) {
2017
    for (i = 0; i < n; i++) {
2018
	xx0 = x0[i%nx0];
2018
	xx0 = x0[i%nx0];
2019
	yy0 = y0[i%ny0];
2019
	yy0 = y0[i%ny0];
2020
	xx1 = x1[i%nx1];
2020
	xx1 = x1[i%nx1];
2021
	yy1 = y1[i%ny1];
2021
	yy1 = y1[i%ny1];
2022
	GConvert(&xx0, &yy0, USER, DEVICE, dd);
2022
	GConvert(&xx0, &yy0, USER, DEVICE, dd);
2023
	GConvert(&xx1, &yy1, USER, DEVICE, dd);
2023
	GConvert(&xx1, &yy1, USER, DEVICE, dd);
2024
	if (R_FINITE(xx0) && R_FINITE(yy0) && R_FINITE(xx1) && R_FINITE(yy1)
2024
	if (R_FINITE(xx0) && R_FINITE(yy0) && R_FINITE(xx1) && R_FINITE(yy1)
2025
	    && !R_TRANSPARENT(thiscol = INTEGER(col)[i % ncol])) {
2025
	    && !R_TRANSPARENT(thiscol = INTEGER(col)[i % ncol])) {
2026
	    Rf_gpptr(dd)->col = thiscol;
2026
	    Rf_gpptr(dd)->col = thiscol;
2027
	    Rf_gpptr(dd)->lty = INTEGER(lty)[i % nlty];
2027
	    Rf_gpptr(dd)->lty = INTEGER(lty)[i % nlty];
2028
	    Rf_gpptr(dd)->lwd = REAL(lwd)[i % nlwd];
2028
	    Rf_gpptr(dd)->lwd = REAL(lwd)[i % nlwd];
2029
	    GArrow(xx0, yy0, xx1, yy1, DEVICE,
2029
	    GArrow(xx0, yy0, xx1, yy1, DEVICE,
2030
		   hlength, angle, code, dd);
2030
		   hlength, angle, code, dd);
2031
	}
2031
	}
2032
    }
2032
    }
2033
    GMode(0, dd);
2033
    GMode(0, dd);
2034
    GRestorePars(dd);
2034
    GRestorePars(dd);
2035
 
2035
 
2036
    UNPROTECT(3);
2036
    UNPROTECT(3);
2037
    /* NOTE: only record operation if no "error"  */
2037
    /* NOTE: only record operation if no "error"  */
2038
    if (GRecording(call, dd))
2038
    if (GRecording(call, dd))
2039
	recordGraphicOperation(op, originalArgs, dd);
2039
	recordGraphicOperation(op, originalArgs, dd);
2040
    return R_NilValue;
2040
    return R_NilValue;
2041
}
2041
}
2042
 
2042
 
2043
 
2043
 
2044
static void drawPolygon(int n, double *x, double *y,
2044
static void drawPolygon(int n, double *x, double *y,
2045
			int lty, int fill, int border, DevDesc *dd)
2045
			int lty, int fill, int border, DevDesc *dd)
2046
{
2046
{
2047
    if (lty == NA_INTEGER)
2047
    if (lty == NA_INTEGER)
2048
	Rf_gpptr(dd)->lty = Rf_dpptr(dd)->lty;
2048
	Rf_gpptr(dd)->lty = Rf_dpptr(dd)->lty;
2049
    else
2049
    else
2050
	Rf_gpptr(dd)->lty = lty;
2050
	Rf_gpptr(dd)->lty = lty;
2051
    GPolygon(n, x, y, USER, fill, border, dd);
2051
    GPolygon(n, x, y, USER, fill, border, dd);
2052
}
2052
}
2053
 
2053
 
2054
SEXP attribute_hidden do_polygon(SEXP call, SEXP op, SEXP args, SEXP env)
2054
SEXP attribute_hidden do_polygon(SEXP call, SEXP op, SEXP args, SEXP env)
2055
{
2055
{
2056
    /* polygon(x, y, col, border, lty, ...) */
2056
    /* polygon(x, y, col, border, lty, ...) */
2057
    SEXP sx, sy, col, border, lty;
2057
    SEXP sx, sy, col, border, lty;
2058
    int nx;
2058
    int nx;
2059
    int ncol, nborder, nlty, i, start=0;
2059
    int ncol, nborder, nlty, i, start=0;
2060
    int num = 0;
2060
    int num = 0;
2061
    double *x, *y, xx, yy, xold, yold;
2061
    double *x, *y, xx, yy, xold, yold;
2062
 
2062
 
2063
    SEXP originalArgs = args;
2063
    SEXP originalArgs = args;
2064
    DevDesc *dd = CurrentDevice();
2064
    DevDesc *dd = CurrentDevice();
2065
 
2065
 
2066
    GCheckState(dd);
2066
    GCheckState(dd);
2067
 
2067
 
2068
    if (length(args) < 2) error(_("too few arguments"));
2068
    if (length(args) < 2) error(_("too few arguments"));
2069
    /* (x,y) is checked in R via xy.coords() ; no need here : */
2069
    /* (x,y) is checked in R via xy.coords() ; no need here : */
2070
    sx = SETCAR(args, coerceVector(CAR(args), REALSXP));  args = CDR(args);
2070
    sx = SETCAR(args, coerceVector(CAR(args), REALSXP));  args = CDR(args);
2071
    sy = SETCAR(args, coerceVector(CAR(args), REALSXP));  args = CDR(args);
2071
    sy = SETCAR(args, coerceVector(CAR(args), REALSXP));  args = CDR(args);
2072
    nx = LENGTH(sx);
2072
    nx = LENGTH(sx);
2073
 
2073
 
2074
    PROTECT(col = FixupCol(CAR(args), R_TRANWHITE));	args = CDR(args);
2074
    PROTECT(col = FixupCol(CAR(args), R_TRANWHITE));	args = CDR(args);
2075
    ncol = LENGTH(col);
2075
    ncol = LENGTH(col);
2076
 
2076
 
2077
    PROTECT(border = FixupCol(CAR(args), Rf_gpptr(dd)->fg)); args = CDR(args);
2077
    PROTECT(border = FixupCol(CAR(args), Rf_gpptr(dd)->fg)); args = CDR(args);
2078
    nborder = LENGTH(border);
2078
    nborder = LENGTH(border);
2079
 
2079
 
2080
    PROTECT(lty = FixupLty(CAR(args), Rf_gpptr(dd)->lty)); args = CDR(args);
2080
    PROTECT(lty = FixupLty(CAR(args), Rf_gpptr(dd)->lty)); args = CDR(args);
2081
    nlty = length(lty);
2081
    nlty = length(lty);
2082
 
2082
 
2083
    GSavePars(dd);
2083
    GSavePars(dd);
2084
    ProcessInlinePars(args, dd, call);
2084
    ProcessInlinePars(args, dd, call);
2085
 
2085
 
2086
    GMode(1, dd);
2086
    GMode(1, dd);
2087
 
2087
 
2088
    x = REAL(sx);
2088
    x = REAL(sx);
2089
    y = REAL(sy);
2089
    y = REAL(sy);
2090
    xold = NA_REAL;
2090
    xold = NA_REAL;
2091
    yold = NA_REAL;
2091
    yold = NA_REAL;
2092
    for (i = 0; i < nx; i++) {
2092
    for (i = 0; i < nx; i++) {
2093
	xx = x[i];
2093
	xx = x[i];
2094
	yy = y[i];
2094
	yy = y[i];
2095
	GConvert(&xx, &yy, USER, DEVICE, dd);
2095
	GConvert(&xx, &yy, USER, DEVICE, dd);
2096
	if ((R_FINITE(xx) && R_FINITE(yy)) &&
2096
	if ((R_FINITE(xx) && R_FINITE(yy)) &&
2097
	    !(R_FINITE(xold) && R_FINITE(yold)))
2097
	    !(R_FINITE(xold) && R_FINITE(yold)))
2098
	    start = i; /* first point of current segment */
2098
	    start = i; /* first point of current segment */
2099
	else if ((R_FINITE(xold) && R_FINITE(yold)) &&
2099
	else if ((R_FINITE(xold) && R_FINITE(yold)) &&
2100
		 !(R_FINITE(xx) && R_FINITE(yy))) {
2100
		 !(R_FINITE(xx) && R_FINITE(yy))) {
2101
	    if (i-start > 1) {
2101
	    if (i-start > 1) {
2102
		drawPolygon(i-start, x+start, y+start,
2102
		drawPolygon(i-start, x+start, y+start,
2103
			    INTEGER(lty)[num%nlty],
2103
			    INTEGER(lty)[num%nlty],
2104
			    INTEGER(col)[num%ncol],
2104
			    INTEGER(col)[num%ncol],
2105
			    INTEGER(border)[num%nborder], dd);
2105
			    INTEGER(border)[num%nborder], dd);
2106
		num++;
2106
		num++;
2107
	    }
2107
	    }
2108
	}
2108
	}
2109
	else if ((R_FINITE(xold) && R_FINITE(yold)) && (i == nx-1)) { /* last */
2109
	else if ((R_FINITE(xold) && R_FINITE(yold)) && (i == nx-1)) { /* last */
2110
	    drawPolygon(nx-start, x+start, y+start,
2110
	    drawPolygon(nx-start, x+start, y+start,
2111
			INTEGER(lty)[num%nlty],
2111
			INTEGER(lty)[num%nlty],
2112
			INTEGER(col)[num%ncol],
2112
			INTEGER(col)[num%ncol],
2113
			INTEGER(border)[num%nborder], dd);
2113
			INTEGER(border)[num%nborder], dd);
2114
	    num++;
2114
	    num++;
2115
	}
2115
	}
2116
	xold = xx;
2116
	xold = xx;
2117
	yold = yy;
2117
	yold = yy;
2118
    }
2118
    }
2119
 
2119
 
2120
    GMode(0, dd);
2120
    GMode(0, dd);
2121
 
2121
 
2122
    GRestorePars(dd);
2122
    GRestorePars(dd);
2123
    UNPROTECT(3);
2123
    UNPROTECT(3);
2124
    /* NOTE: only record operation if no "error"  */
2124
    /* NOTE: only record operation if no "error"  */
2125
    if (GRecording(call, dd))
2125
    if (GRecording(call, dd))
2126
	recordGraphicOperation(op, originalArgs, dd);
2126
	recordGraphicOperation(op, originalArgs, dd);
2127
    return R_NilValue;
2127
    return R_NilValue;
2128
}
2128
}
2129
 
2129
 
2130
SEXP attribute_hidden do_text(SEXP call, SEXP op, SEXP args, SEXP env)
2130
SEXP attribute_hidden do_text(SEXP call, SEXP op, SEXP args, SEXP env)
2131
{
2131
{
2132
/* text(xy, labels, adj, pos, offset,
2132
/* text(xy, labels, adj, pos, offset,
2133
 *	vfont, cex, col, font, ...)
2133
 *	vfont, cex, col, font, ...)
2134
 */
2134
 */
2135
    SEXP sx, sy, sxy, txt, adj, pos, cex, col, rawcol, font, vfont;
2135
    SEXP sx, sy, sxy, txt, adj, pos, cex, col, rawcol, font, vfont;
2136
    int i, n, npos, ncex, ncol, nfont, ntxt;
2136
    int i, n, npos, ncex, ncol, nfont, ntxt;
2137
    double adjx = 0, adjy = 0, offset = 0.5;
2137
    double adjx = 0, adjy = 0, offset = 0.5;
2138
    double *x, *y;
2138
    double *x, *y;
2139
    double xx, yy;
2139
    double xx, yy;
2140
    Rboolean vectorFonts = FALSE;
2140
    Rboolean vectorFonts = FALSE;
2141
    SEXP string, originalArgs = args;
2141
    SEXP string, originalArgs = args;
2142
    DevDesc *dd = CurrentDevice();
2142
    DevDesc *dd = CurrentDevice();
2143
 
2143
 
2144
    GCheckState(dd);
2144
    GCheckState(dd);
2145
 
2145
 
2146
    if (length(args) < 3) error(_("too few arguments"));
2146
    if (length(args) < 3) error(_("too few arguments"));
2147
 
2147
 
2148
    PLOT_XY_DEALING("text");
2148
    PLOT_XY_DEALING("text");
2149
 
2149
 
2150
    /* labels */
2150
    /* labels */
2151
    txt = CAR(args);
2151
    txt = CAR(args);
2152
    if (isSymbol(txt) || isLanguage(txt))
2152
    if (isSymbol(txt) || isLanguage(txt))
2153
	txt = coerceVector(txt, EXPRSXP);
2153
	txt = coerceVector(txt, EXPRSXP);
2154
    else if (!isExpression(txt))
2154
    else if (!isExpression(txt))
2155
	txt = coerceVector(txt, STRSXP);
2155
	txt = coerceVector(txt, STRSXP);
2156
    PROTECT(txt);
2156
    PROTECT(txt);
2157
    if (length(txt) <= 0)
2157
    if (length(txt) <= 0)
2158
	error(_("zero length 'labels'"));
2158
	error(_("zero length 'labels'"));
2159
    args = CDR(args);
2159
    args = CDR(args);
2160
 
2160
 
2161
    PROTECT(adj = CAR(args));
2161
    PROTECT(adj = CAR(args));
2162
    if (isNull(adj) || (isNumeric(adj) && length(adj) == 0)) {
2162
    if (isNull(adj) || (isNumeric(adj) && length(adj) == 0)) {
2163
	adjx = Rf_gpptr(dd)->adj;
2163
	adjx = Rf_gpptr(dd)->adj;
2164
	adjy = NA_REAL;
2164
	adjy = NA_REAL;
2165
    }
2165
    }
2166
    else if (isReal(adj)) {
2166
    else if (isReal(adj)) {
2167
	if (LENGTH(adj) == 1) {
2167
	if (LENGTH(adj) == 1) {
2168
	    adjx = REAL(adj)[0];
2168
	    adjx = REAL(adj)[0];
2169
	    adjy = NA_REAL;
2169
	    adjy = NA_REAL;
2170
	}
2170
	}
2171
	else {
2171
	else {
2172
	    adjx = REAL(adj)[0];
2172
	    adjx = REAL(adj)[0];
2173
	    adjy = REAL(adj)[1];
2173
	    adjy = REAL(adj)[1];
2174
	}
2174
	}
2175
    }
2175
    }
2176
    else if (isInteger(adj)) {
2176
    else if (isInteger(adj)) {
2177
	if (LENGTH(adj) == 1) {
2177
	if (LENGTH(adj) == 1) {
2178
	    adjx = INTEGER(adj)[0];
2178
	    adjx = INTEGER(adj)[0];
2179
	    adjy = NA_REAL;
2179
	    adjy = NA_REAL;
2180
	}
2180
	}
2181
	else {
2181
	else {
2182
	    adjx = INTEGER(adj)[0];
2182
	    adjx = INTEGER(adj)[0];
2183
	    adjy = INTEGER(adj)[1];
2183
	    adjy = INTEGER(adj)[1];
2184
	}
2184
	}
2185
    }
2185
    }
2186
    else error(_("invalid '%s' value"), "adj");
2186
    else error(_("invalid '%s' value"), "adj");
2187
    args = CDR(args);
2187
    args = CDR(args);
2188
 
2188
 
2189
    PROTECT(pos = coerceVector(CAR(args), INTSXP));
2189
    PROTECT(pos = coerceVector(CAR(args), INTSXP));
2190
    npos = length(pos);
2190
    npos = length(pos);
2191
    for (i = 0; i < npos; i++)
2191
    for (i = 0; i < npos; i++)
2192
	if (INTEGER(pos)[i] < 1 || INTEGER(pos)[i] > 4)
2192
	if (INTEGER(pos)[i] < 1 || INTEGER(pos)[i] > 4)
2193
	    error(_("invalid '%s' value"), "pos");
2193
	    error(_("invalid '%s' value"), "pos");
2194
    args = CDR(args);
2194
    args = CDR(args);
2195
 
2195
 
2196
    offset = GConvertXUnits(asReal(CAR(args)), CHARS, INCHES, dd);
2196
    offset = GConvertXUnits(asReal(CAR(args)), CHARS, INCHES, dd);
2197
    args = CDR(args);
2197
    args = CDR(args);
2198
 
2198
 
2199
    PROTECT(vfont = FixupVFont(CAR(args)));
2199
    PROTECT(vfont = FixupVFont(CAR(args)));
2200
    if (!isNull(vfont))
2200
    if (!isNull(vfont))
2201
	vectorFonts = TRUE;
2201
	vectorFonts = TRUE;
2202
    args = CDR(args);
2202
    args = CDR(args);
2203
 
2203
 
2204
    PROTECT(cex = FixupCex(CAR(args), 1.0));
2204
    PROTECT(cex = FixupCex(CAR(args), 1.0));
2205
    ncex = LENGTH(cex);
2205
    ncex = LENGTH(cex);
2206
    args = CDR(args);
2206
    args = CDR(args);
2207
 
2207
 
2208
    rawcol = CAR(args);
2208
    rawcol = CAR(args);
2209
    PROTECT(col = FixupCol(rawcol, R_TRANWHITE));
2209
    PROTECT(col = FixupCol(rawcol, R_TRANWHITE));
2210
    ncol = LENGTH(col);
2210
    ncol = LENGTH(col);
2211
    args = CDR(args);
2211
    args = CDR(args);
2212
 
2212
 
2213
    PROTECT(font = FixupFont(CAR(args), NA_INTEGER));
2213
    PROTECT(font = FixupFont(CAR(args), NA_INTEGER));
2214
    nfont = LENGTH(font);
2214
    nfont = LENGTH(font);
2215
    args = CDR(args);
2215
    args = CDR(args);
2216
 
2216
 
2217
    x = REAL(sx);
2217
    x = REAL(sx);
2218
    y = REAL(sy);
2218
    y = REAL(sy);
2219
    /* n = LENGTH(sx) = LENGTH(sy) */
2219
    /* n = LENGTH(sx) = LENGTH(sy) */
2220
    ntxt = LENGTH(txt);
2220
    ntxt = LENGTH(txt);
2221
 
2221
 
2222
    GSavePars(dd);
2222
    GSavePars(dd);
2223
    ProcessInlinePars(args, dd, call);
2223
    ProcessInlinePars(args, dd, call);
2224
 
2224
 
2225
    GMode(1, dd);
2225
    GMode(1, dd);
2226
    if (n == 0 && ntxt > 0)
2226
    if (n == 0 && ntxt > 0)
2227
	error(_("no coordinates were supplied"));
2227
	error(_("no coordinates were supplied"));
2228
    for (i = 0; i < imax2(n,ntxt); i++) {
2228
    for (i = 0; i < imax2(n,ntxt); i++) {
2229
	xx = x[i % n];
2229
	xx = x[i % n];
2230
	yy = y[i % n];
2230
	yy = y[i % n];
2231
	GConvert(&xx, &yy, USER, INCHES, dd);
2231
	GConvert(&xx, &yy, USER, INCHES, dd);
2232
	if (R_FINITE(xx) && R_FINITE(yy)) {
2232
	if (R_FINITE(xx) && R_FINITE(yy)) {
2233
	    if (ncol && !isNAcol(rawcol, i, ncol))
2233
	    if (ncol && !isNAcol(rawcol, i, ncol))
2234
		Rf_gpptr(dd)->col = INTEGER(col)[i % ncol];
2234
		Rf_gpptr(dd)->col = INTEGER(col)[i % ncol];
2235
	    else
2235
	    else
2236
		Rf_gpptr(dd)->col = Rf_dpptr(dd)->col;
2236
		Rf_gpptr(dd)->col = Rf_dpptr(dd)->col;
2237
	    if (ncex && R_FINITE(REAL(cex)[i%ncex]))
2237
	    if (ncex && R_FINITE(REAL(cex)[i%ncex]))
2238
		Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase * REAL(cex)[i % ncex];
2238
		Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase * REAL(cex)[i % ncex];
2239
	    else
2239
	    else
2240
		Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase;
2240
		Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase;
2241
	    if (nfont && INTEGER(font)[i % nfont] != NA_INTEGER)
2241
	    if (nfont && INTEGER(font)[i % nfont] != NA_INTEGER)
2242
		Rf_gpptr(dd)->font = INTEGER(font)[i % nfont];
2242
		Rf_gpptr(dd)->font = INTEGER(font)[i % nfont];
2243
	    else
2243
	    else
2244
		Rf_gpptr(dd)->font = Rf_dpptr(dd)->font;
2244
		Rf_gpptr(dd)->font = Rf_dpptr(dd)->font;
2245
	    if (npos > 0) {
2245
	    if (npos > 0) {
2246
		switch(INTEGER(pos)[i % npos]) {
2246
		switch(INTEGER(pos)[i % npos]) {
2247
		case 1:
2247
		case 1:
2248
		    yy = yy - offset;
2248
		    yy = yy - offset;
2249
		    adjx = 0.5;
2249
		    adjx = 0.5;
2250
		    adjy = 1 - (0.5 - Rf_gpptr(dd)->yCharOffset);
2250
		    adjy = 1 - (0.5 - Rf_gpptr(dd)->yCharOffset);
2251
		    break;
2251
		    break;
2252
		case 2:
2252
		case 2:
2253
		    xx = xx - offset;
2253
		    xx = xx - offset;
2254
		    adjx = 1;
2254
		    adjx = 1;
2255
		    adjy = Rf_gpptr(dd)->yCharOffset;
2255
		    adjy = Rf_gpptr(dd)->yCharOffset;
2256
		    break;
2256
		    break;
2257
		case 3:
2257
		case 3:
2258
		    yy = yy + offset;
2258
		    yy = yy + offset;
2259
		    adjx = 0.5;
2259
		    adjx = 0.5;
2260
		    adjy = 0;
2260
		    adjy = 0;
2261
		    break;
2261
		    break;
2262
		case 4:
2262
		case 4:
2263
		    xx = xx + offset;
2263
		    xx = xx + offset;
2264
		    adjx = 0;
2264
		    adjx = 0;
2265
		    adjy = Rf_gpptr(dd)->yCharOffset;
2265
		    adjy = Rf_gpptr(dd)->yCharOffset;
2266
		    break;
2266
		    break;
2267
		}
2267
		}
2268
	    }
2268
	    }
2269
	    if (vectorFonts) {
2269
	    if (vectorFonts) {
2270
		string = STRING_ELT(txt, i % ntxt);
2270
		string = STRING_ELT(txt, i % ntxt);
2271
		if(string != NA_STRING)
2271
		if(string != NA_STRING)
2272
		    GVText(xx, yy, INCHES, translateChar(string),
2272
		    GVText(xx, yy, INCHES, translateChar(string),
2273
			   INTEGER(vfont)[0], INTEGER(vfont)[1],
2273
			   INTEGER(vfont)[0], INTEGER(vfont)[1],
2274
			   adjx, adjy, Rf_gpptr(dd)->srt, dd);
2274
			   adjx, adjy, Rf_gpptr(dd)->srt, dd);
2275
	    } else if (isExpression(txt)) {
2275
	    } else if (isExpression(txt)) {
2276
		GMathText(xx, yy, INCHES, VECTOR_ELT(txt, i % ntxt),
2276
		GMathText(xx, yy, INCHES, VECTOR_ELT(txt, i % ntxt),
2277
			  adjx, adjy, Rf_gpptr(dd)->srt, dd);
2277
			  adjx, adjy, Rf_gpptr(dd)->srt, dd);
2278
	    } else {
2278
	    } else {
2279
		string = STRING_ELT(txt, i % ntxt);
2279
		string = STRING_ELT(txt, i % ntxt);
2280
		if(string != NA_STRING)
2280
		if(string != NA_STRING)
2281
		    GText(xx, yy, INCHES, translateChar(string),
2281
		    GText(xx, yy, INCHES, translateChar(string),
2282
			  adjx, adjy, Rf_gpptr(dd)->srt, dd);
2282
			  adjx, adjy, Rf_gpptr(dd)->srt, dd);
2283
	    }
2283
	    }
2284
	}
2284
	}
2285
    }
2285
    }
2286
    GMode(0, dd);
2286
    GMode(0, dd);
2287
 
2287
 
2288
    GRestorePars(dd);
2288
    GRestorePars(dd);
2289
    UNPROTECT(7);
2289
    UNPROTECT(7);
2290
    /* NOTE: only record operation if no "error"  */
2290
    /* NOTE: only record operation if no "error"  */
2291
    if (GRecording(call, dd))
2291
    if (GRecording(call, dd))
2292
	recordGraphicOperation(op, originalArgs, dd);
2292
	recordGraphicOperation(op, originalArgs, dd);
2293
    return R_NilValue;
2293
    return R_NilValue;
2294
}
2294
}
2295
 
2295
 
2296
static double ComputeAdjValue(double adj, int side, int las)
2296
static double ComputeAdjValue(double adj, int side, int las)
2297
{
2297
{
2298
    if (!R_FINITE(adj)) {
2298
    if (!R_FINITE(adj)) {
2299
	switch(las) {
2299
	switch(las) {
2300
	case 0:/* parallel to axis */
2300
	case 0:/* parallel to axis */
2301
	    adj = 0.5; break;
2301
	    adj = 0.5; break;
2302
	case 1:/* horizontal */
2302
	case 1:/* horizontal */
2303
	    switch(side) {
2303
	    switch(side) {
2304
	    case 1:
2304
	    case 1:
2305
	    case 3: adj = 0.5; break;
2305
	    case 3: adj = 0.5; break;
2306
	    case 2: adj = 1.0; break;
2306
	    case 2: adj = 1.0; break;
2307
	    case 4: adj = 0.0; break;
2307
	    case 4: adj = 0.0; break;
2308
	    }
2308
	    }
2309
	    break;
2309
	    break;
2310
	case 2:/* perpendicular to axis */
2310
	case 2:/* perpendicular to axis */
2311
	    switch(side) {
2311
	    switch(side) {
2312
	    case 1:
2312
	    case 1:
2313
	    case 2: adj = 1.0; break;
2313
	    case 2: adj = 1.0; break;
2314
	    case 3:
2314
	    case 3:
2315
	    case 4: adj = 0.0; break;
2315
	    case 4: adj = 0.0; break;
2316
	    }
2316
	    }
2317
	    break;
2317
	    break;
2318
	case 3:/* vertical */
2318
	case 3:/* vertical */
2319
	    switch(side) {
2319
	    switch(side) {
2320
	    case 1: adj = 1.0; break;
2320
	    case 1: adj = 1.0; break;
2321
	    case 3: adj = 0.0; break;
2321
	    case 3: adj = 0.0; break;
2322
	    case 2:
2322
	    case 2:
2323
	    case 4: adj = 0.5; break;
2323
	    case 4: adj = 0.5; break;
2324
	    }
2324
	    }
2325
	    break;
2325
	    break;
2326
	}
2326
	}
2327
    }
2327
    }
2328
    return adj;
2328
    return adj;
2329
}
2329
}
2330
 
2330
 
2331
static double ComputeAtValueFromAdj(double adj, int side, int outer,
2331
static double ComputeAtValueFromAdj(double adj, int side, int outer,
2332
				    DevDesc *dd)
2332
				    DevDesc *dd)
2333
{
2333
{
2334
    double at = 0;		/* -Wall */
2334
    double at = 0;		/* -Wall */
2335
    switch(side % 2) {
2335
    switch(side % 2) {
2336
    case 0:
2336
    case 0:
2337
	at  = outer ? adj : yNPCtoUsr(adj, dd);
2337
	at  = outer ? adj : yNPCtoUsr(adj, dd);
2338
	break;
2338
	break;
2339
    case 1:
2339
    case 1:
2340
	at = outer ? adj : xNPCtoUsr(adj, dd);
2340
	at = outer ? adj : xNPCtoUsr(adj, dd);
2341
	break;
2341
	break;
2342
    }
2342
    }
2343
    return at;
2343
    return at;
2344
}
2344
}
2345
 
2345
 
2346
static double ComputeAtValue(double at, double adj,
2346
static double ComputeAtValue(double at, double adj,
2347
			     int side, int las, int outer,
2347
			     int side, int las, int outer,
2348
			     DevDesc *dd)
2348
			     DevDesc *dd)
2349
{
2349
{
2350
    if (!R_FINITE(at)) {
2350
    if (!R_FINITE(at)) {
2351
	/* If the text is parallel to the axis, use "adj" for "at"
2351
	/* If the text is parallel to the axis, use "adj" for "at"
2352
	 * Otherwise, centre the text
2352
	 * Otherwise, centre the text
2353
	 */
2353
	 */
2354
	switch(las) {
2354
	switch(las) {
2355
	case 0:/* parallel to axis */
2355
	case 0:/* parallel to axis */
2356
	    at = ComputeAtValueFromAdj(adj, side, outer, dd);
2356
	    at = ComputeAtValueFromAdj(adj, side, outer, dd);
2357
	    break;
2357
	    break;
2358
	case 1:/* horizontal */
2358
	case 1:/* horizontal */
2359
	    switch(side) {
2359
	    switch(side) {
2360
	    case 1:
2360
	    case 1:
2361
	    case 3:
2361
	    case 3:
2362
		at = ComputeAtValueFromAdj(adj, side, outer, dd);
2362
		at = ComputeAtValueFromAdj(adj, side, outer, dd);
2363
		break;
2363
		break;
2364
	    case 2:
2364
	    case 2:
2365
	    case 4:
2365
	    case 4:
2366
		at = outer ? 0.5 : yNPCtoUsr(0.5, dd);
2366
		at = outer ? 0.5 : yNPCtoUsr(0.5, dd);
2367
		break;
2367
		break;
2368
	    }
2368
	    }
2369
	    break;
2369
	    break;
2370
	case 2:/* perpendicular to axis */
2370
	case 2:/* perpendicular to axis */
2371
	    switch(side) {
2371
	    switch(side) {
2372
	    case 1:
2372
	    case 1:
2373
	    case 3:
2373
	    case 3:
2374
		at = outer ? 0.5 : xNPCtoUsr(0.5, dd);
2374
		at = outer ? 0.5 : xNPCtoUsr(0.5, dd);
2375
		break;
2375
		break;
2376
	    case 2:
2376
	    case 2:
2377
	    case 4:
2377
	    case 4:
2378
		at = outer ? 0.5 : yNPCtoUsr(0.5, dd);
2378
		at = outer ? 0.5 : yNPCtoUsr(0.5, dd);
2379
		break;
2379
		break;
2380
	    }
2380
	    }
2381
	    break;
2381
	    break;
2382
	case 3:/* vertical */
2382
	case 3:/* vertical */
2383
	    switch(side) {
2383
	    switch(side) {
2384
	    case 1:
2384
	    case 1:
2385
	    case 3:
2385
	    case 3:
2386
		at = outer ? 0.5 : xNPCtoUsr(0.5, dd);
2386
		at = outer ? 0.5 : xNPCtoUsr(0.5, dd);
2387
		break;
2387
		break;
2388
	    case 2:
2388
	    case 2:
2389
	    case 4:
2389
	    case 4:
2390
		at = ComputeAtValueFromAdj(adj, side, outer, dd);
2390
		at = ComputeAtValueFromAdj(adj, side, outer, dd);
2391
		break;
2391
		break;
2392
	    }
2392
	    }
2393
	    break;
2393
	    break;
2394
	}
2394
	}
2395
    }
2395
    }
2396
    return at;
2396
    return at;
2397
}
2397
}
2398
 
2398
 
2399
/* mtext(text,
2399
/* mtext(text,
2400
	 side = 3,
2400
	 side = 3,
2401
	 line = 0,
2401
	 line = 0,
2402
	 outer = TRUE,
2402
	 outer = TRUE,
2403
	 at = NA,
2403
	 at = NA,
2404
	 adj = NA,
2404
	 adj = NA,
2405
	 padj = NA,
2405
	 padj = NA,
2406
	 cex = NA,
2406
	 cex = NA,
2407
	 col = NA,
2407
	 col = NA,
2408
	 font = NA,
2408
	 font = NA,
2409
	 ...) */
2409
	 ...) */
2410
 
2410
 
2411
SEXP attribute_hidden do_mtext(SEXP call, SEXP op, SEXP args, SEXP env)
2411
SEXP attribute_hidden do_mtext(SEXP call, SEXP op, SEXP args, SEXP env)
2412
{
2412
{
2413
    SEXP text, side, line, outer, at, adj, padj, cex, col, font, string;
2413
    SEXP text, side, line, outer, at, adj, padj, cex, col, font, string;
2414
    SEXP rawcol;
2414
    SEXP rawcol;
2415
    int ntext, nside, nline, nouter, nat, nadj, npadj, ncex, ncol, nfont;
2415
    int ntext, nside, nline, nouter, nat, nadj, npadj, ncex, ncol, nfont;
2416
    Rboolean dirtyplot = FALSE, gpnewsave = FALSE, dpnewsave = FALSE;
2416
    Rboolean dirtyplot = FALSE, gpnewsave = FALSE, dpnewsave = FALSE;
2417
    int i, n, fontsave, colsave;
2417
    int i, n, fontsave, colsave;
2418
    double cexsave;
2418
    double cexsave;
2419
    SEXP originalArgs = args;
2419
    SEXP originalArgs = args;
2420
    DevDesc *dd = CurrentDevice();
2420
    DevDesc *dd = CurrentDevice();
2421
 
2421
 
2422
    GCheckState(dd);
2422
    GCheckState(dd);
2423
 
2423
 
2424
    if (length(args) < 9)
2424
    if (length(args) < 9)
2425
	error(_("too few arguments"));
2425
	error(_("too few arguments"));
2426
 
2426
 
2427
    /* Arg1 : text= */
2427
    /* Arg1 : text= */
2428
    text = CAR(args);
2428
    text = CAR(args);
2429
    if (isSymbol(text) || isLanguage(text))
2429
    if (isSymbol(text) || isLanguage(text))
2430
	text = coerceVector(text, EXPRSXP);
2430
	text = coerceVector(text, EXPRSXP);
2431
    else if (!isExpression(text))
2431
    else if (!isExpression(text))
2432
	text = coerceVector(text, STRSXP);
2432
	text = coerceVector(text, STRSXP);
2433
    PROTECT(text);
2433
    PROTECT(text);
2434
    n = ntext = length(text);
2434
    n = ntext = length(text);
2435
    if (ntext <= 0)
2435
    if (ntext <= 0)
2436
	error(_("zero length 'text' specified"));
2436
	error(_("zero length 'text' specified"));
2437
    args = CDR(args);
2437
    args = CDR(args);
2438
 
2438
 
2439
    /* Arg2 : side= */
2439
    /* Arg2 : side= */
2440
    PROTECT(side = coerceVector(CAR(args), INTSXP));
2440
    PROTECT(side = coerceVector(CAR(args), INTSXP));
2441
    nside = length(side);
2441
    nside = length(side);
2442
    if (nside <= 0) error(_("zero length 'side' specified"));
2442
    if (nside <= 0) error(_("zero length 'side' specified"));
2443
    if (n < nside) n = nside;
2443
    if (n < nside) n = nside;
2444
    args = CDR(args);
2444
    args = CDR(args);
2445
 
2445
 
2446
    /* Arg3 : line= */
2446
    /* Arg3 : line= */
2447
    PROTECT(line = coerceVector(CAR(args), REALSXP));
2447
    PROTECT(line = coerceVector(CAR(args), REALSXP));
2448
    nline = length(line);
2448
    nline = length(line);
2449
    if (nline <= 0) error(_("zero length 'line' specified"));
2449
    if (nline <= 0) error(_("zero length 'line' specified"));
2450
    if (n < nline) n = nline;
2450
    if (n < nline) n = nline;
2451
    args = CDR(args);
2451
    args = CDR(args);
2452
 
2452
 
2453
    /* Arg4 : outer= */
2453
    /* Arg4 : outer= */
2454
    /* outer == NA => outer <- 0 */
2454
    /* outer == NA => outer <- 0 */
2455
    PROTECT(outer = coerceVector(CAR(args), INTSXP));
2455
    PROTECT(outer = coerceVector(CAR(args), INTSXP));
2456
    nouter = length(outer);
2456
    nouter = length(outer);
2457
    if (nouter <= 0) error(_("zero length 'outer' specified"));
2457
    if (nouter <= 0) error(_("zero length 'outer' specified"));
2458
    if (n < nouter) n = nouter;
2458
    if (n < nouter) n = nouter;
2459
    args = CDR(args);
2459
    args = CDR(args);
2460
 
2460
 
2461
    /* Arg5 : at= */
2461
    /* Arg5 : at= */
2462
    PROTECT(at = coerceVector(CAR(args), REALSXP));
2462
    PROTECT(at = coerceVector(CAR(args), REALSXP));
2463
    nat = length(at);
2463
    nat = length(at);
2464
    if (nat <= 0) error(_("zero length 'at' specified"));
2464
    if (nat <= 0) error(_("zero length 'at' specified"));
2465
    if (n < nat) n = nat;
2465
    if (n < nat) n = nat;
2466
    args = CDR(args);
2466
    args = CDR(args);
2467
 
2467
 
2468
    /* Arg6 : adj= */
2468
    /* Arg6 : adj= */
2469
    PROTECT(adj = coerceVector(CAR(args), REALSXP));
2469
    PROTECT(adj = coerceVector(CAR(args), REALSXP));
2470
    nadj = length(adj);
2470
    nadj = length(adj);
2471
    if (nadj <= 0) error(_("zero length 'adj' specified"));
2471
    if (nadj <= 0) error(_("zero length 'adj' specified"));
2472
    if (n < nadj) n = nadj;
2472
    if (n < nadj) n = nadj;
2473
    args = CDR(args);
2473
    args = CDR(args);
2474
 
2474
 
2475
    /* Arg7 : padj= */
2475
    /* Arg7 : padj= */
2476
    PROTECT(padj = coerceVector(CAR(args), REALSXP));
2476
    PROTECT(padj = coerceVector(CAR(args), REALSXP));
2477
    npadj = length(padj);
2477
    npadj = length(padj);
2478
    if (npadj <= 0) error(_("zero length 'padj' specified"));
2478
    if (npadj <= 0) error(_("zero length 'padj' specified"));
2479
    if (n < npadj) n = npadj;
2479
    if (n < npadj) n = npadj;
2480
    args = CDR(args);
2480
    args = CDR(args);
2481
 
2481
 
2482
    /* Arg8 : cex */
2482
    /* Arg8 : cex */
2483
    PROTECT(cex = FixupCex(CAR(args), 1.0));
2483
    PROTECT(cex = FixupCex(CAR(args), 1.0));
2484
    ncex = length(cex);
2484
    ncex = length(cex);
2485
    if (ncex <= 0) error(_("zero length 'cex' specified"));
2485
    if (ncex <= 0) error(_("zero length 'cex' specified"));
2486
    if (n < ncex) n = ncex;
2486
    if (n < ncex) n = ncex;
2487
    args = CDR(args);
2487
    args = CDR(args);
2488
 
2488
 
2489
    /* Arg9 : col */
2489
    /* Arg9 : col */
2490
    rawcol = CAR(args);
2490
    rawcol = CAR(args);
2491
    PROTECT(col = FixupCol(rawcol, R_TRANWHITE));
2491
    PROTECT(col = FixupCol(rawcol, R_TRANWHITE));
2492
    ncol = length(col);
2492
    ncol = length(col);
2493
    if (ncol <= 0) error(_("zero length 'col' specified"));
2493
    if (ncol <= 0) error(_("zero length 'col' specified"));
2494
    if (n < ncol) n = ncol;
2494
    if (n < ncol) n = ncol;
2495
    args = CDR(args);
2495
    args = CDR(args);
2496
 
2496
 
2497
    /* Arg10 : font */
2497
    /* Arg10 : font */
2498
    PROTECT(font = FixupFont(CAR(args), NA_INTEGER));
2498
    PROTECT(font = FixupFont(CAR(args), NA_INTEGER));
2499
    nfont = length(font);
2499
    nfont = length(font);
2500
    if (nfont <= 0) error(_("zero length 'font' specified"));
2500
    if (nfont <= 0) error(_("zero length 'font' specified"));
2501
    if (n < nfont) n = nfont;
2501
    if (n < nfont) n = nfont;
2502
    args = CDR(args);
2502
    args = CDR(args);
2503
 
2503
 
2504
    GSavePars(dd);
2504
    GSavePars(dd);
2505
    ProcessInlinePars(args, dd, call);
2505
    ProcessInlinePars(args, dd, call);
2506
 
2506
 
2507
    /* If we only scribble in the outer margins, */
2507
    /* If we only scribble in the outer margins, */
2508
    /* we don't want to mark the plot as dirty. */
2508
    /* we don't want to mark the plot as dirty. */
2509
 
2509
 
2510
    dirtyplot = FALSE;
2510
    dirtyplot = FALSE;
2511
    gpnewsave = Rf_gpptr(dd)->new;
2511
    gpnewsave = Rf_gpptr(dd)->new;
2512
    dpnewsave = Rf_dpptr(dd)->new;
2512
    dpnewsave = Rf_dpptr(dd)->new;
2513
    cexsave = Rf_gpptr(dd)->cex;
2513
    cexsave = Rf_gpptr(dd)->cex;
2514
    fontsave = Rf_gpptr(dd)->font;
2514
    fontsave = Rf_gpptr(dd)->font;
2515
    colsave = Rf_gpptr(dd)->col;
2515
    colsave = Rf_gpptr(dd)->col;
2516
 
2516
 
2517
    /* override par("xpd") and force clipping to figure region */
2517
    /* override par("xpd") and force clipping to figure region */
2518
    /* NOTE: don't override to _reduce_ clipping region */
2518
    /* NOTE: don't override to _reduce_ clipping region */
2519
    if (Rf_gpptr(dd)->xpd < 1)
2519
    if (Rf_gpptr(dd)->xpd < 1)
2520
	Rf_gpptr(dd)->xpd = 1;
2520
	Rf_gpptr(dd)->xpd = 1;
2521
 
2521
 
2522
    if (outer) {
2522
    if (outer) {
2523
	gpnewsave = Rf_gpptr(dd)->new;
2523
	gpnewsave = Rf_gpptr(dd)->new;
2524
	dpnewsave = Rf_dpptr(dd)->new;
2524
	dpnewsave = Rf_dpptr(dd)->new;
2525
	/* override par("xpd") and force clipping to device region */
2525
	/* override par("xpd") and force clipping to device region */
2526
	Rf_gpptr(dd)->xpd = 2;
2526
	Rf_gpptr(dd)->xpd = 2;
2527
    }
2527
    }
2528
    GMode(1, dd);
2528
    GMode(1, dd);
2529
 
2529
 
2530
    for (i = 0; i < n; i++) {
2530
    for (i = 0; i < n; i++) {
2531
	double atval = REAL(at)[i%nat];
2531
	double atval = REAL(at)[i%nat];
2532
	double adjval = REAL(adj)[i%nadj];
2532
	double adjval = REAL(adj)[i%nadj];
2533
	double padjval = REAL(padj)[i%npadj];
2533
	double padjval = REAL(padj)[i%npadj];
2534
	double cexval = REAL(cex)[i%ncex];
2534
	double cexval = REAL(cex)[i%ncex];
2535
	double lineval = REAL(line)[i%nline];
2535
	double lineval = REAL(line)[i%nline];
2536
	int outerval = INTEGER(outer)[i%nouter];
2536
	int outerval = INTEGER(outer)[i%nouter];
2537
	int sideval = INTEGER(side)[i%nside];
2537
	int sideval = INTEGER(side)[i%nside];
2538
	int fontval = INTEGER(font)[i%nfont];
2538
	int fontval = INTEGER(font)[i%nfont];
2539
	int colval = INTEGER(col)[i%ncol];
2539
	int colval = INTEGER(col)[i%ncol];
2540
 
2540
 
2541
	if (outerval == NA_INTEGER) outerval = 0;
2541
	if (outerval == NA_INTEGER) outerval = 0;
2542
	/* Note : we ignore any shrinking produced */
2542
	/* Note : we ignore any shrinking produced */
2543
	/* by mfrow / mfcol specs here.	 I.e. don't */
2543
	/* by mfrow / mfcol specs here.	 I.e. don't */
2544
	/* Rf_gpptr(dd)->cexbase. */
2544
	/* Rf_gpptr(dd)->cexbase. */
2545
	if (R_FINITE(cexval)) Rf_gpptr(dd)->cex = cexval;
2545
	if (R_FINITE(cexval)) Rf_gpptr(dd)->cex = cexval;
2546
	else cexval = cexsave;
2546
	else cexval = cexsave;
2547
	Rf_gpptr(dd)->font = (fontval == NA_INTEGER) ? fontsave : fontval;
2547
	Rf_gpptr(dd)->font = (fontval == NA_INTEGER) ? fontsave : fontval;
2548
	if (isNAcol(rawcol, i, ncol))
2548
	if (isNAcol(rawcol, i, ncol))
2549
	    Rf_gpptr(dd)->col = colsave;
2549
	    Rf_gpptr(dd)->col = colsave;
2550
	else
2550
	else
2551
	    Rf_gpptr(dd)->col = colval;
2551
	    Rf_gpptr(dd)->col = colval;
2552
	Rf_gpptr(dd)->adj = ComputeAdjValue(adjval, sideval, Rf_gpptr(dd)->las);
2552
	Rf_gpptr(dd)->adj = ComputeAdjValue(adjval, sideval, Rf_gpptr(dd)->las);
2553
	padjval = ComputePAdjValue(padjval, sideval, Rf_gpptr(dd)->las);
2553
	padjval = ComputePAdjValue(padjval, sideval, Rf_gpptr(dd)->las);
2554
	atval = ComputeAtValue(atval, Rf_gpptr(dd)->adj, sideval, Rf_gpptr(dd)->las,
2554
	atval = ComputeAtValue(atval, Rf_gpptr(dd)->adj, sideval, Rf_gpptr(dd)->las,
2555
			       outerval, dd);
2555
			       outerval, dd);
2556
 
2556
 
2557
	if (isExpression(text))
2557
	if (isExpression(text))
2558
	    GMMathText(VECTOR_ELT(text, i%ntext),
2558
	    GMMathText(VECTOR_ELT(text, i%ntext),
2559
		       sideval, lineval, outerval, atval, Rf_gpptr(dd)->las,
2559
		       sideval, lineval, outerval, atval, Rf_gpptr(dd)->las,
2560
		       padjval, dd);
2560
		       padjval, dd);
2561
	else {
2561
	else {
2562
	    string = STRING_ELT(text, i%ntext);
2562
	    string = STRING_ELT(text, i%ntext);
2563
	    if(string != NA_STRING)
2563
	    if(string != NA_STRING)
2564
		GMtext(translateChar(string), sideval, lineval,
2564
		GMtext(translateChar(string), sideval, lineval,
2565
		       outerval, atval, Rf_gpptr(dd)->las, padjval, dd);
2565
		       outerval, atval, Rf_gpptr(dd)->las, padjval, dd);
2566
	}
2566
	}
2567
 
2567
 
2568
	if (outerval == 0) dirtyplot = TRUE;
2568
	if (outerval == 0) dirtyplot = TRUE;
2569
    }
2569
    }
2570
    GMode(0, dd);
2570
    GMode(0, dd);
2571
 
2571
 
2572
    GRestorePars(dd);
2572
    GRestorePars(dd);
2573
    if (!dirtyplot) {
2573
    if (!dirtyplot) {
2574
	Rf_gpptr(dd)->new = gpnewsave;
2574
	Rf_gpptr(dd)->new = gpnewsave;
2575
	Rf_dpptr(dd)->new = dpnewsave;
2575
	Rf_dpptr(dd)->new = dpnewsave;
2576
    }
2576
    }
2577
    UNPROTECT(10);
2577
    UNPROTECT(10);
2578
 
2578
 
2579
    /* NOTE: only record operation if no "error"  */
2579
    /* NOTE: only record operation if no "error"  */
2580
    if (GRecording(call, dd))
2580
    if (GRecording(call, dd))
2581
	recordGraphicOperation(op, originalArgs, dd);
2581
	recordGraphicOperation(op, originalArgs, dd);
2582
    return R_NilValue;
2582
    return R_NilValue;
2583
}/* do_mtext */
2583
}/* do_mtext */
2584
 
2584
 
2585
 
2585
 
2586
SEXP attribute_hidden do_title(SEXP call, SEXP op, SEXP args, SEXP env)
2586
SEXP attribute_hidden do_title(SEXP call, SEXP op, SEXP args, SEXP env)
2587
{
2587
{
2588
/* Annotation for plots :
2588
/* Annotation for plots :
2589
 
2589
 
2590
   title(main, sub, xlab, ylab,
2590
   title(main, sub, xlab, ylab,
2591
	 line, outer,
2591
	 line, outer,
2592
	 ...) */
2592
	 ...) */
2593
 
2593
 
2594
    SEXP Main, xlab, ylab, sub, string;
2594
    SEXP Main, xlab, ylab, sub, string;
2595
    double adj, adjy, cex, offset, line, hpos, vpos, where;
2595
    double adj, adjy, cex, offset, line, hpos, vpos, where;
2596
    int col, font, outer;
2596
    int col, font, outer;
2597
    int i, n;
2597
    int i, n;
2598
    SEXP originalArgs = args;
2598
    SEXP originalArgs = args;
2599
    DevDesc *dd = CurrentDevice();
2599
    DevDesc *dd = CurrentDevice();
2600
 
2600
 
2601
    GCheckState(dd);
2601
    GCheckState(dd);
2602
 
2602
 
2603
    if (length(args) < 6) error(_("too few arguments"));
2603
    if (length(args) < 6) error(_("too few arguments"));
2604
 
2604
 
2605
    Main = sub = xlab = ylab = R_NilValue;
2605
    Main = sub = xlab = ylab = R_NilValue;
2606
 
2606
 
2607
    if (CAR(args) != R_NilValue && LENGTH(CAR(args)) > 0)
2607
    if (CAR(args) != R_NilValue && LENGTH(CAR(args)) > 0)
2608
	Main = CAR(args);
2608
	Main = CAR(args);
2609
    args = CDR(args);
2609
    args = CDR(args);
2610
 
2610
 
2611
    if (CAR(args) != R_NilValue && LENGTH(CAR(args)) > 0)
2611
    if (CAR(args) != R_NilValue && LENGTH(CAR(args)) > 0)
2612
	sub = CAR(args);
2612
	sub = CAR(args);
2613
    args = CDR(args);
2613
    args = CDR(args);
2614
 
2614
 
2615
    if (CAR(args) != R_NilValue && LENGTH(CAR(args)) > 0)
2615
    if (CAR(args) != R_NilValue && LENGTH(CAR(args)) > 0)
2616
	xlab = CAR(args);
2616
	xlab = CAR(args);
2617
    args = CDR(args);
2617
    args = CDR(args);
2618
 
2618
 
2619
    if (CAR(args) != R_NilValue && LENGTH(CAR(args)) > 0)
2619
    if (CAR(args) != R_NilValue && LENGTH(CAR(args)) > 0)
2620
	ylab = CAR(args);
2620
	ylab = CAR(args);
2621
    args = CDR(args);
2621
    args = CDR(args);
2622
 
2622
 
2623
    line = asReal(CAR(args));
2623
    line = asReal(CAR(args));
2624
    args = CDR(args);
2624
    args = CDR(args);
2625
 
2625
 
2626
    outer = asLogical(CAR(args));
2626
    outer = asLogical(CAR(args));
2627
    if (outer == NA_LOGICAL) outer = 0;
2627
    if (outer == NA_LOGICAL) outer = 0;
2628
    args = CDR(args);
2628
    args = CDR(args);
2629
 
2629
 
2630
    GSavePars(dd);
2630
    GSavePars(dd);
2631
    ProcessInlinePars(args, dd, call);
2631
    ProcessInlinePars(args, dd, call);
2632
 
2632
 
2633
    /* override par("xpd") and force clipping to figure region */
2633
    /* override par("xpd") and force clipping to figure region */
2634
    /* NOTE: don't override to _reduce_ clipping region */
2634
    /* NOTE: don't override to _reduce_ clipping region */
2635
    if (Rf_gpptr(dd)->xpd < 1)
2635
    if (Rf_gpptr(dd)->xpd < 1)
2636
	Rf_gpptr(dd)->xpd = 1;
2636
	Rf_gpptr(dd)->xpd = 1;
2637
    if (outer)
2637
    if (outer)
2638
	Rf_gpptr(dd)->xpd = 2;
2638
	Rf_gpptr(dd)->xpd = 2;
2639
    adj = Rf_gpptr(dd)->adj;
2639
    adj = Rf_gpptr(dd)->adj;
2640
 
2640
 
2641
    GMode(1, dd);
2641
    GMode(1, dd);
2642
    if (Main != R_NilValue) {
2642
    if (Main != R_NilValue) {
2643
	cex = Rf_gpptr(dd)->cexmain;
2643
	cex = Rf_gpptr(dd)->cexmain;
2644
	col = Rf_gpptr(dd)->colmain;
2644
	col = Rf_gpptr(dd)->colmain;
2645
	font = Rf_gpptr(dd)->fontmain;
2645
	font = Rf_gpptr(dd)->fontmain;
2646
	/* GetTextArg may coerce, so protect the result */
2646
	/* GetTextArg may coerce, so protect the result */
2647
	GetTextArg(call, Main, &Main, &col, &cex, &font);
2647
	GetTextArg(call, Main, &Main, &col, &cex, &font);
2648
	PROTECT(Main);	
2648
	PROTECT(Main);	
2649
	Rf_gpptr(dd)->col = col;
2649
	Rf_gpptr(dd)->col = col;
2650
	Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase * cex;
2650
	Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase * cex;
2651
	Rf_gpptr(dd)->font = font;
2651
	Rf_gpptr(dd)->font = font;
2652
	if (outer) {
2652
	if (outer) {
2653
	    if (R_FINITE(line)) {
2653
	    if (R_FINITE(line)) {
2654
		vpos = line;
2654
		vpos = line;
2655
		adjy = 0;
2655
		adjy = 0;
2656
	    }
2656
	    }
2657
	    else {
2657
	    else {
2658
		vpos = 0.5 * Rf_gpptr(dd)->oma[2];
2658
		vpos = 0.5 * Rf_gpptr(dd)->oma[2];
2659
		adjy = 0.5;
2659
		adjy = 0.5;
2660
	    }
2660
	    }
2661
	    hpos = adj;
2661
	    hpos = adj;
2662
	    where = OMA3;
2662
	    where = OMA3;
2663
	}
2663
	}
2664
	else {
2664
	else {
2665
	    if (R_FINITE(line)) {
2665
	    if (R_FINITE(line)) {
2666
		vpos = line;
2666
		vpos = line;
2667
		adjy = 0;
2667
		adjy = 0;
2668
	    }
2668
	    }
2669
	    else {
2669
	    else {
2670
		vpos = 0.5 * Rf_gpptr(dd)->mar[2];
2670
		vpos = 0.5 * Rf_gpptr(dd)->mar[2];
2671
		adjy = 0.5;
2671
		adjy = 0.5;
2672
	    }
2672
	    }
2673
	    hpos = GConvertX(adj, NPC, USER, dd);
2673
	    hpos = GConvertX(adj, NPC, USER, dd);
2674
	    where = MAR3;
2674
	    where = MAR3;
2675
	}
2675
	}
2676
	if (isExpression(Main)) {
2676
	if (isExpression(Main)) {
2677
	    GMathText(hpos, vpos, where, VECTOR_ELT(Main, 0),
2677
	    GMathText(hpos, vpos, where, VECTOR_ELT(Main, 0),
2678
		      adj, 0.5, 0.0, dd);
2678
		      adj, 0.5, 0.0, dd);
2679
	}
2679
	}
2680
	else {
2680
	else {
2681
	  n = length(Main);
2681
	  n = length(Main);
2682
	  offset = 0.5 * (n - 1) + vpos;
2682
	  offset = 0.5 * (n - 1) + vpos;
2683
	  for (i = 0; i < n; i++) {
2683
	  for (i = 0; i < n; i++) {
2684
		string = STRING_ELT(Main, i);
2684
		string = STRING_ELT(Main, i);
2685
		if(string != NA_STRING)
2685
		if(string != NA_STRING)
2686
		    GText(hpos, offset - i, where, translateChar(string),
2686
		    GText(hpos, offset - i, where, translateChar(string),
2687
			  adj, adjy, 0.0, dd);
2687
			  adj, adjy, 0.0, dd);
2688
	  }
2688
	  }
2689
	}
2689
	}
2690
	UNPROTECT(1);
2690
	UNPROTECT(1);
2691
    }
2691
    }
2692
    if (sub != R_NilValue) {
2692
    if (sub != R_NilValue) {
2693
	cex = Rf_gpptr(dd)->cexsub;
2693
	cex = Rf_gpptr(dd)->cexsub;
2694
	col = Rf_gpptr(dd)->colsub;
2694
	col = Rf_gpptr(dd)->colsub;
2695
	font = Rf_gpptr(dd)->fontsub;
2695
	font = Rf_gpptr(dd)->fontsub;
2696
	/* GetTextArg may coerce, so protect the result */
2696
	/* GetTextArg may coerce, so protect the result */
2697
	GetTextArg(call, sub, &sub, &col, &cex, &font);
2697
	GetTextArg(call, sub, &sub, &col, &cex, &font);
2698
	PROTECT(sub);
2698
	PROTECT(sub);
2699
	Rf_gpptr(dd)->col = col;
2699
	Rf_gpptr(dd)->col = col;
2700
	Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase * cex;
2700
	Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase * cex;
2701
	Rf_gpptr(dd)->font = font;
2701
	Rf_gpptr(dd)->font = font;
2702
	if (R_FINITE(line))
2702
	if (R_FINITE(line))
2703
	    vpos = line;
2703
	    vpos = line;
2704
	else
2704
	else
2705
	    vpos = Rf_gpptr(dd)->mgp[0] + 1;
2705
	    vpos = Rf_gpptr(dd)->mgp[0] + 1;
2706
	if (outer) {
2706
	if (outer) {
2707
	    hpos = adj;
2707
	    hpos = adj;
2708
	    where = 1;
2708
	    where = 1;
2709
	}
2709
	}
2710
	else {
2710
	else {
2711
	    hpos = GConvertX(adj, NPC, USER, dd);
2711
	    hpos = GConvertX(adj, NPC, USER, dd);
2712
	    where = 0;
2712
	    where = 0;
2713
	}
2713
	}
2714
	if (isExpression(sub))
2714
	if (isExpression(sub))
2715
	    GMMathText(VECTOR_ELT(sub, 0), 1, vpos, where,
2715
	    GMMathText(VECTOR_ELT(sub, 0), 1, vpos, where,
2716
		       hpos, 0, 0.0, dd);
2716
		       hpos, 0, 0.0, dd);
2717
	else {
2717
	else {
2718
	    n = length(sub);
2718
	    n = length(sub);
2719
	    for (i = 0; i < n; i++) {
2719
	    for (i = 0; i < n; i++) {
2720
		string = STRING_ELT(sub, i);
2720
		string = STRING_ELT(sub, i);
2721
		if(string != NA_STRING)
2721
		if(string != NA_STRING)
2722
		    GMtext(translateChar(string), 1, vpos, where, 
2722
		    GMtext(translateChar(string), 1, vpos, where, 
2723
			   hpos, 0, 0.0, dd);
2723
			   hpos, 0, 0.0, dd);
2724
	    }
2724
	    }
2725
	}
2725
	}
2726
	UNPROTECT(1);
2726
	UNPROTECT(1);
2727
    }
2727
    }
2728
    if (xlab != R_NilValue) {
2728
    if (xlab != R_NilValue) {
2729
	cex = Rf_gpptr(dd)->cexlab;
2729
	cex = Rf_gpptr(dd)->cexlab;
2730
	col = Rf_gpptr(dd)->collab;
2730
	col = Rf_gpptr(dd)->collab;
2731
	font = Rf_gpptr(dd)->fontlab;
2731
	font = Rf_gpptr(dd)->fontlab;
2732
	/* GetTextArg may coerce, so protect the result */
2732
	/* GetTextArg may coerce, so protect the result */
2733
	GetTextArg(call, xlab, &xlab, &col, &cex, &font);
2733
	GetTextArg(call, xlab, &xlab, &col, &cex, &font);
2734
	PROTECT(xlab);
2734
	PROTECT(xlab);
2735
	Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase * cex;
2735
	Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase * cex;
2736
	Rf_gpptr(dd)->col = col;
2736
	Rf_gpptr(dd)->col = col;
2737
	Rf_gpptr(dd)->font = font;
2737
	Rf_gpptr(dd)->font = font;
2738
	if (R_FINITE(line))
2738
	if (R_FINITE(line))
2739
	    vpos = line;
2739
	    vpos = line;
2740
	else
2740
	else
2741
	    vpos = Rf_gpptr(dd)->mgp[0];
2741
	    vpos = Rf_gpptr(dd)->mgp[0];
2742
	if (outer) {
2742
	if (outer) {
2743
	    hpos = adj;
2743
	    hpos = adj;
2744
	    where = 1;
2744
	    where = 1;
2745
	}
2745
	}
2746
	else {
2746
	else {
2747
	    hpos = GConvertX(adj, NPC, USER, dd);
2747
	    hpos = GConvertX(adj, NPC, USER, dd);
2748
	    where = 0;
2748
	    where = 0;
2749
	}
2749
	}
2750
	if (isExpression(xlab))
2750
	if (isExpression(xlab))
2751
	    GMMathText(VECTOR_ELT(xlab, 0), 1, vpos, where,
2751
	    GMMathText(VECTOR_ELT(xlab, 0), 1, vpos, where,
2752
		       hpos, 0, 0.0, dd);
2752
		       hpos, 0, 0.0, dd);
2753
	else {
2753
	else {
2754
	    n = length(xlab);
2754
	    n = length(xlab);
2755
	    for (i = 0; i < n; i++) {
2755
	    for (i = 0; i < n; i++) {
2756
		string = STRING_ELT(xlab, i);
2756
		string = STRING_ELT(xlab, i);
2757
		if(string != NA_STRING)
2757
		if(string != NA_STRING)
2758
		    GMtext(translateChar(string), 1, vpos + i, 
2758
		    GMtext(translateChar(string), 1, vpos + i, 
2759
			   where, hpos, 0, 0.0, dd);
2759
			   where, hpos, 0, 0.0, dd);
2760
	    }
2760
	    }
2761
	}
2761
	}
2762
	UNPROTECT(1);
2762
	UNPROTECT(1);
2763
    }
2763
    }
2764
    if (ylab != R_NilValue) {
2764
    if (ylab != R_NilValue) {
2765
	cex = Rf_gpptr(dd)->cexlab;
2765
	cex = Rf_gpptr(dd)->cexlab;
2766
	col = Rf_gpptr(dd)->collab;
2766
	col = Rf_gpptr(dd)->collab;
2767
	font = Rf_gpptr(dd)->fontlab;
2767
	font = Rf_gpptr(dd)->fontlab;
2768
	/* GetTextArg may coerce, so protect the result */
2768
	/* GetTextArg may coerce, so protect the result */
2769
	GetTextArg(call, ylab, &ylab, &col, &cex, &font);
2769
	GetTextArg(call, ylab, &ylab, &col, &cex, &font);
2770
	PROTECT(ylab);
2770
	PROTECT(ylab);
2771
	Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase * cex;
2771
	Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase * cex;
2772
	Rf_gpptr(dd)->col = col;
2772
	Rf_gpptr(dd)->col = col;
2773
	Rf_gpptr(dd)->font = font;
2773
	Rf_gpptr(dd)->font = font;
2774
	if (R_FINITE(line))
2774
	if (R_FINITE(line))
2775
	    vpos = line;
2775
	    vpos = line;
2776
	else
2776
	else
2777
	    vpos = Rf_gpptr(dd)->mgp[0];
2777
	    vpos = Rf_gpptr(dd)->mgp[0];
2778
	if (outer) {
2778
	if (outer) {
2779
	    hpos = adj;
2779
	    hpos = adj;
2780
	    where = 1;
2780
	    where = 1;
2781
	}
2781
	}
2782
	else {
2782
	else {
2783
	    hpos = GConvertY(adj, NPC, USER, dd);
2783
	    hpos = GConvertY(adj, NPC, USER, dd);
2784
	    where = 0;
2784
	    where = 0;
2785
	}
2785
	}
2786
	if (isExpression(ylab))
2786
	if (isExpression(ylab))
2787
	    GMMathText(VECTOR_ELT(ylab, 0), 2, vpos, where,
2787
	    GMMathText(VECTOR_ELT(ylab, 0), 2, vpos, where,
2788
		       hpos, 0, 0.0, dd);
2788
		       hpos, 0, 0.0, dd);
2789
	else {
2789
	else {
2790
	    n = length(ylab);
2790
	    n = length(ylab);
2791
	    for (i = 0; i < n; i++) {
2791
	    for (i = 0; i < n; i++) {
2792
		string = STRING_ELT(ylab, i);
2792
		string = STRING_ELT(ylab, i);
2793
		if(string != NA_STRING)
2793
		if(string != NA_STRING)
2794
		    GMtext(translateChar(string), 2, vpos - i, 
2794
		    GMtext(translateChar(string), 2, vpos - i, 
2795
			   where, hpos, 0, 0.0, dd);
2795
			   where, hpos, 0, 0.0, dd);
2796
	    }
2796
	    }
2797
	}
2797
	}
2798
	UNPROTECT(1);
2798
	UNPROTECT(1);
2799
    }
2799
    }
2800
    GMode(0, dd);
2800
    GMode(0, dd);
2801
    GRestorePars(dd);
2801
    GRestorePars(dd);
2802
    /* NOTE: only record operation if no "error"  */
2802
    /* NOTE: only record operation if no "error"  */
2803
    if (GRecording(call, dd))
2803
    if (GRecording(call, dd))
2804
	recordGraphicOperation(op, originalArgs, dd);
2804
	recordGraphicOperation(op, originalArgs, dd);
2805
    return R_NilValue;
2805
    return R_NilValue;
2806
}/* do_title */
2806
}/* do_title */
2807
 
2807
 
2808
 
2808
 
2809
/*  abline(a, b, h, v, col, lty, lwd, ...)
2809
/*  abline(a, b, h, v, col, lty, lwd, ...)
2810
    draw lines in intercept/slope form.	 */
2810
    draw lines in intercept/slope form.	 */
2811
 
2811
 
2812
static void getxlimits(double *x, DevDesc *dd) {
2812
static void getxlimits(double *x, DevDesc *dd) {
2813
    /*
2813
    /*
2814
     * xpd = 0 means clip to current plot region
2814
     * xpd = 0 means clip to current plot region
2815
     * xpd = 1 means clip to current figure region
2815
     * xpd = 1 means clip to current figure region
2816
     * xpd = 2 means clip to device region
2816
     * xpd = 2 means clip to device region
2817
     */
2817
     */
2818
    switch (Rf_gpptr(dd)->xpd) {
2818
    switch (Rf_gpptr(dd)->xpd) {
2819
    case 0:
2819
    case 0:
2820
	x[0] = Rf_gpptr(dd)->usr[0];
2820
	x[0] = Rf_gpptr(dd)->usr[0];
2821
	x[1] = Rf_gpptr(dd)->usr[1];
2821
	x[1] = Rf_gpptr(dd)->usr[1];
2822
	break;
2822
	break;
2823
    case 1:
2823
    case 1:
2824
	x[0] = GConvertX(0, NFC, USER, dd);
2824
	x[0] = GConvertX(0, NFC, USER, dd);
2825
	x[1] = GConvertX(1, NFC, USER, dd);
2825
	x[1] = GConvertX(1, NFC, USER, dd);
2826
	break;
2826
	break;
2827
    case 2:
2827
    case 2:
2828
	x[0] = GConvertX(0, NDC, USER, dd);
2828
	x[0] = GConvertX(0, NDC, USER, dd);
2829
	x[1] = GConvertX(1, NDC, USER, dd);
2829
	x[1] = GConvertX(1, NDC, USER, dd);
2830
	break;
2830
	break;
2831
    }
2831
    }
2832
}
2832
}
2833
 
2833
 
2834
static void getylimits(double *y, DevDesc *dd) {
2834
static void getylimits(double *y, DevDesc *dd) {
2835
    switch (Rf_gpptr(dd)->xpd) {
2835
    switch (Rf_gpptr(dd)->xpd) {
2836
    case 0:
2836
    case 0:
2837
	y[0] = Rf_gpptr(dd)->usr[2];
2837
	y[0] = Rf_gpptr(dd)->usr[2];
2838
	y[1] = Rf_gpptr(dd)->usr[3];
2838
	y[1] = Rf_gpptr(dd)->usr[3];
2839
	break;
2839
	break;
2840
    case 1:
2840
    case 1:
2841
	y[0] = GConvertY(0, NFC, USER, dd);
2841
	y[0] = GConvertY(0, NFC, USER, dd);
2842
	y[1] = GConvertY(1, NFC, USER, dd);
2842
	y[1] = GConvertY(1, NFC, USER, dd);
2843
	break;
2843
	break;
2844
    case 2:
2844
    case 2:
2845
	y[0] = GConvertY(0, NDC, USER, dd);
2845
	y[0] = GConvertY(0, NDC, USER, dd);
2846
	y[1] = GConvertY(1, NDC, USER, dd);
2846
	y[1] = GConvertY(1, NDC, USER, dd);
2847
	break;
2847
	break;
2848
    }
2848
    }
2849
}
2849
}
2850
 
2850
 
2851
SEXP attribute_hidden do_abline(SEXP call, SEXP op, SEXP args, SEXP env)
2851
SEXP attribute_hidden do_abline(SEXP call, SEXP op, SEXP args, SEXP env)
2852
{
2852
{
2853
    SEXP a, b, h, v, untf, col, lty, lwd;
2853
    SEXP a, b, h, v, untf, col, lty, lwd;
2854
    int i, ncol, nlines, nlty, nlwd, lstart, lstop;
2854
    int i, ncol, nlines, nlty, nlwd, lstart, lstop;
2855
    double aa, bb, x[2], y[2]={0.,0.} /* -Wall */;
2855
    double aa, bb, x[2], y[2]={0.,0.} /* -Wall */;
2856
    SEXP originalArgs = args;
2856
    SEXP originalArgs = args;
2857
    DevDesc *dd = CurrentDevice();
2857
    DevDesc *dd = CurrentDevice();
2858
 
2858
 
2859
    GCheckState(dd);
2859
    GCheckState(dd);
2860
 
2860
 
2861
    if (length(args) < 5) error(_("too few arguments"));
2861
    if (length(args) < 5) error(_("too few arguments"));
2862
 
2862
 
2863
    if ((a = CAR(args)) != R_NilValue)
2863
    if ((a = CAR(args)) != R_NilValue)
2864
	SETCAR(args, a = coerceVector(a, REALSXP));
2864
	SETCAR(args, a = coerceVector(a, REALSXP));
2865
    args = CDR(args);
2865
    args = CDR(args);
2866
 
2866
 
2867
    if ((b = CAR(args)) != R_NilValue)
2867
    if ((b = CAR(args)) != R_NilValue)
2868
	SETCAR(args, b = coerceVector(b, REALSXP));
2868
	SETCAR(args, b = coerceVector(b, REALSXP));
2869
    args = CDR(args);
2869
    args = CDR(args);
2870
 
2870
 
2871
    if ((h = CAR(args)) != R_NilValue)
2871
    if ((h = CAR(args)) != R_NilValue)
2872
	SETCAR(args, h = coerceVector(h, REALSXP));
2872
	SETCAR(args, h = coerceVector(h, REALSXP));
2873
    args = CDR(args);
2873
    args = CDR(args);
2874
 
2874
 
2875
    if ((v = CAR(args)) != R_NilValue)
2875
    if ((v = CAR(args)) != R_NilValue)
2876
	SETCAR(args, v = coerceVector(v, REALSXP));
2876
	SETCAR(args, v = coerceVector(v, REALSXP));
2877
    args = CDR(args);
2877
    args = CDR(args);
2878
 
2878
 
2879
    if ((untf = CAR(args)) != R_NilValue)
2879
    if ((untf = CAR(args)) != R_NilValue)
2880
	SETCAR(args, untf = coerceVector(untf, LGLSXP));
2880
	SETCAR(args, untf = coerceVector(untf, LGLSXP));
2881
    args = CDR(args);
2881
    args = CDR(args);
2882
 
2882
 
2883
 
2883
 
2884
    PROTECT(col = FixupCol(CAR(args), R_TRANWHITE));	args = CDR(args);
2884
    PROTECT(col = FixupCol(CAR(args), R_TRANWHITE));	args = CDR(args);
2885
    ncol = LENGTH(col);
2885
    ncol = LENGTH(col);
2886
 
2886
 
2887
    PROTECT(lty = FixupLty(CAR(args), Rf_gpptr(dd)->lty)); args = CDR(args);
2887
    PROTECT(lty = FixupLty(CAR(args), Rf_gpptr(dd)->lty)); args = CDR(args);
2888
    nlty = length(lty);
2888
    nlty = length(lty);
2889
 
2889
 
2890
    PROTECT(lwd = FixupLwd(CAR(args), Rf_gpptr(dd)->lwd)); args = CDR(args);
2890
    PROTECT(lwd = FixupLwd(CAR(args), Rf_gpptr(dd)->lwd)); args = CDR(args);
2891
    nlwd = length(lwd);
2891
    nlwd = length(lwd);
2892
 
2892
 
2893
    GSavePars(dd);
2893
    GSavePars(dd);
2894
 
2894
 
2895
    ProcessInlinePars(args, dd, call);
2895
    ProcessInlinePars(args, dd, call);
2896
 
2896
 
2897
    nlines = 0;
2897
    nlines = 0;
2898
 
2898
 
2899
    if (a != R_NilValue) {
2899
    if (a != R_NilValue) {
2900
	if (b == R_NilValue) {
2900
	if (b == R_NilValue) {
2901
	    if (LENGTH(a) != 2)
2901
	    if (LENGTH(a) != 2)
2902
		error(_("invalid a=, b= specification"));
2902
		error(_("invalid a=, b= specification"));
2903
	    aa = REAL(a)[0];
2903
	    aa = REAL(a)[0];
2904
	    bb = REAL(a)[1];
2904
	    bb = REAL(a)[1];
2905
	}
2905
	}
2906
	else {
2906
	else {
2907
	    aa = asReal(a);
2907
	    aa = asReal(a);
2908
	    bb = asReal(b);
2908
	    bb = asReal(b);
2909
	}
2909
	}
2910
	if (!R_FINITE(aa) || !R_FINITE(bb))
2910
	if (!R_FINITE(aa) || !R_FINITE(bb))
2911
	    error(_("'a' and 'b' must be finite"));
2911
	    error(_("'a' and 'b' must be finite"));
2912
	Rf_gpptr(dd)->col = INTEGER(col)[0];
2912
	Rf_gpptr(dd)->col = INTEGER(col)[0];
2913
	Rf_gpptr(dd)->lwd = REAL(lwd)[0];
2913
	Rf_gpptr(dd)->lwd = REAL(lwd)[0];
2914
	if (nlty && INTEGER(lty)[0] != NA_INTEGER)
2914
	if (nlty && INTEGER(lty)[0] != NA_INTEGER)
2915
	    Rf_gpptr(dd)->lty = INTEGER(lty)[0];
2915
	    Rf_gpptr(dd)->lty = INTEGER(lty)[0];
2916
	else
2916
	else
2917
	    Rf_gpptr(dd)->lty = Rf_dpptr(dd)->lty;
2917
	    Rf_gpptr(dd)->lty = Rf_dpptr(dd)->lty;
2918
	GMode(1, dd);
2918
	GMode(1, dd);
2919
	/* FIXME?
2919
	/* FIXME?
2920
	 * Seems like the logic here is just draw from xmin to xmax
2920
	 * Seems like the logic here is just draw from xmin to xmax
2921
	 * and you're guaranteed to draw at least from ymin to ymax
2921
	 * and you're guaranteed to draw at least from ymin to ymax
2922
	 * This MAY cause a problem at some stage when the line being
2922
	 * This MAY cause a problem at some stage when the line being
2923
	 * drawn is VERY steep -- and the problem is worse now that
2923
	 * drawn is VERY steep -- and the problem is worse now that
2924
	 * abline will potentially draw to the extents of the device
2924
	 * abline will potentially draw to the extents of the device
2925
	 * (when xpd=NA).  NOTE that R's internal clipping protects the
2925
	 * (when xpd=NA).  NOTE that R's internal clipping protects the
2926
	 * device drivers from stupidly large numbers, BUT there is
2926
	 * device drivers from stupidly large numbers, BUT there is
2927
	 * still a risk that we could produce a number which is too
2927
	 * still a risk that we could produce a number which is too
2928
	 * big for the computer's brain.
2928
	 * big for the computer's brain.
2929
	 * Paul.
2929
	 * Paul.
2930
	 *
2930
	 *
2931
	 * The problem is worse -- you could get NaN, which at least the
2931
	 * The problem is worse -- you could get NaN, which at least the
2932
	 * X11 device coerces to -2^31 <TSL>
2932
	 * X11 device coerces to -2^31 <TSL>
2933
	 */
2933
	 */
2934
	getxlimits(x, dd);
2934
	getxlimits(x, dd);
2935
	if (R_FINITE(Rf_gpptr(dd)->lwd)) {
2935
	if (R_FINITE(Rf_gpptr(dd)->lwd)) {
2936
	    if (LOGICAL(untf)[0] == 1 && (Rf_gpptr(dd)->xlog || Rf_gpptr(dd)->ylog)) {
2936
	    if (LOGICAL(untf)[0] == 1 && (Rf_gpptr(dd)->xlog || Rf_gpptr(dd)->ylog)) {
2937
		double xx[101], yy[101];
2937
		double xx[101], yy[101];
2938
		double xstep = (x[1] - x[0])/100;
2938
		double xstep = (x[1] - x[0])/100;
2939
		for (i = 0; i < 100; i++) {
2939
		for (i = 0; i < 100; i++) {
2940
		    xx[i] = x[0] + i*xstep;
2940
		    xx[i] = x[0] + i*xstep;
2941
		    yy[i] = aa + xx[i] * bb;
2941
		    yy[i] = aa + xx[i] * bb;
2942
		}
2942
		}
2943
		xx[100] = x[1];
2943
		xx[100] = x[1];
2944
		yy[100] = aa + x[1] * bb;
2944
		yy[100] = aa + x[1] * bb;
2945
 
2945
 
2946
		/* now get rid of -ve values */
2946
		/* now get rid of -ve values */
2947
		lstart=0;lstop=100;
2947
		lstart=0;lstop=100;
2948
		if (Rf_gpptr(dd)->xlog){
2948
		if (Rf_gpptr(dd)->xlog){
2949
			for(;xx[lstart]<=0 && lstart<101;lstart++);
2949
			for(;xx[lstart]<=0 && lstart<101;lstart++);
2950
			for(;xx[lstop]<=0 && lstop>0;lstop--);
2950
			for(;xx[lstop]<=0 && lstop>0;lstop--);
2951
		}
2951
		}
2952
		if (Rf_gpptr(dd)->ylog){
2952
		if (Rf_gpptr(dd)->ylog){
2953
			for(;yy[lstart]<=0 && lstart<101;lstart++);
2953
			for(;yy[lstart]<=0 && lstart<101;lstart++);
2954
			for(;yy[lstop]<=0 && lstop>0;lstop--);
2954
			for(;yy[lstop]<=0 && lstop>0;lstop--);
2955
		}
2955
		}
2956
 
2956
 
2957
 
2957
 
2958
		GPolyline(lstop-lstart+1, xx+lstart, yy+lstart, USER, dd);
2958
		GPolyline(lstop-lstart+1, xx+lstart, yy+lstart, USER, dd);
2959
	    }
2959
	    }
2960
	    else {
2960
	    else {
2961
		double x0, x1;
2961
		double x0, x1;
2962
 
2962
 
2963
		x0 = ( Rf_gpptr(dd)->xlog ) ?	log10(x[0]) : x[0];
2963
		x0 = ( Rf_gpptr(dd)->xlog ) ?	log10(x[0]) : x[0];
2964
		x1 = ( Rf_gpptr(dd)->xlog ) ?	log10(x[1]) : x[1];
2964
		x1 = ( Rf_gpptr(dd)->xlog ) ?	log10(x[1]) : x[1];
2965
 
2965
 
2966
		y[0] = aa + x0 * bb;
2966
		y[0] = aa + x0 * bb;
2967
		y[1] = aa + x1 * bb;
2967
		y[1] = aa + x1 * bb;
2968
 
2968
 
2969
		if ( Rf_gpptr(dd)->ylog ){
2969
		if ( Rf_gpptr(dd)->ylog ){
2970
		    y[0] = pow(10.,y[0]);
2970
		    y[0] = pow(10.,y[0]);
2971
		    y[1] = pow(10.,y[1]);
2971
		    y[1] = pow(10.,y[1]);
2972
		}
2972
		}
2973
 
2973
 
2974
		GLine(x[0], y[0], x[1], y[1], USER, dd);
2974
		GLine(x[0], y[0], x[1], y[1], USER, dd);
2975
	    }
2975
	    }
2976
	}
2976
	}
2977
	GMode(0, dd);
2977
	GMode(0, dd);
2978
	nlines++;
2978
	nlines++;
2979
    }
2979
    }
2980
    if (h != R_NilValue) {
2980
    if (h != R_NilValue) {
2981
	GMode(1, dd);
2981
	GMode(1, dd);
2982
	for (i = 0; i < LENGTH(h); i++) {
2982
	for (i = 0; i < LENGTH(h); i++) {
2983
	    Rf_gpptr(dd)->col = INTEGER(col)[nlines % ncol];
2983
	    Rf_gpptr(dd)->col = INTEGER(col)[nlines % ncol];
2984
	    if (nlty && INTEGER(lty)[nlines % nlty] != NA_INTEGER)
2984
	    if (nlty && INTEGER(lty)[nlines % nlty] != NA_INTEGER)
2985
		Rf_gpptr(dd)->lty = INTEGER(lty)[nlines % nlty];
2985
		Rf_gpptr(dd)->lty = INTEGER(lty)[nlines % nlty];
2986
	    else
2986
	    else
2987
		Rf_gpptr(dd)->lty = Rf_dpptr(dd)->lty;
2987
		Rf_gpptr(dd)->lty = Rf_dpptr(dd)->lty;
2988
	    Rf_gpptr(dd)->lwd = REAL(lwd)[nlines % nlwd];
2988
	    Rf_gpptr(dd)->lwd = REAL(lwd)[nlines % nlwd];
2989
	    aa = REAL(h)[i];
2989
	    aa = REAL(h)[i];
2990
	    if (R_FINITE(aa) && R_FINITE(Rf_gpptr(dd)->lwd)) {
2990
	    if (R_FINITE(aa) && R_FINITE(Rf_gpptr(dd)->lwd)) {
2991
		getxlimits(x, dd);
2991
		getxlimits(x, dd);
2992
		y[0] = aa;
2992
		y[0] = aa;
2993
		y[1] = aa;
2993
		y[1] = aa;
2994
		GLine(x[0], y[0], x[1], y[1], USER, dd);
2994
		GLine(x[0], y[0], x[1], y[1], USER, dd);
2995
	    }
2995
	    }
2996
	    nlines++;
2996
	    nlines++;
2997
	}
2997
	}
2998
	GMode(0, dd);
2998
	GMode(0, dd);
2999
    }
2999
    }
3000
    if (v != R_NilValue) {
3000
    if (v != R_NilValue) {
3001
	GMode(1, dd);
3001
	GMode(1, dd);
3002
	for (i = 0; i < LENGTH(v); i++) {
3002
	for (i = 0; i < LENGTH(v); i++) {
3003
	    Rf_gpptr(dd)->col = INTEGER(col)[nlines % ncol];
3003
	    Rf_gpptr(dd)->col = INTEGER(col)[nlines % ncol];
3004
	    if (nlty && INTEGER(lty)[nlines % nlty] != NA_INTEGER)
3004
	    if (nlty && INTEGER(lty)[nlines % nlty] != NA_INTEGER)
3005
		Rf_gpptr(dd)->lty = INTEGER(lty)[nlines % nlty];
3005
		Rf_gpptr(dd)->lty = INTEGER(lty)[nlines % nlty];
3006
	    else
3006
	    else
3007
		Rf_gpptr(dd)->lty = Rf_dpptr(dd)->lty;
3007
		Rf_gpptr(dd)->lty = Rf_dpptr(dd)->lty;
3008
	    Rf_gpptr(dd)->lwd = REAL(lwd)[nlines % nlwd];
3008
	    Rf_gpptr(dd)->lwd = REAL(lwd)[nlines % nlwd];
3009
	    aa = REAL(v)[i];
3009
	    aa = REAL(v)[i];
3010
	    if (R_FINITE(aa) && R_FINITE(Rf_gpptr(dd)->lwd)) {
3010
	    if (R_FINITE(aa) && R_FINITE(Rf_gpptr(dd)->lwd)) {
3011
		getylimits(y, dd);
3011
		getylimits(y, dd);
3012
		x[0] = aa;
3012
		x[0] = aa;
3013
		x[1] = aa;
3013
		x[1] = aa;
3014
		GLine(x[0], y[0], x[1], y[1], USER, dd);
3014
		GLine(x[0], y[0], x[1], y[1], USER, dd);
3015
	    }
3015
	    }
3016
	    nlines++;
3016
	    nlines++;
3017
	}
3017
	}
3018
	GMode(0, dd);
3018
	GMode(0, dd);
3019
    }
3019
    }
3020
    UNPROTECT(3);
3020
    UNPROTECT(3);
3021
    GRestorePars(dd);
3021
    GRestorePars(dd);
3022
    /* NOTE: only record operation if no "error"  */
3022
    /* NOTE: only record operation if no "error"  */
3023
    if (GRecording(call, dd))
3023
    if (GRecording(call, dd))
3024
	recordGraphicOperation(op, originalArgs, dd);
3024
	recordGraphicOperation(op, originalArgs, dd);
3025
    return R_NilValue;
3025
    return R_NilValue;
3026
} /* do_abline */
3026
} /* do_abline */
3027
 
3027
 
3028
 
3028
 
3029
SEXP attribute_hidden do_box(SEXP call, SEXP op, SEXP args, SEXP env)
3029
SEXP attribute_hidden do_box(SEXP call, SEXP op, SEXP args, SEXP env)
3030
{
3030
{
3031
/*     box(which="plot", lty="solid", ...)
3031
/*     box(which="plot", lty="solid", ...)
3032
       --- which is coded, 1 = plot, 2 = figure, 3 = inner, 4 = outer.
3032
       --- which is coded, 1 = plot, 2 = figure, 3 = inner, 4 = outer.
3033
*/
3033
*/
3034
    int which, col;
3034
    int which, col;
3035
    SEXP colsxp, fgsxp;
3035
    SEXP colsxp, fgsxp;
3036
    SEXP originalArgs = args;
3036
    SEXP originalArgs = args;
3037
    DevDesc *dd = CurrentDevice();
3037
    DevDesc *dd = CurrentDevice();
3038
 
3038
 
3039
    GCheckState(dd);
3039
    GCheckState(dd);
3040
    GSavePars(dd);
3040
    GSavePars(dd);
3041
    which = asInteger(CAR(args)); args = CDR(args);
3041
    which = asInteger(CAR(args)); args = CDR(args);
3042
    if (which < 1 || which > 4)
3042
    if (which < 1 || which > 4)
3043
	error(_("invalid 'which' specification"));
3043
	error(_("invalid 'which' specification"));
3044
    /*
3044
    /*
3045
     * If specified non-NA col then use that, else ...
3045
     * If specified non-NA col then use that, else ...
3046
     *
3046
     *
3047
     * if specified non-NA fg then use that, else ...
3047
     * if specified non-NA fg then use that, else ...
3048
     *
3048
     *
3049
     * else use par("col")
3049
     * else use par("col")
3050
     */
3050
     */
3051
    col= Rf_gpptr(dd)->col;
3051
    col= Rf_gpptr(dd)->col;
3052
    ProcessInlinePars(args, dd, call);
3052
    ProcessInlinePars(args, dd, call);
3053
    colsxp = getInlinePar(args, "col");
3053
    colsxp = getInlinePar(args, "col");
3054
    if (isNAcol(colsxp, 0, 1)) {
3054
    if (isNAcol(colsxp, 0, 1)) {
3055
	fgsxp = getInlinePar(args, "fg");
3055
	fgsxp = getInlinePar(args, "fg");
3056
	if (isNAcol(fgsxp, 0, 1))
3056
	if (isNAcol(fgsxp, 0, 1))
3057
	    Rf_gpptr(dd)->col = col;
3057
	    Rf_gpptr(dd)->col = col;
3058
	else
3058
	else
3059
	    Rf_gpptr(dd)->col = Rf_gpptr(dd)->fg;
3059
	    Rf_gpptr(dd)->col = Rf_gpptr(dd)->fg;
3060
    }
3060
    }
3061
    /* override par("xpd") and force clipping to device region */
3061
    /* override par("xpd") and force clipping to device region */
3062
    Rf_gpptr(dd)->xpd = 2;
3062
    Rf_gpptr(dd)->xpd = 2;
3063
    GMode(1, dd);
3063
    GMode(1, dd);
3064
    GBox(which, dd);
3064
    GBox(which, dd);
3065
    GMode(0, dd);
3065
    GMode(0, dd);
3066
    GRestorePars(dd);
3066
    GRestorePars(dd);
3067
    /* NOTE: only record operation if no "error"  */
3067
    /* NOTE: only record operation if no "error"  */
3068
    if (GRecording(call, dd))
3068
    if (GRecording(call, dd))
3069
	recordGraphicOperation(op, originalArgs, dd);
3069
	recordGraphicOperation(op, originalArgs, dd);
3070
    return R_NilValue;
3070
    return R_NilValue;
3071
}
3071
}
3072
 
3072
 
3073
static void drawPointsLines(double xp, double yp, double xold, double yold,
3073
static void drawPointsLines(double xp, double yp, double xold, double yold,
3074
			    char type, int first, DevDesc *dd)
3074
			    char type, int first, DevDesc *dd)
3075
{
3075
{
3076
    if (type == 'p' || type == 'o')
3076
    if (type == 'p' || type == 'o')
3077
	GSymbol(xp, yp, DEVICE, Rf_gpptr(dd)->pch, dd);
3077
	GSymbol(xp, yp, DEVICE, Rf_gpptr(dd)->pch, dd);
3078
    if ((type == 'l' || type == 'o') && !first)
3078
    if ((type == 'l' || type == 'o') && !first)
3079
	GLine(xold, yold, xp, yp, DEVICE, dd);
3079
	GLine(xold, yold, xp, yp, DEVICE, dd);
3080
}
3080
}
3081
 
3081
 
3082
SEXP attribute_hidden do_locator(SEXP call, SEXP op, SEXP args, SEXP env)
3082
SEXP attribute_hidden do_locator(SEXP call, SEXP op, SEXP args, SEXP env)
3083
{
3083
{
3084
    SEXP x, y, nobs, ans, saveans, stype = R_NilValue;
3084
    SEXP x, y, nobs, ans, saveans, stype = R_NilValue;
3085
    int i, n, type='p';
3085
    int i, n, type='p';
3086
    double xp, yp, xold=0, yold=0;
3086
    double xp, yp, xold=0, yold=0;
3087
    DevDesc *dd = CurrentDevice();
3087
    DevDesc *dd = CurrentDevice();
3088
 
3088
 
3089
    /* If replaying, just draw the points and lines that were recorded */
3089
    /* If replaying, just draw the points and lines that were recorded */
3090
    if (call == R_NilValue) {
3090
    if (call == R_NilValue) {
3091
	x = CAR(args); args = CDR(args);
3091
	x = CAR(args); args = CDR(args);
3092
	y = CAR(args); args = CDR(args);
3092
	y = CAR(args); args = CDR(args);
3093
	nobs = CAR(args); args = CDR(args);
3093
	nobs = CAR(args); args = CDR(args);
3094
	n = INTEGER(nobs)[0];
3094
	n = INTEGER(nobs)[0];
3095
	stype = CAR(args); args = CDR(args);
3095
	stype = CAR(args); args = CDR(args);
3096
	type = CHAR(STRING_ELT(stype, 0))[0];
3096
	type = CHAR(STRING_ELT(stype, 0))[0];
3097
	if (type != 'n') {
3097
	if (type != 'n') {
3098
	    GMode(1, dd);
3098
	    GMode(1, dd);
3099
	    for (i = 0; i < n; i++) {
3099
	    for (i = 0; i < n; i++) {
3100
		xp = REAL(x)[i];
3100
		xp = REAL(x)[i];
3101
		yp = REAL(y)[i];
3101
		yp = REAL(y)[i];
3102
		GConvert(&xp, &yp, USER, DEVICE, dd);
3102
		GConvert(&xp, &yp, USER, DEVICE, dd);
3103
		drawPointsLines(xp, yp, xold, yold, type, i==0, dd);
3103
		drawPointsLines(xp, yp, xold, yold, type, i==0, dd);
3104
		xold = xp;
3104
		xold = xp;
3105
		yold = yp;
3105
		yold = yp;
3106
	    }
3106
	    }
3107
	    GMode(0, dd);
3107
	    GMode(0, dd);
3108
	}
3108
	}
3109
	return R_NilValue;
3109
	return R_NilValue;
3110
    } else {
3110
    } else {
3111
	GCheckState(dd);
3111
	GCheckState(dd);
3112
 
3112
 
3113
	checkArity(op, args);
3113
	checkArity(op, args);
3114
	n = asInteger(CAR(args));
3114
	n = asInteger(CAR(args));
3115
	if (n <= 0 || n == NA_INTEGER)
3115
	if (n <= 0 || n == NA_INTEGER)
3116
	    error(_("invalid number of points in locator()"));
3116
	    error(_("invalid number of points in locator()"));
3117
	args = CDR(args);
3117
	args = CDR(args);
3118
	if (isString(CAR(args)) && LENGTH(CAR(args)) == 1)
3118
	if (isString(CAR(args)) && LENGTH(CAR(args)) == 1)
3119
	    stype = CAR(args);
3119
	    stype = CAR(args);
3120
	else
3120
	else
3121
	    error(_("invalid plot type"));
3121
	    error(_("invalid plot type"));
3122
	type = CHAR(STRING_ELT(stype, 0))[0];
3122
	type = CHAR(STRING_ELT(stype, 0))[0];
3123
	PROTECT(x = allocVector(REALSXP, n));
3123
	PROTECT(x = allocVector(REALSXP, n));
3124
	PROTECT(y = allocVector(REALSXP, n));
3124
	PROTECT(y = allocVector(REALSXP, n));
3125
	PROTECT(nobs=allocVector(INTSXP,1));
3125
	PROTECT(nobs=allocVector(INTSXP,1));
3126
 
3126
 
3127
	GMode(2, dd);
3127
	GMode(2, dd);
3128
	for (i = 0; i < n; i++) {
3128
	for (i = 0; i < n; i++) {
3129
	    if (!GLocator(&(REAL(x)[i]), &(REAL(y)[i]), USER, dd)) break;
3129
	    if (!GLocator(&(REAL(x)[i]), &(REAL(y)[i]), USER, dd)) break;
3130
	    if (type != 'n') {
3130
	    if (type != 'n') {
3131
		GMode(1, dd);
3131
		GMode(1, dd);
3132
		xp = REAL(x)[i];
3132
		xp = REAL(x)[i];
3133
		yp = REAL(y)[i];
3133
		yp = REAL(y)[i];
3134
		GConvert(&xp, &yp, USER, DEVICE, dd);
3134
		GConvert(&xp, &yp, USER, DEVICE, dd);
3135
		drawPointsLines(xp, yp, xold, yold, type, i==0, dd);
3135
		drawPointsLines(xp, yp, xold, yold, type, i==0, dd);
3136
		GMode(2, dd);
3136
		GMode(2, dd);
3137
		xold = xp; yold = yp;
3137
		xold = xp; yold = yp;
3138
	    }
3138
	    }
3139
	}
3139
	}
3140
	GMode(0, dd);
3140
	GMode(0, dd);
3141
	INTEGER(nobs)[0] = i;
3141
	INTEGER(nobs)[0] = i;
3142
	for (; i < n; i++) {
3142
	for (; i < n; i++) {
3143
	    REAL(x)[i] = NA_REAL;
3143
	    REAL(x)[i] = NA_REAL;
3144
	    REAL(y)[i] = NA_REAL;
3144
	    REAL(y)[i] = NA_REAL;
3145
	}
3145
	}
3146
	PROTECT(ans = allocList(3));
3146
	PROTECT(ans = allocList(3));
3147
	SETCAR(ans, x);
3147
	SETCAR(ans, x);
3148
	SETCADR(ans, y);
3148
	SETCADR(ans, y);
3149
	SETCADDR(ans, nobs);
3149
	SETCADDR(ans, nobs);
3150
	PROTECT(saveans = allocList(4));
3150
	PROTECT(saveans = allocList(4));
3151
	SETCAR(saveans, x);
3151
	SETCAR(saveans, x);
3152
	SETCADR(saveans, y);
3152
	SETCADR(saveans, y);
3153
	SETCADDR(saveans, nobs);
3153
	SETCADDR(saveans, nobs);
3154
	SETCADDDR(saveans, CAR(args));
3154
	SETCADDDR(saveans, CAR(args));
3155
	/* Record the points and lines that were drawn in the display list */
3155
	/* Record the points and lines that were drawn in the display list */
3156
	recordGraphicOperation(op, saveans, dd);
3156
	recordGraphicOperation(op, saveans, dd);
3157
	UNPROTECT(5);
3157
	UNPROTECT(5);
3158
	return ans;
3158
	return ans;
3159
    }
3159
    }
3160
}
3160
}
3161
 
3161
 
3162
static void drawLabel(double xi, double yi, int pos, double offset,
3162
static void drawLabel(double xi, double yi, int pos, double offset,
3163
		      const char *l, DevDesc *dd)
3163
		      const char *l, DevDesc *dd)
3164
{
3164
{
3165
    switch (pos) {
3165
    switch (pos) {
3166
    case 4:
3166
    case 4:
3167
	xi = xi+offset;
3167
	xi = xi+offset;
3168
	GText(xi, yi, INCHES, l, 0.0,
3168
	GText(xi, yi, INCHES, l, 0.0,
3169
	      Rf_gpptr(dd)->yCharOffset, 0.0, dd);
3169
	      Rf_gpptr(dd)->yCharOffset, 0.0, dd);
3170
	break;
3170
	break;
3171
    case 2:
3171
    case 2:
3172
	xi = xi-offset;
3172
	xi = xi-offset;
3173
	GText(xi, yi, INCHES, l, 1.0,
3173
	GText(xi, yi, INCHES, l, 1.0,
3174
	      Rf_gpptr(dd)->yCharOffset, 0.0, dd);
3174
	      Rf_gpptr(dd)->yCharOffset, 0.0, dd);
3175
	break;
3175
	break;
3176
    case 3:
3176
    case 3:
3177
	yi = yi+offset;
3177
	yi = yi+offset;
3178
	GText(xi, yi, INCHES, l, 0.5,
3178
	GText(xi, yi, INCHES, l, 0.5,
3179
	      0.0, 0.0, dd);
3179
	      0.0, 0.0, dd);
3180
	break;
3180
	break;
3181
    case 1:
3181
    case 1:
3182
	yi = yi-offset;
3182
	yi = yi-offset;
3183
	GText(xi, yi, INCHES, l, 0.5,
3183
	GText(xi, yi, INCHES, l, 0.5,
3184
	      1-(0.5-Rf_gpptr(dd)->yCharOffset),
3184
	      1-(0.5-Rf_gpptr(dd)->yCharOffset),
3185
	      0.0, dd);
3185
	      0.0, dd);
3186
	break;
3186
	break;
3187
    case 0:
3187
    case 0:
3188
	GText(xi, yi, INCHES, l, 0.0, 0.0, 0.0, dd);
3188
	GText(xi, yi, INCHES, l, 0.0, 0.0, 0.0, dd);
3189
	break;
3189
	break;
3190
    }
3190
    }
3191
}
3191
}
3192
 
3192
 
3193
/* This manages R_Visibile */
3193
/* This manages R_Visibile */
3194
SEXP attribute_hidden do_identify(SEXP call, SEXP op, SEXP args, SEXP env)
3194
SEXP attribute_hidden do_identify(SEXP call, SEXP op, SEXP args, SEXP env)
3195
{
3195
{
3196
    SEXP ans, x, y, l, ind, pos, Offset, draw, saveans;
3196
    SEXP ans, x, y, l, ind, pos, Offset, draw, saveans;
3197
    double xi, yi, xp, yp, d, dmin, offset, tol;
3197
    double xi, yi, xp, yp, d, dmin, offset, tol;
3198
    int atpen, i, imin, k, n, npts, plot, posi, warn;
3198
    int atpen, i, imin, k, n, npts, plot, posi, warn;
3199
    DevDesc *dd = CurrentDevice();
3199
    DevDesc *dd = CurrentDevice();
3200
 
3200
 
3201
    /* If we are replaying the display list, then just redraw the
3201
    /* If we are replaying the display list, then just redraw the
3202
       labels beside the identified points */
3202
       labels beside the identified points */
3203
    if (call == R_NilValue) {
3203
    if (call == R_NilValue) {
3204
	ind = CAR(args); args = CDR(args);
3204
	ind = CAR(args); args = CDR(args);
3205
	pos = CAR(args); args = CDR(args);
3205
	pos = CAR(args); args = CDR(args);
3206
	x = CAR(args); args = CDR(args);
3206
	x = CAR(args); args = CDR(args);
3207
	y = CAR(args); args = CDR(args);
3207
	y = CAR(args); args = CDR(args);
3208
	Offset = CAR(args); args = CDR(args);
3208
	Offset = CAR(args); args = CDR(args);
3209
	l = CAR(args); args = CDR(args);
3209
	l = CAR(args); args = CDR(args);
3210
	draw = CAR(args);
3210
	draw = CAR(args);
3211
	n = length(x);
3211
	n = length(x);
3212
	/*
3212
	/*
3213
	 * Most of the appropriate settings have been set up in
3213
	 * Most of the appropriate settings have been set up in
3214
	 * R code by par(...)
3214
	 * R code by par(...)
3215
	 * Hence no GSavePars() or ProcessInlinePars() here
3215
	 * Hence no GSavePars() or ProcessInlinePars() here
3216
	 * (also because this function is unusual in that it does
3216
	 * (also because this function is unusual in that it does
3217
	 *  different things when run by a user compared to when
3217
	 *  different things when run by a user compared to when
3218
	 *  run from the display list)
3218
	 *  run from the display list)
3219
	 * BUT par(cex) only sets cexbase, so here we set cex from cexbase
3219
	 * BUT par(cex) only sets cexbase, so here we set cex from cexbase
3220
	 */
3220
	 */
3221
	Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase;
3221
	Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase;
3222
	offset = GConvertXUnits(asReal(Offset), CHARS, INCHES, dd);
3222
	offset = GConvertXUnits(asReal(Offset), CHARS, INCHES, dd);
3223
	for (i = 0; i < n; i++) {
3223
	for (i = 0; i < n; i++) {
3224
	    plot = LOGICAL(ind)[i];
3224
	    plot = LOGICAL(ind)[i];
3225
	    if (LOGICAL(draw)[0] && plot) {
3225
	    if (LOGICAL(draw)[0] && plot) {
3226
		xi = REAL(x)[i];
3226
		xi = REAL(x)[i];
3227
		yi = REAL(y)[i];
3227
		yi = REAL(y)[i];
3228
		GConvert(&xi, &yi, USER, INCHES, dd);
3228
		GConvert(&xi, &yi, USER, INCHES, dd);
3229
		posi = INTEGER(pos)[i];
3229
		posi = INTEGER(pos)[i];
3230
		drawLabel(xi, yi, posi, offset,
3230
		drawLabel(xi, yi, posi, offset,
3231
			  translateChar(STRING_ELT(l, i)), dd);
3231
			  translateChar(STRING_ELT(l, i)), dd);
3232
	    }
3232
	    }
3233
	}
3233
	}
3234
	return R_NilValue;
3234
	return R_NilValue;
3235
    }
3235
    }
3236
    else {
3236
    else {
3237
	GCheckState(dd);
3237
	GCheckState(dd);
3238
 
3238
 
3239
	checkArity(op, args);
3239
	checkArity(op, args);
3240
	x = CAR(args); args = CDR(args);
3240
	x = CAR(args); args = CDR(args);
3241
	y = CAR(args); args = CDR(args);
3241
	y = CAR(args); args = CDR(args);
3242
	l = CAR(args); args = CDR(args);
3242
	l = CAR(args); args = CDR(args);
3243
	npts = asInteger(CAR(args)); args = CDR(args);
3243
	npts = asInteger(CAR(args)); args = CDR(args);
3244
	plot = asLogical(CAR(args)); args = CDR(args);
3244
	plot = asLogical(CAR(args)); args = CDR(args);
3245
	Offset = CAR(args); args = CDR(args);
3245
	Offset = CAR(args); args = CDR(args);
3246
	tol = asReal(CAR(args)); args = CDR(args);
3246
	tol = asReal(CAR(args)); args = CDR(args);
3247
	atpen = asLogical(CAR(args));
3247
	atpen = asLogical(CAR(args));
3248
	if (npts <= 0 || npts == NA_INTEGER)
3248
	if (npts <= 0 || npts == NA_INTEGER)
3249
	    error(_("invalid number of points in identify()"));
3249
	    error(_("invalid number of points in identify()"));
3250
	if (!isReal(x) || !isReal(y) || !isString(l) || !isReal(Offset))
3250
	if (!isReal(x) || !isReal(y) || !isString(l) || !isReal(Offset))
3251
	    error(_("incorrect argument type"));
3251
	    error(_("incorrect argument type"));
3252
	if (tol <= 0 || ISNAN(tol))
3252
	if (tol <= 0 || ISNAN(tol))
3253
	    error(_("invalid '%s' value"), "tolerance");
3253
	    error(_("invalid '%s' value"), "tolerance");
3254
	if (plot == NA_LOGICAL)
3254
	if (plot == NA_LOGICAL)
3255
	    error(_("invalid '%s' value"), "plot");
3255
	    error(_("invalid '%s' value"), "plot");
3256
	if (atpen == NA_LOGICAL)
3256
	if (atpen == NA_LOGICAL)
3257
	    error(_("invalid '%s' value"), "atpen");
3257
	    error(_("invalid '%s' value"), "atpen");
3258
	if (LENGTH(x) != LENGTH(y) || LENGTH(x) != LENGTH(l))
3258
	if (LENGTH(x) != LENGTH(y) || LENGTH(x) != LENGTH(l))
3259
	    error(_("different argument lengths"));
3259
	    error(_("different argument lengths"));
3260
	n = LENGTH(x);
3260
	n = LENGTH(x);
3261
	if (n <= 0) {
3261
	if (n <= 0) {
3262
	    R_Visible = FALSE;
3262
	    R_Visible = FALSE;
3263
	    return NULL;
3263
	    return NULL;
3264
	}
3264
	}
3265
 
3265
 
3266
	/*
3266
	/*
3267
	 * Most of the appropriate settings have been set up in
3267
	 * Most of the appropriate settings have been set up in
3268
	 * R code by par(...)
3268
	 * R code by par(...)
3269
	 * Hence no GSavePars() or ProcessInlinePars() here
3269
	 * Hence no GSavePars() or ProcessInlinePars() here
3270
	 * (also because this function is unusual in that it does
3270
	 * (also because this function is unusual in that it does
3271
	 *  different things when run by a user compared to when
3271
	 *  different things when run by a user compared to when
3272
	 *  run from the display list)
3272
	 *  run from the display list)
3273
	 * BUT par(cex) only sets cexbase, so here we set cex from cexbase
3273
	 * BUT par(cex) only sets cexbase, so here we set cex from cexbase
3274
	 */
3274
	 */
3275
	Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase;
3275
	Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase;
3276
	offset = GConvertXUnits(asReal(Offset), CHARS, INCHES, dd);
3276
	offset = GConvertXUnits(asReal(Offset), CHARS, INCHES, dd);
3277
	PROTECT(ind = allocVector(LGLSXP, n));
3277
	PROTECT(ind = allocVector(LGLSXP, n));
3278
	PROTECT(pos = allocVector(INTSXP, n));
3278
	PROTECT(pos = allocVector(INTSXP, n));
3279
	for (i = 0; i < n; i++) LOGICAL(ind)[i] = 0;
3279
	for (i = 0; i < n; i++) LOGICAL(ind)[i] = 0;
3280
 
3280
 
3281
	k = 0;
3281
	k = 0;
3282
	GMode(2, dd);
3282
	GMode(2, dd);
3283
	PROTECT(x = duplicate(x));
3283
	PROTECT(x = duplicate(x));
3284
	PROTECT(y = duplicate(y));
3284
	PROTECT(y = duplicate(y));
3285
	while (k < npts) {
3285
	while (k < npts) {
3286
	    if (!GLocator(&xp, &yp, INCHES, dd)) break;
3286
	    if (!GLocator(&xp, &yp, INCHES, dd)) break;
3287
	    /*
3287
	    /*
3288
	     * Repeat cex setting from cexbase within loop
3288
	     * Repeat cex setting from cexbase within loop
3289
	     * so that if window is redrawn
3289
	     * so that if window is redrawn
3290
	     * (e.g., conver/uncover window)
3290
	     * (e.g., conver/uncover window)
3291
	     * during identifying (i.e., between clicks)
3291
	     * during identifying (i.e., between clicks)
3292
	     * we reset cex properly.
3292
	     * we reset cex properly.
3293
	     */
3293
	     */
3294
	    Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase;
3294
	    Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase;
3295
	    dmin = DBL_MAX;
3295
	    dmin = DBL_MAX;
3296
	    imin = -1;
3296
	    imin = -1;
3297
	    for (i = 0; i < n; i++) {
3297
	    for (i = 0; i < n; i++) {
3298
		xi = REAL(x)[i];
3298
		xi = REAL(x)[i];
3299
		yi = REAL(y)[i];
3299
		yi = REAL(y)[i];
3300
		GConvert(&xi, &yi, USER, INCHES, dd);
3300
		GConvert(&xi, &yi, USER, INCHES, dd);
3301
		if (!R_FINITE(xi) || !R_FINITE(yi)) continue;
3301
		if (!R_FINITE(xi) || !R_FINITE(yi)) continue;
3302
		d = hypot(xp-xi, yp-yi);
3302
		d = hypot(xp-xi, yp-yi);
3303
		if (d < dmin) {
3303
		if (d < dmin) {
3304
		    imin = i;
3304
		    imin = i;
3305
		    dmin = d;
3305
		    dmin = d;
3306
		}
3306
		}
3307
	    }
3307
	    }
3308
	    /* can't use warning because we want to print immediately  */
3308
	    /* can't use warning because we want to print immediately  */
3309
	    /* might want to handle warn=2? */
3309
	    /* might want to handle warn=2? */
3310
	    warn = asInteger(GetOption(install("warn"), R_BaseEnv));
3310
	    warn = asInteger(GetOption(install("warn"), R_BaseEnv));
3311
	    if (dmin > tol) {
3311
	    if (dmin > tol) {
3312
	        if(warn >= 0) {
3312
	        if(warn >= 0) {
3313
		    REprintf(_("warning: no point with %.2f inches\n"), tol);
3313
		    REprintf(_("warning: no point with %.2f inches\n"), tol);
3314
		    R_FlushConsole();
3314
		    R_FlushConsole();
3315
		}
3315
		}
3316
	    }
3316
	    }
3317
	    else if (LOGICAL(ind)[imin]) {
3317
	    else if (LOGICAL(ind)[imin]) {
3318
	        if(warn >= 0 ) {
3318
	        if(warn >= 0 ) {
3319
		    REprintf(_("warning: nearest point already identified\n"));
3319
		    REprintf(_("warning: nearest point already identified\n"));
3320
		    R_FlushConsole();
3320
		    R_FlushConsole();
3321
		}
3321
		}
3322
	    }
3322
	    }
3323
	    else {
3323
	    else {
3324
		k++;
3324
		k++;
3325
		LOGICAL(ind)[imin] = 1;
3325
		LOGICAL(ind)[imin] = 1;
3326
 
3326
 
3327
		if (atpen) {
3327
		if (atpen) {
3328
		    xi = xp;
3328
		    xi = xp;
3329
		    yi = yp;
3329
		    yi = yp;
3330
		    INTEGER(pos)[imin] = 0;
3330
		    INTEGER(pos)[imin] = 0;
3331
		    /* now record where to replot if necessary */
3331
		    /* now record where to replot if necessary */
3332
		    GConvert(&xp, &yp, INCHES, USER, dd);
3332
		    GConvert(&xp, &yp, INCHES, USER, dd);
3333
		    REAL(x)[imin] = xp; REAL(y)[imin] = yp;
3333
		    REAL(x)[imin] = xp; REAL(y)[imin] = yp;
3334
		} else {
3334
		} else {
3335
		    xi = REAL(x)[imin];
3335
		    xi = REAL(x)[imin];
3336
		    yi = REAL(y)[imin];
3336
		    yi = REAL(y)[imin];
3337
		    GConvert(&xi, &yi, USER, INCHES, dd);
3337
		    GConvert(&xi, &yi, USER, INCHES, dd);
3338
		    if (fabs(xp-xi) >= fabs(yp-yi)) {
3338
		    if (fabs(xp-xi) >= fabs(yp-yi)) {
3339
			if (xp >= xi)
3339
			if (xp >= xi)
3340
			    INTEGER(pos)[imin] = 4;
3340
			    INTEGER(pos)[imin] = 4;
3341
			else
3341
			else
3342
			    INTEGER(pos)[imin] = 2;
3342
			    INTEGER(pos)[imin] = 2;
3343
		    } else {
3343
		    } else {
3344
			if (yp >= yi)
3344
			if (yp >= yi)
3345
			    INTEGER(pos)[imin] = 3;
3345
			    INTEGER(pos)[imin] = 3;
3346
			else
3346
			else
3347
			    INTEGER(pos)[imin] = 1;
3347
			    INTEGER(pos)[imin] = 1;
3348
		    }
3348
		    }
3349
		}
3349
		}
3350
		if (plot)
3350
		if (plot)
3351
		    drawLabel(xi, yi, INTEGER(pos)[imin], offset,
3351
		    drawLabel(xi, yi, INTEGER(pos)[imin], offset,
3352
			      translateChar(STRING_ELT(l, imin)), dd);
3352
			      translateChar(STRING_ELT(l, imin)), dd);
3353
	    }
3353
	    }
3354
	}
3354
	}
3355
	GMode(0, dd);
3355
	GMode(0, dd);
3356
	PROTECT(ans = allocList(2));
3356
	PROTECT(ans = allocList(2));
3357
	SETCAR(ans, ind);
3357
	SETCAR(ans, ind);
3358
	SETCADR(ans, pos);
3358
	SETCADR(ans, pos);
3359
	PROTECT(saveans = allocList(7));
3359
	PROTECT(saveans = allocList(7));
3360
	SETCAR(saveans, ind);
3360
	SETCAR(saveans, ind);
3361
	SETCADR(saveans, pos);
3361
	SETCADR(saveans, pos);
3362
	SETCADDR(saveans, x);
3362
	SETCADDR(saveans, x);
3363
	SETCADDDR(saveans, y);
3363
	SETCADDDR(saveans, y);
3364
	SETCAD4R(saveans, Offset);
3364
	SETCAD4R(saveans, Offset);
3365
	SETCAD4R(CDR(saveans), l);
3365
	SETCAD4R(CDR(saveans), l);
3366
	SETCAD4R(CDDR(saveans), ScalarLogical(plot));
3366
	SETCAD4R(CDDR(saveans), ScalarLogical(plot));
3367
 
3367
 
3368
	/* If we are recording, save enough information to be able to
3368
	/* If we are recording, save enough information to be able to
3369
	   redraw the text labels beside identified points */
3369
	   redraw the text labels beside identified points */
3370
	if (GRecording(call, dd))
3370
	if (GRecording(call, dd))
3371
	    recordGraphicOperation(op, saveans, dd);
3371
	    recordGraphicOperation(op, saveans, dd);
3372
	UNPROTECT(6);
3372
	UNPROTECT(6);
3373
 
3373
 
3374
	R_Visible = TRUE;
3374
	R_Visible = TRUE;
3375
	return ans;
3375
	return ans;
3376
    }
3376
    }
3377
}
3377
}
3378
 
3378
 
3379
/* strheight(str, units)  ||  strwidth(str, units) */
3379
/* strheight(str, units)  ||  strwidth(str, units) */
3380
#define DO_STR_DIM(KIND) 						\
3380
#define DO_STR_DIM(KIND) 						\
3381
{									\
3381
{									\
3382
    SEXP ans, str, ch;							\
3382
    SEXP ans, str, ch;							\
3383
    int i, n, units;							\
3383
    int i, n, units;							\
3384
    double cex, cexsave;						\
3384
    double cex, cexsave;						\
3385
    DevDesc *dd = CurrentDevice();					\
3385
    DevDesc *dd = CurrentDevice();					\
3386
									\
3386
									\
3387
    checkArity(op, args);						\
3387
    checkArity(op, args);						\
3388
    /* GCheckState(dd); */						\
3388
    /* GCheckState(dd); */						\
3389
									\
3389
									\
3390
    str = CAR(args);							\
3390
    str = CAR(args);							\
3391
    if (isSymbol(str) || isLanguage(str))				\
3391
    if (isSymbol(str) || isLanguage(str))				\
3392
	str = coerceVector(str, EXPRSXP);				\
3392
	str = coerceVector(str, EXPRSXP);				\
3393
    else if (!isExpression(str))					\
3393
    else if (!isExpression(str))					\
3394
	str = coerceVector(str, STRSXP);				\
3394
	str = coerceVector(str, STRSXP);				\
3395
    PROTECT(str);							\
3395
    PROTECT(str);							\
3396
    args = CDR(args);							\
3396
    args = CDR(args);							\
3397
									\
3397
									\
3398
    if ((units = asInteger(CAR(args))) == NA_INTEGER || units < 0)	\
3398
    if ((units = asInteger(CAR(args))) == NA_INTEGER || units < 0)	\
3399
	error(_("invalid units"));				\
3399
	error(_("invalid units"));				\
3400
    args = CDR(args);							\
3400
    args = CDR(args);							\
3401
									\
3401
									\
3402
    if (isNull(CAR(args)))						\
3402
    if (isNull(CAR(args)))						\
3403
	cex = Rf_gpptr(dd)->cex;					\
3403
	cex = Rf_gpptr(dd)->cex;					\
3404
    else if (!R_FINITE((cex = asReal(CAR(args)))) || cex <= 0.0)       	\
3404
    else if (!R_FINITE((cex = asReal(CAR(args)))) || cex <= 0.0)       	\
3405
	error(_("invalid '%s' value"), "cex");	       	\
3405
	error(_("invalid '%s' value"), "cex");	       	\
3406
									\
3406
									\
3407
    n = LENGTH(str);							\
3407
    n = LENGTH(str);							\
3408
    PROTECT(ans = allocVector(REALSXP, n));				\
3408
    PROTECT(ans = allocVector(REALSXP, n));				\
3409
    cexsave = Rf_gpptr(dd)->cex;					\
3409
    cexsave = Rf_gpptr(dd)->cex;					\
3410
    Rf_gpptr(dd)->cex = cex * Rf_gpptr(dd)->cexbase;			\
3410
    Rf_gpptr(dd)->cex = cex * Rf_gpptr(dd)->cexbase;			\
3411
    for (i = 0; i < n; i++)						\
3411
    for (i = 0; i < n; i++)						\
3412
	if (isExpression(str))						\
3412
	if (isExpression(str))						\
3413
	    REAL(ans)[i] = GExpression ## KIND(VECTOR_ELT(str, i),	\
3413
	    REAL(ans)[i] = GExpression ## KIND(VECTOR_ELT(str, i),	\
3414
					     GMapUnits(units), dd);	\
3414
					     GMapUnits(units), dd);	\
3415
	else {								\
3415
	else {								\
3416
	    ch = STRING_ELT(str, i);					\
3416
	    ch = STRING_ELT(str, i);					\
3417
	    REAL(ans)[i] = (ch == NA_STRING) ? 0.0 :			\
3417
	    REAL(ans)[i] = (ch == NA_STRING) ? 0.0 :			\
3418
		GStr ## KIND(CHAR(ch), GMapUnits(units), dd);		\
3418
		GStr ## KIND(CHAR(ch), GMapUnits(units), dd);		\
3419
	}								\
3419
	}								\
3420
    Rf_gpptr(dd)->cex = cexsave;					\
3420
    Rf_gpptr(dd)->cex = cexsave;					\
3421
    UNPROTECT(2);							\
3421
    UNPROTECT(2);							\
3422
    return ans;								\
3422
    return ans;								\
3423
}
3423
}
3424
 
3424
 
3425
SEXP attribute_hidden do_strheight(SEXP call, SEXP op, SEXP args, SEXP env)
3425
SEXP attribute_hidden do_strheight(SEXP call, SEXP op, SEXP args, SEXP env)
3426
DO_STR_DIM(Height)
3426
DO_STR_DIM(Height)
3427
 
3427
 
3428
SEXP attribute_hidden do_strwidth (SEXP call, SEXP op, SEXP args, SEXP env)
3428
SEXP attribute_hidden do_strwidth (SEXP call, SEXP op, SEXP args, SEXP env)
3429
DO_STR_DIM(Width)
3429
DO_STR_DIM(Width)
3430
 
3430
 
3431
#undef DO_STR_DIM
3431
#undef DO_STR_DIM
3432
 
3432
 
3433
 
3433
 
3434
static int *dnd_lptr;
3434
static int *dnd_lptr;
3435
static int *dnd_rptr;
3435
static int *dnd_rptr;
3436
static double *dnd_hght;
3436
static double *dnd_hght;
3437
static double *dnd_xpos;
3437
static double *dnd_xpos;
3438
static double dnd_hang;
3438
static double dnd_hang;
3439
static double dnd_offset;
3439
static double dnd_offset;
3440
 
3440
 
3441
static void drawdend(int node, double *x, double *y, SEXP dnd_llabels,
3441
static void drawdend(int node, double *x, double *y, SEXP dnd_llabels,
3442
		     DevDesc *dd)
3442
		     DevDesc *dd)
3443
{
3443
{
3444
/* Recursive function for 'hclust' dendrogram drawing:
3444
/* Recursive function for 'hclust' dendrogram drawing:
3445
 * Do left + Do right + Do myself
3445
 * Do left + Do right + Do myself
3446
 * "do" : 1) label leafs (if there are) and __
3446
 * "do" : 1) label leafs (if there are) and __
3447
 *        2) find coordinates to draw the  | |
3447
 *        2) find coordinates to draw the  | |
3448
 *        3) return (*x,*y) of "my anchor"
3448
 *        3) return (*x,*y) of "my anchor"
3449
 */
3449
 */
3450
    double xl, xr, yl, yr;
3450
    double xl, xr, yl, yr;
3451
    double xx[4], yy[4];
3451
    double xx[4], yy[4];
3452
    int k;
3452
    int k;
3453
 
3453
 
3454
    *y = dnd_hght[node-1];
3454
    *y = dnd_hght[node-1];
3455
    /* left part  */
3455
    /* left part  */
3456
    k = dnd_lptr[node-1];
3456
    k = dnd_lptr[node-1];
3457
    if (k > 0) drawdend(k, &xl, &yl, dnd_llabels, dd);
3457
    if (k > 0) drawdend(k, &xl, &yl, dnd_llabels, dd);
3458
    else {
3458
    else {
3459
	xl = dnd_xpos[-k-1];
3459
	xl = dnd_xpos[-k-1];
3460
	yl = (dnd_hang >= 0) ? *y - dnd_hang : 0;
3460
	yl = (dnd_hang >= 0) ? *y - dnd_hang : 0;
3461
	if(STRING_ELT(dnd_llabels, -k-1) != NA_STRING)
3461
	if(STRING_ELT(dnd_llabels, -k-1) != NA_STRING)
3462
	    GText(xl, yl-dnd_offset, USER, 
3462
	    GText(xl, yl-dnd_offset, USER, 
3463
		  translateChar(STRING_ELT(dnd_llabels, -k-1)),
3463
		  translateChar(STRING_ELT(dnd_llabels, -k-1)),
3464
		  1.0, 0.3, 90.0, dd);
3464
		  1.0, 0.3, 90.0, dd);
3465
    }
3465
    }
3466
    /* right part */
3466
    /* right part */
3467
    k = dnd_rptr[node-1];
3467
    k = dnd_rptr[node-1];
3468
    if (k > 0) drawdend(k, &xr, &yr, dnd_llabels, dd);
3468
    if (k > 0) drawdend(k, &xr, &yr, dnd_llabels, dd);
3469
    else {
3469
    else {
3470
	xr = dnd_xpos[-k-1];
3470
	xr = dnd_xpos[-k-1];
3471
	yr = (dnd_hang >= 0) ? *y - dnd_hang : 0;
3471
	yr = (dnd_hang >= 0) ? *y - dnd_hang : 0;
3472
	if(STRING_ELT(dnd_llabels, -k-1) != NA_STRING)
3472
	if(STRING_ELT(dnd_llabels, -k-1) != NA_STRING)
3473
	    GText(xr, yr-dnd_offset, USER, 
3473
	    GText(xr, yr-dnd_offset, USER, 
3474
		  translateChar(STRING_ELT(dnd_llabels, -k-1)),
3474
		  translateChar(STRING_ELT(dnd_llabels, -k-1)),
3475
		  1.0, 0.3, 90.0, dd);
3475
		  1.0, 0.3, 90.0, dd);
3476
    }
3476
    }
3477
    xx[0] = xl; yy[0] = yl;
3477
    xx[0] = xl; yy[0] = yl;
3478
    xx[1] = xl; yy[1] = *y;
3478
    xx[1] = xl; yy[1] = *y;
3479
    xx[2] = xr; yy[2] = *y;
3479
    xx[2] = xr; yy[2] = *y;
3480
    xx[3] = xr; yy[3] = yr;
3480
    xx[3] = xr; yy[3] = yr;
3481
    GPolyline(4, xx, yy, USER, dd);
3481
    GPolyline(4, xx, yy, USER, dd);
3482
    *x = 0.5 * (xl + xr);
3482
    *x = 0.5 * (xl + xr);
3483
}
3483
}
3484
 
3484
 
3485
 
3485
 
3486
SEXP attribute_hidden do_dend(SEXP call, SEXP op, SEXP args, SEXP env)
3486
SEXP attribute_hidden do_dend(SEXP call, SEXP op, SEXP args, SEXP env)
3487
{
3487
{
3488
    double x, y;
3488
    double x, y;
3489
    int n;
3489
    int n;
3490
 
3490
 
3491
    SEXP originalArgs, dnd_llabels, xpos;
3491
    SEXP originalArgs, dnd_llabels, xpos;
3492
    DevDesc *dd;
3492
    DevDesc *dd;
3493
 
3493
 
3494
    dd = CurrentDevice();
3494
    dd = CurrentDevice();
3495
    GCheckState(dd);
3495
    GCheckState(dd);
3496
 
3496
 
3497
    originalArgs = args;
3497
    originalArgs = args;
3498
    if (length(args) < 6)
3498
    if (length(args) < 6)
3499
	error(_("too few arguments"));
3499
	error(_("too few arguments"));
3500
 
3500
 
3501
    /* n */
3501
    /* n */
3502
    n = asInteger(CAR(args));
3502
    n = asInteger(CAR(args));
3503
    if (n == NA_INTEGER || n < 2)
3503
    if (n == NA_INTEGER || n < 2)
3504
	goto badargs;
3504
	goto badargs;
3505
    args = CDR(args);
3505
    args = CDR(args);
3506
 
3506
 
3507
    /* merge */
3507
    /* merge */
3508
    if (TYPEOF(CAR(args)) != INTSXP || length(CAR(args)) != 2*n)
3508
    if (TYPEOF(CAR(args)) != INTSXP || length(CAR(args)) != 2*n)
3509
	goto badargs;
3509
	goto badargs;
3510
    dnd_lptr = &(INTEGER(CAR(args))[0]);
3510
    dnd_lptr = &(INTEGER(CAR(args))[0]);
3511
    dnd_rptr = &(INTEGER(CAR(args))[n]);
3511
    dnd_rptr = &(INTEGER(CAR(args))[n]);
3512
    args = CDR(args);
3512
    args = CDR(args);
3513
 
3513
 
3514
    /* height */
3514
    /* height */
3515
    if (TYPEOF(CAR(args)) != REALSXP || length(CAR(args)) != n)
3515
    if (TYPEOF(CAR(args)) != REALSXP || length(CAR(args)) != n)
3516
	goto badargs;
3516
	goto badargs;
3517
    dnd_hght = REAL(CAR(args));
3517
    dnd_hght = REAL(CAR(args));
3518
    args = CDR(args);
3518
    args = CDR(args);
3519
 
3519
 
3520
    /* ord = order(x$order) */
3520
    /* ord = order(x$order) */
3521
    if (length(CAR(args)) != n+1)
3521
    if (length(CAR(args)) != n+1)
3522
	goto badargs;
3522
	goto badargs;
3523
    PROTECT(xpos = coerceVector(CAR(args), REALSXP));
3523
    PROTECT(xpos = coerceVector(CAR(args), REALSXP));
3524
    dnd_xpos = REAL(xpos);
3524
    dnd_xpos = REAL(xpos);
3525
    args = CDR(args);
3525
    args = CDR(args);
3526
 
3526
 
3527
    /* hang */
3527
    /* hang */
3528
    dnd_hang = asReal(CAR(args));
3528
    dnd_hang = asReal(CAR(args));
3529
    if (!R_FINITE(dnd_hang))
3529
    if (!R_FINITE(dnd_hang))
3530
	goto badargs;
3530
	goto badargs;
3531
    dnd_hang = dnd_hang * (dnd_hght[n-1] - dnd_hght[0]);
3531
    dnd_hang = dnd_hang * (dnd_hght[n-1] - dnd_hght[0]);
3532
    args = CDR(args);
3532
    args = CDR(args);
3533
 
3533
 
3534
    /* labels */
3534
    /* labels */
3535
    if (TYPEOF(CAR(args)) != STRSXP || length(CAR(args)) != n+1)
3535
    if (TYPEOF(CAR(args)) != STRSXP || length(CAR(args)) != n+1)
3536
	goto badargs;
3536
	goto badargs;
3537
    dnd_llabels = CAR(args);
3537
    dnd_llabels = CAR(args);
3538
    args = CDR(args);
3538
    args = CDR(args);
3539
 
3539
 
3540
    GSavePars(dd);
3540
    GSavePars(dd);
3541
    ProcessInlinePars(args, dd, call);
3541
    ProcessInlinePars(args, dd, call);
3542
    Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase * Rf_gpptr(dd)->cex;
3542
    Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase * Rf_gpptr(dd)->cex;
3543
    dnd_offset = GConvertYUnits(GStrWidth("m", INCHES, dd), INCHES, USER, dd);
3543
    dnd_offset = GConvertYUnits(GStrWidth("m", INCHES, dd), INCHES, USER, dd);
3544
 
3544
 
3545
    /* override par("xpd") and force clipping to figure region */
3545
    /* override par("xpd") and force clipping to figure region */
3546
    /* NOTE: don't override to _reduce_ clipping region */
3546
    /* NOTE: don't override to _reduce_ clipping region */
3547
    if (Rf_gpptr(dd)->xpd < 1)
3547
    if (Rf_gpptr(dd)->xpd < 1)
3548
	Rf_gpptr(dd)->xpd = 1;
3548
	Rf_gpptr(dd)->xpd = 1;
3549
 
3549
 
3550
    GMode(1, dd);
3550
    GMode(1, dd);
3551
    drawdend(n, &x, &y, dnd_llabels, dd);
3551
    drawdend(n, &x, &y, dnd_llabels, dd);
3552
    GMode(0, dd);
3552
    GMode(0, dd);
3553
    GRestorePars(dd);
3553
    GRestorePars(dd);
3554
    /* NOTE: only record operation if no "error"  */
3554
    /* NOTE: only record operation if no "error"  */
3555
    if (GRecording(call, dd))
3555
    if (GRecording(call, dd))
3556
	recordGraphicOperation(op, originalArgs, dd);
3556
	recordGraphicOperation(op, originalArgs, dd);
3557
    UNPROTECT(1);
3557
    UNPROTECT(1);
3558
    return R_NilValue;
3558
    return R_NilValue;
3559
 
3559
 
3560
  badargs:
3560
  badargs:
3561
    error(_("invalid dendrogram input"));
3561
    error(_("invalid dendrogram input"));
3562
    return R_NilValue;/* never used; to keep -Wall happy */
3562
    return R_NilValue;/* never used; to keep -Wall happy */
3563
}
3563
}
3564
 
3564
 
3565
SEXP attribute_hidden do_dendwindow(SEXP call, SEXP op, SEXP args, SEXP env)
3565
SEXP attribute_hidden do_dendwindow(SEXP call, SEXP op, SEXP args, SEXP env)
3566
{
3566
{
3567
    int i, imax, n;
3567
    int i, imax, n;
3568
    double pin, *ll, tmp, yval, *y, ymin, ymax, yrange, m;
3568
    double pin, *ll, tmp, yval, *y, ymin, ymax, yrange, m;
3569
    SEXP originalArgs, merge, height, llabels, str;
3569
    SEXP originalArgs, merge, height, llabels, str;
3570
    void *vmax;
3570
    void *vmax;
3571
    DevDesc *dd;
3571
    DevDesc *dd;
3572
 
3572
 
3573
    dd = CurrentDevice();
3573
    dd = CurrentDevice();
3574
    GCheckState(dd);
3574
    GCheckState(dd);
3575
    originalArgs = args;
3575
    originalArgs = args;
3576
    if (length(args) < 5)
3576
    if (length(args) < 5)
3577
	error(_("too few arguments"));
3577
	error(_("too few arguments"));
3578
    n = asInteger(CAR(args));
3578
    n = asInteger(CAR(args));
3579
    if (n == NA_INTEGER || n < 2)
3579
    if (n == NA_INTEGER || n < 2)
3580
	goto badargs;
3580
	goto badargs;
3581
    args = CDR(args);
3581
    args = CDR(args);
3582
    if (TYPEOF(CAR(args)) != INTSXP || length(CAR(args)) != 2 * n)
3582
    if (TYPEOF(CAR(args)) != INTSXP || length(CAR(args)) != 2 * n)
3583
	goto badargs;
3583
	goto badargs;
3584
    merge = CAR(args);
3584
    merge = CAR(args);
3585
 
3585
 
3586
    args = CDR(args);
3586
    args = CDR(args);
3587
    if (TYPEOF(CAR(args)) != REALSXP || length(CAR(args)) != n)
3587
    if (TYPEOF(CAR(args)) != REALSXP || length(CAR(args)) != n)
3588
	goto badargs;
3588
	goto badargs;
3589
    height = CAR(args);
3589
    height = CAR(args);
3590
 
3590
 
3591
    args = CDR(args);
3591
    args = CDR(args);
3592
    dnd_hang = asReal(CAR(args));
3592
    dnd_hang = asReal(CAR(args));
3593
    if (!R_FINITE(dnd_hang))
3593
    if (!R_FINITE(dnd_hang))
3594
	goto badargs;
3594
	goto badargs;
3595
 
3595
 
3596
    args = CDR(args);
3596
    args = CDR(args);
3597
    if (TYPEOF(CAR(args)) != STRSXP || length(CAR(args)) != n + 1)
3597
    if (TYPEOF(CAR(args)) != STRSXP || length(CAR(args)) != n + 1)
3598
	goto badargs;
3598
	goto badargs;
3599
    llabels = CAR(args);
3599
    llabels = CAR(args);
3600
 
3600
 
3601
    args = CDR(args);
3601
    args = CDR(args);
3602
    GSavePars(dd);
3602
    GSavePars(dd);
3603
    ProcessInlinePars(args, dd, call);
3603
    ProcessInlinePars(args, dd, call);
3604
    Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase * Rf_gpptr(dd)->cex;
3604
    Rf_gpptr(dd)->cex = Rf_gpptr(dd)->cexbase * Rf_gpptr(dd)->cex;
3605
    dnd_offset = GStrWidth("m", INCHES, dd);
3605
    dnd_offset = GStrWidth("m", INCHES, dd);
3606
    vmax = vmaxget();
3606
    vmax = vmaxget();
3607
    /* n is the number of merges, so the points are labelled 1 ... n+1 */
3607
    /* n is the number of merges, so the points are labelled 1 ... n+1 */
3608
    y =  (double*)R_alloc(n+1, sizeof(double));
3608
    y =  (double*)R_alloc(n+1, sizeof(double));
3609
    ll = (double*)R_alloc(n+1, sizeof(double));
3609
    ll = (double*)R_alloc(n+1, sizeof(double));
3610
    dnd_lptr = &(INTEGER(merge)[0]);
3610
    dnd_lptr = &(INTEGER(merge)[0]);
3611
    dnd_rptr = &(INTEGER(merge)[n]);
3611
    dnd_rptr = &(INTEGER(merge)[n]);
3612
    ymax = ymin = REAL(height)[0];
3612
    ymax = ymin = REAL(height)[0];
3613
    for (i = 1; i < n; i++) {
3613
    for (i = 1; i < n; i++) {
3614
	m = REAL(height)[i];
3614
	m = REAL(height)[i];
3615
	if (m > ymax)
3615
	if (m > ymax)
3616
	    ymax = m;
3616
	    ymax = m;
3617
	else if (m < ymin)
3617
	else if (m < ymin)
3618
	    ymin = m;
3618
	    ymin = m;
3619
    }
3619
    }
3620
    pin = Rf_gpptr(dd)->pin[1];
3620
    pin = Rf_gpptr(dd)->pin[1];
3621
    for (i = 0; i <= n; i++) {
3621
    for (i = 0; i <= n; i++) {
3622
	str = STRING_ELT(llabels, i);
3622
	str = STRING_ELT(llabels, i);
3623
	ll[i] = (str == NA_STRING) ? 0.0 :
3623
	ll[i] = (str == NA_STRING) ? 0.0 :
3624
	    GStrWidth(translateChar(str), INCHES, dd) + dnd_offset;
3624
	    GStrWidth(translateChar(str), INCHES, dd) + dnd_offset;
3625
    }
3625
    }
3626
 
3626
 
3627
    imax = -1; yval = -DBL_MAX;
3627
    imax = -1; yval = -DBL_MAX;
3628
    if (dnd_hang >= 0) {
3628
    if (dnd_hang >= 0) {
3629
	ymin = ymax - (1 + dnd_hang) * (ymax - ymin);
3629
	ymin = ymax - (1 + dnd_hang) * (ymax - ymin);
3630
	yrange = ymax - ymin;
3630
	yrange = ymax - ymin;
3631
	/* determine leaf heights */
3631
	/* determine leaf heights */
3632
	for (i = 0; i < n; i++) {
3632
	for (i = 0; i < n; i++) {
3633
	    if (dnd_lptr[i] < 0)
3633
	    if (dnd_lptr[i] < 0)
3634
		y[-dnd_lptr[i] - 1] = REAL(height)[i];
3634
		y[-dnd_lptr[i] - 1] = REAL(height)[i];
3635
	    if (dnd_rptr[i] < 0)
3635
	    if (dnd_rptr[i] < 0)
3636
		y[-dnd_rptr[i] - 1] = REAL(height)[i];
3636
		y[-dnd_rptr[i] - 1] = REAL(height)[i];
3637
	}
3637
	}
3638
	/* determine the most extreme label depth */
3638
	/* determine the most extreme label depth */
3639
	/* assuming that we are using the full plot */
3639
	/* assuming that we are using the full plot */
3640
	/* window for the tree itself */
3640
	/* window for the tree itself */
3641
	for (i = 0; i <= n; i++) {
3641
	for (i = 0; i <= n; i++) {
3642
	    tmp = ((ymax - y[i]) / yrange) * pin + ll[i];
3642
	    tmp = ((ymax - y[i]) / yrange) * pin + ll[i];
3643
	    if (tmp > yval) {
3643
	    if (tmp > yval) {
3644
		yval = tmp;
3644
		yval = tmp;
3645
		imax = i;
3645
		imax = i;
3646
	    }
3646
	    }
3647
	}
3647
	}
3648
    }
3648
    }
3649
    else {
3649
    else {
3650
	yrange = ymax;
3650
	yrange = ymax;
3651
	for (i = 0; i <= n; i++) {
3651
	for (i = 0; i <= n; i++) {
3652
	    tmp = pin + ll[i];
3652
	    tmp = pin + ll[i];
3653
	    if (tmp > yval) {
3653
	    if (tmp > yval) {
3654
		yval = tmp;
3654
		yval = tmp;
3655
		imax = i;
3655
		imax = i;
3656
	    }
3656
	    }
3657
	}
3657
	}
3658
    }
3658
    }
3659
    /* now determine how much to scale */
3659
    /* now determine how much to scale */
3660
    ymin = ymax - (pin/(pin - ll[imax])) * yrange;
3660
    ymin = ymax - (pin/(pin - ll[imax])) * yrange;
3661
    GScale(1.0, n+1.0, 1 /* x */, dd);
3661
    GScale(1.0, n+1.0, 1 /* x */, dd);
3662
    GScale(ymin, ymax, 2 /* y */, dd);
3662
    GScale(ymin, ymax, 2 /* y */, dd);
3663
    GMapWin2Fig(dd);
3663
    GMapWin2Fig(dd);
3664
    GRestorePars(dd);
3664
    GRestorePars(dd);
3665
    /* NOTE: only record operation if no "error"  */
3665
    /* NOTE: only record operation if no "error"  */
3666
    if (GRecording(call, dd))
3666
    if (GRecording(call, dd))
3667
	recordGraphicOperation(op, originalArgs, dd);
3667
	recordGraphicOperation(op, originalArgs, dd);
3668
    vmaxset(vmax);
3668
    vmaxset(vmax);
3669
    return R_NilValue;
3669
    return R_NilValue;
3670
  badargs:
3670
  badargs:
3671
    error(_("invalid dendrogram input"));
3671
    error(_("invalid dendrogram input"));
3672
    return R_NilValue;/* never used; to keep -Wall happy */
3672
    return R_NilValue;/* never used; to keep -Wall happy */
3673
}
3673
}
3674
 
3674
 
3675
 
3675
 
3676
SEXP attribute_hidden do_erase(SEXP call, SEXP op, SEXP args, SEXP env)
3676
SEXP attribute_hidden do_erase(SEXP call, SEXP op, SEXP args, SEXP env)
3677
{
3677
{
3678
    SEXP col;
3678
    SEXP col;
3679
    int ncol;
3679
    int ncol;
3680
    DevDesc *dd = CurrentDevice();
3680
    DevDesc *dd = CurrentDevice();
3681
    checkArity(op, args);
3681
    checkArity(op, args);
3682
    PROTECT(col = FixupCol(CAR(args), R_TRANWHITE));
3682
    PROTECT(col = FixupCol(CAR(args), R_TRANWHITE));
3683
    ncol = LENGTH(col);
3683
    ncol = LENGTH(col);
3684
    GSavePars(dd);
3684
    GSavePars(dd);
3685
    GMode(1, dd);
3685
    GMode(1, dd);
3686
    GRect(0.0, 0.0, 1.0, 1.0, NDC, INTEGER(col)[0], R_TRANWHITE, dd);
3686
    GRect(0.0, 0.0, 1.0, 1.0, NDC, INTEGER(col)[0], R_TRANWHITE, dd);
3687
    GMode(0, dd);
3687
    GMode(0, dd);
3688
    GRestorePars(dd);
3688
    GRestorePars(dd);
3689
    UNPROTECT(1);
3689
    UNPROTECT(1);
3690
    return R_NilValue;
3690
    return R_NilValue;
3691
}
3691
}
3692
 
3692
 
3693
 
3693
 
3694
SEXP attribute_hidden do_getSnapshot(SEXP call, SEXP op, SEXP args, SEXP env)
3694
SEXP attribute_hidden do_getSnapshot(SEXP call, SEXP op, SEXP args, SEXP env)
3695
{
3695
{
3696
    DevDesc *dd = CurrentDevice();
3696
    DevDesc *dd = CurrentDevice();
3697
 
3697
 
3698
    checkArity(op, args);
3698
    checkArity(op, args);
3699
    if (dd->newDevStruct) {
3699
    if (dd->newDevStruct) {
3700
	return GEcreateSnapshot((GEDevDesc*) dd);
3700
	return GEcreateSnapshot((GEDevDesc*) dd);
3701
    } else {
3701
    } else {
3702
	error(_("cannot take snapshot of old-style device"));
3702
	error(_("cannot take snapshot of old-style device"));
3703
	return R_NilValue;
3703
	return R_NilValue;
3704
    }
3704
    }
3705
}
3705
}
3706
 
3706
 
3707
SEXP attribute_hidden do_playSnapshot(SEXP call, SEXP op, SEXP args, SEXP env)
3707
SEXP attribute_hidden do_playSnapshot(SEXP call, SEXP op, SEXP args, SEXP env)
3708
{
3708
{
3709
    DevDesc *dd = CurrentDevice();
3709
    DevDesc *dd = CurrentDevice();
3710
 
3710
 
3711
    checkArity(op, args);
3711
    checkArity(op, args);
3712
    if (dd->newDevStruct)
3712
    if (dd->newDevStruct)
3713
	GEplaySnapshot(CAR(args), (GEDevDesc*) dd);
3713
	GEplaySnapshot(CAR(args), (GEDevDesc*) dd);
3714
    else
3714
    else
3715
	error(_("cannot play snapshot on old-style device"));
3715
	error(_("cannot play snapshot on old-style device"));
3716
    return R_NilValue;
3716
    return R_NilValue;
3717
}
3717
}
3718
 
3718
 
3719
/* I don't think this gets called in any base R code
3719
/* I don't think this gets called in any base R code
3720
 */
3720
 */
3721
SEXP attribute_hidden do_replay(SEXP call, SEXP op, SEXP args, SEXP env)
3721
SEXP attribute_hidden do_replay(SEXP call, SEXP op, SEXP args, SEXP env)
3722
{
3722
{
3723
    if (!NoDevices()) {
3723
    if (!NoDevices()) {
3724
	GEDevDesc *dd = GEcurrentDevice();
3724
	GEDevDesc *dd = GEcurrentDevice();
3725
	checkArity(op, args);
3725
	checkArity(op, args);
3726
	/*     Rf_dpptr(dd)->resize(); */
3726
	/*     Rf_dpptr(dd)->resize(); */
3727
	GEplayDisplayList(dd);
3727
	GEplayDisplayList(dd);
3728
    }
3728
    }
3729
    return R_NilValue;
3729
    return R_NilValue;
3730
}
3730
}
3731
 
3731
 
3732
SEXP attribute_hidden do_playDL(SEXP call, SEXP op, SEXP args, SEXP env)
3732
SEXP attribute_hidden do_playDL(SEXP call, SEXP op, SEXP args, SEXP env)
3733
{
3733
{
3734
    DevDesc *dd = CurrentDevice();
3734
    DevDesc *dd = CurrentDevice();
3735
    SEXP theList;
3735
    SEXP theList;
3736
    int ask;
3736
    int ask;
3737
 
3737
 
3738
    checkArity(op, args);
3738
    checkArity(op, args);
3739
    if(!isList(theList = CAR(args)))
3739
    if(!isList(theList = CAR(args)))
3740
       error(_("invalid argument"));
3740
       error(_("invalid argument"));
3741
    if (dd->newDevStruct)
3741
    if (dd->newDevStruct)
3742
	((GEDevDesc*) dd)->dev->displayList = theList;
3742
	((GEDevDesc*) dd)->dev->displayList = theList;
3743
    else
3743
    else
3744
	dd->displayList = theList;
3744
	dd->displayList = theList;
3745
    if (theList != R_NilValue) {
3745
    if (theList != R_NilValue) {
3746
	ask = Rf_gpptr(dd)->ask;
3746
	ask = Rf_gpptr(dd)->ask;
3747
	Rf_gpptr(dd)->ask = 1;
3747
	Rf_gpptr(dd)->ask = 1;
3748
	GReset(dd);
3748
	GReset(dd);
3749
	while (theList != R_NilValue) {
3749
	while (theList != R_NilValue) {
3750
	    SEXP theOperation = CAR(theList);
3750
	    SEXP theOperation = CAR(theList);
3751
	    SEXP l_op = CAR(theOperation);
3751
	    SEXP l_op = CAR(theOperation);
3752
	    SEXP l_args = CDR(theOperation);
3752
	    SEXP l_args = CDR(theOperation);
3753
	    PRIMFUN(l_op) (R_NilValue, l_op, l_args, R_NilValue);
3753
	    PRIMFUN(l_op) (R_NilValue, l_op, l_args, R_NilValue);
3754
	    if (!Rf_gpptr(dd)->valid) break;
3754
	    if (!Rf_gpptr(dd)->valid) break;
3755
	    theList = CDR(theList);
3755
	    theList = CDR(theList);
3756
	}
3756
	}
3757
	Rf_gpptr(dd)->ask = ask;
3757
	Rf_gpptr(dd)->ask = ask;
3758
    }
3758
    }
3759
    return R_NilValue;
3759
    return R_NilValue;
3760
}
3760
}
3761
 
3761
 
3762
SEXP attribute_hidden do_setGPar(SEXP call, SEXP op, SEXP args, SEXP env)
3762
SEXP attribute_hidden do_setGPar(SEXP call, SEXP op, SEXP args, SEXP env)
3763
{
3763
{
3764
    DevDesc *dd = CurrentDevice();
3764
    DevDesc *dd = CurrentDevice();
3765
    int lGPar = 1 + sizeof(GPar) / sizeof(int);
3765
    int lGPar = 1 + sizeof(GPar) / sizeof(int);
3766
    SEXP GP;
3766
    SEXP GP;
3767
 
3767
 
3768
    checkArity(op, args);
3768
    checkArity(op, args);
3769
    GP = CAR(args);
3769
    GP = CAR(args);
3770
    if (!isInteger(GP) || length(GP) != lGPar)
3770
    if (!isInteger(GP) || length(GP) != lGPar)
3771
	error(_("invalid graphics parameter list"));
3771
	error(_("invalid graphics parameter list"));
3772
    copyGPar((GPar *) INTEGER(GP), Rf_dpSavedptr(dd)); /* &dd->Rf_dpSaved); */
3772
    copyGPar((GPar *) INTEGER(GP), Rf_dpSavedptr(dd)); /* &dd->Rf_dpSaved); */
3773
    return R_NilValue;
3773
    return R_NilValue;
3774
}
3774
}
3775
 
3775
 
3776
/* symbols(..) in ../library/base/R/symbols.R  : */
3776
/* symbols(..) in ../library/base/R/symbols.R  : */
3777
 
3777
 
3778
/* utility just computing range() */
3778
/* utility just computing range() */
3779
static Rboolean SymbolRange(double *x, int n, double *xmax, double *xmin)
3779
static Rboolean SymbolRange(double *x, int n, double *xmax, double *xmin)
3780
{
3780
{
3781
    int i;
3781
    int i;
3782
    *xmax = -DBL_MAX;
3782
    *xmax = -DBL_MAX;
3783
    *xmin =  DBL_MAX;
3783
    *xmin =  DBL_MAX;
3784
    for(i = 0; i < n; i++)
3784
    for(i = 0; i < n; i++)
3785
	if (R_FINITE(x[i])) {
3785
	if (R_FINITE(x[i])) {
3786
	    if (*xmax < x[i]) *xmax = x[i];
3786
	    if (*xmax < x[i]) *xmax = x[i];
3787
	    if (*xmin > x[i]) *xmin = x[i];
3787
	    if (*xmin > x[i]) *xmin = x[i];
3788
	}
3788
	}
3789
    return(*xmax >= *xmin && *xmin >= 0);
3789
    return(*xmax >= *xmin && *xmin >= 0);
3790
}
3790
}
3791
 
3791
 
3792
static void CheckSymbolPar(SEXP call, SEXP p, int *nr, int *nc)
3792
static void CheckSymbolPar(SEXP call, SEXP p, int *nr, int *nc)
3793
{
3793
{
3794
    SEXP dim = getAttrib(p, R_DimSymbol);
3794
    SEXP dim = getAttrib(p, R_DimSymbol);
3795
    switch(length(dim)) {
3795
    switch(length(dim)) {
3796
    case 0:
3796
    case 0:
3797
	*nr = LENGTH(p);
3797
	*nr = LENGTH(p);
3798
	*nc = 1;
3798
	*nc = 1;
3799
	break;
3799
	break;
3800
    case 1:
3800
    case 1:
3801
	*nr = INTEGER(dim)[0];
3801
	*nr = INTEGER(dim)[0];
3802
	*nc = 1;
3802
	*nc = 1;
3803
	break;
3803
	break;
3804
    case 2:
3804
    case 2:
3805
	*nr = INTEGER(dim)[0];
3805
	*nr = INTEGER(dim)[0];
3806
	*nc = INTEGER(dim)[1];
3806
	*nc = INTEGER(dim)[1];
3807
	break;
3807
	break;
3808
    default:
3808
    default:
3809
	*nr = 0;
3809
	*nr = 0;
3810
	*nc = 0;
3810
	*nc = 0;
3811
    }
3811
    }
3812
    if (*nr == 0 || *nc == 0)
3812
    if (*nr == 0 || *nc == 0)
3813
	error(_("invalid symbol parameter vector"));
3813
	error(_("invalid symbol parameter vector"));
3814
}
3814
}
3815
 
3815
 
3816
/* Internal  symbols(x, y, type, data, inches, bg, fg, ...) */
3816
/* Internal  symbols(x, y, type, data, inches, bg, fg, ...) */
3817
SEXP attribute_hidden do_symbols(SEXP call, SEXP op, SEXP args, SEXP env)
3817
SEXP attribute_hidden do_symbols(SEXP call, SEXP op, SEXP args, SEXP env)
3818
{
3818
{
3819
    SEXP x, y, p, fg, bg;
3819
    SEXP x, y, p, fg, bg;
3820
    int i, j, nr, nc, nbg, nfg, type;
3820
    int i, j, nr, nc, nbg, nfg, type;
3821
    double pmax, pmin, inches, rx, ry;
3821
    double pmax, pmin, inches, rx, ry;
3822
    double xx, yy, p0, p1, p2, p3, p4;
3822
    double xx, yy, p0, p1, p2, p3, p4;
3823
    double *pp, *xp, *yp;
3823
    double *pp, *xp, *yp;
3824
    void *vmax;
3824
    void *vmax;
3825
 
3825
 
3826
    SEXP originalArgs = args;
3826
    SEXP originalArgs = args;
3827
    DevDesc *dd = CurrentDevice();
3827
    DevDesc *dd = CurrentDevice();
3828
    GCheckState(dd);
3828
    GCheckState(dd);
3829
 
3829
 
3830
    if (length(args) < 7)
3830
    if (length(args) < 7)
3831
	error(_("too few arguments"));
3831
	error(_("too few arguments"));
3832
 
3832
 
3833
    PROTECT(x = coerceVector(CAR(args), REALSXP)); args = CDR(args);
3833
    PROTECT(x = coerceVector(CAR(args), REALSXP)); args = CDR(args);
3834
    PROTECT(y = coerceVector(CAR(args), REALSXP)); args = CDR(args);
3834
    PROTECT(y = coerceVector(CAR(args), REALSXP)); args = CDR(args);
3835
    if (!isNumeric(x) || !isNumeric(y) || length(x) <= 0 || LENGTH(x) <= 0)
3835
    if (!isNumeric(x) || !isNumeric(y) || length(x) <= 0 || LENGTH(x) <= 0)
3836
	error(_("invalid symbol coordinates"));
3836
	error(_("invalid symbol coordinates"));
3837
 
3837
 
3838
    type = asInteger(CAR(args)); args = CDR(args);
3838
    type = asInteger(CAR(args)); args = CDR(args);
3839
 
3839
 
3840
    /* data: */
3840
    /* data: */
3841
    p = PROTECT(coerceVector(CAR(args), REALSXP)); args = CDR(args);
3841
    p = PROTECT(coerceVector(CAR(args), REALSXP)); args = CDR(args);
3842
    CheckSymbolPar(call, p, &nr, &nc);
3842
    CheckSymbolPar(call, p, &nr, &nc);
3843
    if (LENGTH(x) != nr || LENGTH(y) != nr)
3843
    if (LENGTH(x) != nr || LENGTH(y) != nr)
3844
	error(_("x/y/parameter length mismatch"));
3844
	error(_("x/y/parameter length mismatch"));
3845
 
3845
 
3846
    inches = asReal(CAR(args)); args = CDR(args);
3846
    inches = asReal(CAR(args)); args = CDR(args);
3847
    if (!R_FINITE(inches) || inches < 0)
3847
    if (!R_FINITE(inches) || inches < 0)
3848
	inches = 0;
3848
	inches = 0;
3849
 
3849
 
3850
    PROTECT(bg = FixupCol(CAR(args), R_TRANWHITE)); args = CDR(args);
3850
    PROTECT(bg = FixupCol(CAR(args), R_TRANWHITE)); args = CDR(args);
3851
    nbg = LENGTH(bg);
3851
    nbg = LENGTH(bg);
3852
 
3852
 
3853
    PROTECT(fg = FixupCol(CAR(args), R_TRANWHITE)); args = CDR(args);
3853
    PROTECT(fg = FixupCol(CAR(args), R_TRANWHITE)); args = CDR(args);
3854
    nfg = LENGTH(fg);
3854
    nfg = LENGTH(fg);
3855
 
3855
 
3856
    GSavePars(dd);
3856
    GSavePars(dd);
3857
    ProcessInlinePars(args, dd, call);
3857
    ProcessInlinePars(args, dd, call);
3858
 
3858
 
3859
    GMode(1, dd);
3859
    GMode(1, dd);
3860
    switch (type) {
3860
    switch (type) {
3861
    case 1: /* circles */
3861
    case 1: /* circles */
3862
	if (nc != 1)
3862
	if (nc != 1)
3863
	    error(_("invalid circles data"));
3863
	    error(_("invalid circles data"));
3864
	if (!SymbolRange(REAL(p), nr, &pmax, &pmin))
3864
	if (!SymbolRange(REAL(p), nr, &pmax, &pmin))
3865
	    error(_("invalid symbol parameter"));
3865
	    error(_("invalid symbol parameter"));
3866
	for (i = 0; i < nr; i++) {
3866
	for (i = 0; i < nr; i++) {
3867
	    if (R_FINITE(REAL(x)[i]) && R_FINITE(REAL(y)[i]) &&
3867
	    if (R_FINITE(REAL(x)[i]) && R_FINITE(REAL(y)[i]) &&
3868
		R_FINITE(REAL(p)[i])) {
3868
		R_FINITE(REAL(p)[i])) {
3869
		rx = REAL(p)[i];
3869
		rx = REAL(p)[i];
3870
		/* For GCircle the radius is always in INCHES */
3870
		/* For GCircle the radius is always in INCHES */
3871
		if (inches > 0)
3871
		if (inches > 0)
3872
		    rx *= inches / pmax;
3872
		    rx *= inches / pmax;
3873
		else
3873
		else
3874
		    rx = GConvertXUnits(rx, USER, INCHES, dd);
3874
		    rx = GConvertXUnits(rx, USER, INCHES, dd);
3875
		GCircle(REAL(x)[i], REAL(y)[i],	USER, rx,
3875
		GCircle(REAL(x)[i], REAL(y)[i],	USER, rx,
3876
			INTEGER(bg)[i%nbg], INTEGER(fg)[i%nfg],	dd);
3876
			INTEGER(bg)[i%nbg], INTEGER(fg)[i%nfg],	dd);
3877
	    }
3877
	    }
3878
	}
3878
	}
3879
	break;
3879
	break;
3880
    case 2: /* squares */
3880
    case 2: /* squares */
3881
	if(nc != 1)
3881
	if(nc != 1)
3882
	    error(_("invalid squares data"));
3882
	    error(_("invalid squares data"));
3883
	if(!SymbolRange(REAL(p), nr, &pmax, &pmin))
3883
	if(!SymbolRange(REAL(p), nr, &pmax, &pmin))
3884
	    error(_("invalid symbol parameter"));
3884
	    error(_("invalid symbol parameter"));
3885
	for (i = 0; i < nr; i++) {
3885
	for (i = 0; i < nr; i++) {
3886
	    if (R_FINITE(REAL(x)[i]) && R_FINITE(REAL(y)[i]) &&
3886
	    if (R_FINITE(REAL(x)[i]) && R_FINITE(REAL(y)[i]) &&
3887
		R_FINITE(REAL(p)[i])) {
3887
		R_FINITE(REAL(p)[i])) {
3888
		p0 = REAL(p)[i];
3888
		p0 = REAL(p)[i];
3889
		xx = REAL(x)[i];
3889
		xx = REAL(x)[i];
3890
		yy = REAL(y)[i];
3890
		yy = REAL(y)[i];
3891
		GConvert(&xx, &yy, USER, DEVICE, dd);
3891
		GConvert(&xx, &yy, USER, DEVICE, dd);
3892
		if (inches > 0) {
3892
		if (inches > 0) {
3893
		    p0 *= inches / pmax;
3893
		    p0 *= inches / pmax;
3894
		    rx = GConvertXUnits(0.5 * p0, INCHES, DEVICE, dd);
3894
		    rx = GConvertXUnits(0.5 * p0, INCHES, DEVICE, dd);
3895
		}
3895
		}
3896
		else {
3896
		else {
3897
		    rx = GConvertXUnits(0.5 * p0, USER, DEVICE, dd);
3897
		    rx = GConvertXUnits(0.5 * p0, USER, DEVICE, dd);
3898
		}
3898
		}
3899
		GRect(xx - rx, yy - rx, xx + rx, yy + rx, DEVICE,
3899
		GRect(xx - rx, yy - rx, xx + rx, yy + rx, DEVICE,
3900
		      INTEGER(bg)[i%nbg], INTEGER(fg)[i%nfg], dd);
3900
		      INTEGER(bg)[i%nbg], INTEGER(fg)[i%nfg], dd);
3901
	    }
3901
	    }
3902
	}
3902
	}
3903
	break;
3903
	break;
3904
    case 3: /* rectangles */
3904
    case 3: /* rectangles */
3905
	if (nc != 2)
3905
	if (nc != 2)
3906
	    error(_("invalid rectangles data (need 2 columns)"));
3906
	    error(_("invalid rectangles data (need 2 columns)"));
3907
	if (!SymbolRange(REAL(p), 2 * nr, &pmax, &pmin))
3907
	if (!SymbolRange(REAL(p), 2 * nr, &pmax, &pmin))
3908
	    error(_("invalid symbol parameter"));
3908
	    error(_("invalid symbol parameter"));
3909
	for (i = 0; i < nr; i++) {
3909
	for (i = 0; i < nr; i++) {
3910
	    if (R_FINITE(REAL(x)[i]) && R_FINITE(REAL(y)[i]) &&
3910
	    if (R_FINITE(REAL(x)[i]) && R_FINITE(REAL(y)[i]) &&
3911
		R_FINITE(REAL(p)[i]) && R_FINITE(REAL(p)[i+nr])) {
3911
		R_FINITE(REAL(p)[i]) && R_FINITE(REAL(p)[i+nr])) {
3912
		xx = REAL(x)[i];
3912
		xx = REAL(x)[i];
3913
		yy = REAL(y)[i];
3913
		yy = REAL(y)[i];
3914
		GConvert(&xx, &yy, USER, DEVICE, dd);
3914
		GConvert(&xx, &yy, USER, DEVICE, dd);
3915
		p0 = REAL(p)[i];
3915
		p0 = REAL(p)[i];
3916
		p1 = REAL(p)[i+nr];
3916
		p1 = REAL(p)[i+nr];
3917
		if (inches > 0) {
3917
		if (inches > 0) {
3918
		    p0 *= inches / pmax;
3918
		    p0 *= inches / pmax;
3919
		    p1 *= inches / pmax;
3919
		    p1 *= inches / pmax;
3920
		    rx = GConvertXUnits(0.5 * p0, INCHES, DEVICE, dd);
3920
		    rx = GConvertXUnits(0.5 * p0, INCHES, DEVICE, dd);
3921
		    ry = GConvertYUnits(0.5 * p1, INCHES, DEVICE, dd);
3921
		    ry = GConvertYUnits(0.5 * p1, INCHES, DEVICE, dd);
3922
		}
3922
		}
3923
		else {
3923
		else {
3924
		    rx = GConvertXUnits(0.5 * p0, USER, DEVICE, dd);
3924
		    rx = GConvertXUnits(0.5 * p0, USER, DEVICE, dd);
3925
		    ry = GConvertYUnits(0.5 * p1, USER, DEVICE, dd);
3925
		    ry = GConvertYUnits(0.5 * p1, USER, DEVICE, dd);
3926
		}
3926
		}
3927
		GRect(xx - rx, yy - ry, xx + rx, yy + ry, DEVICE,
3927
		GRect(xx - rx, yy - ry, xx + rx, yy + ry, DEVICE,
3928
		      INTEGER(bg)[i%nbg], INTEGER(fg)[i%nfg], dd);
3928
		      INTEGER(bg)[i%nbg], INTEGER(fg)[i%nfg], dd);
3929
 
3929
 
3930
	    }
3930
	    }
3931
	}
3931
	}
3932
	break;
3932
	break;
3933
    case 4: /* stars */
3933
    case 4: /* stars */
3934
	if (nc < 3)
3934
	if (nc < 3)
3935
	    error(_("invalid stars data"));
3935
	    error(_("invalid stars data"));
3936
	if (!SymbolRange(REAL(p), nc * nr, &pmax, &pmin))
3936
	if (!SymbolRange(REAL(p), nc * nr, &pmax, &pmin))
3937
	    error(_("invalid symbol parameter"));
3937
	    error(_("invalid symbol parameter"));
3938
	vmax = vmaxget();
3938
	vmax = vmaxget();
3939
	pp = (double*)R_alloc(nc, sizeof(double));
3939
	pp = (double*)R_alloc(nc, sizeof(double));
3940
	xp = (double*)R_alloc(nc, sizeof(double));
3940
	xp = (double*)R_alloc(nc, sizeof(double));
3941
	yp = (double*)R_alloc(nc, sizeof(double));
3941
	yp = (double*)R_alloc(nc, sizeof(double));
3942
	p1 = 2.0 * M_PI / nc;
3942
	p1 = 2.0 * M_PI / nc;
3943
	for (i = 0; i < nr; i++) {
3943
	for (i = 0; i < nr; i++) {
3944
	    xx = REAL(x)[i];
3944
	    xx = REAL(x)[i];
3945
	    yy = REAL(y)[i];
3945
	    yy = REAL(y)[i];
3946
	    if (R_FINITE(xx) && R_FINITE(yy)) {
3946
	    if (R_FINITE(xx) && R_FINITE(yy)) {
3947
		GConvert(&xx, &yy, USER, NDC, dd);
3947
		GConvert(&xx, &yy, USER, NDC, dd);
3948
		if (inches > 0) {
3948
		if (inches > 0) {
3949
		    for(j = 0; j < nc; j++) {
3949
		    for(j = 0; j < nc; j++) {
3950
			p0 = REAL(p)[i + j * nr];
3950
			p0 = REAL(p)[i + j * nr];
3951
			if (!R_FINITE(p0)) p0 = 0;
3951
			if (!R_FINITE(p0)) p0 = 0;
3952
			pp[j] = (p0 / pmax) * inches;
3952
			pp[j] = (p0 / pmax) * inches;
3953
		    }
3953
		    }
3954
		}
3954
		}
3955
		else {
3955
		else {
3956
		    for(j = 0; j < nc; j++) {
3956
		    for(j = 0; j < nc; j++) {
3957
			p0 = REAL(p)[i + j * nr];
3957
			p0 = REAL(p)[i + j * nr];
3958
			if (!R_FINITE(p0)) p0 = 0;
3958
			if (!R_FINITE(p0)) p0 = 0;
3959
			pp[j] =	 GConvertXUnits(p0, USER, INCHES, dd);
3959
			pp[j] =	 GConvertXUnits(p0, USER, INCHES, dd);
3960
		    }
3960
		    }
3961
		}
3961
		}
3962
		for(j = 0; j < nc; j++) {
3962
		for(j = 0; j < nc; j++) {
3963
		    xp[j] = GConvertXUnits(pp[j] * cos(j * p1),
3963
		    xp[j] = GConvertXUnits(pp[j] * cos(j * p1),
3964
					   INCHES, NDC, dd) + xx;
3964
					   INCHES, NDC, dd) + xx;
3965
		    yp[j] = GConvertYUnits(pp[j] * sin(j * p1),
3965
		    yp[j] = GConvertYUnits(pp[j] * sin(j * p1),
3966
					   INCHES, NDC, dd) + yy;
3966
					   INCHES, NDC, dd) + yy;
3967
		}
3967
		}
3968
		GPolygon(nc, xp, yp, NDC,
3968
		GPolygon(nc, xp, yp, NDC,
3969
			 INTEGER(bg)[i%nbg], INTEGER(fg)[i%nfg], dd);
3969
			 INTEGER(bg)[i%nbg], INTEGER(fg)[i%nfg], dd);
3970
	    }
3970
	    }
3971
	}
3971
	}
3972
	vmaxset(vmax);
3972
	vmaxset(vmax);
3973
	break;
3973
	break;
3974
    case 5: /* thermometers */
3974
    case 5: /* thermometers */
3975
	if (nc != 3 && nc != 4)
3975
	if (nc != 3 && nc != 4)
3976
	    error(_("invalid thermometers data (need 3 or 4 columns)"));
3976
	    error(_("invalid thermometers data (need 3 or 4 columns)"));
3977
	SymbolRange(REAL(p)+2*nr/* <-- pointer arith*/, nr, &pmax, &pmin);
3977
	SymbolRange(REAL(p)+2*nr/* <-- pointer arith*/, nr, &pmax, &pmin);
3978
	if (pmax < pmin)
3978
	if (pmax < pmin)
3979
	    error(_("invalid thermometers[,%s]"),
3979
	    error(_("invalid thermometers[,%s]"),
3980
		      (nc == 4)? "3:4" : "3");
3980
		      (nc == 4)? "3:4" : "3");
3981
	if (pmin < 0. || pmax > 1.) /* S-PLUS has an error here */
3981
	if (pmin < 0. || pmax > 1.) /* S-PLUS has an error here */
3982
	    warning(_("thermometers[,%s] not in [0,1] -- may look funny"),
3982
	    warning(_("thermometers[,%s] not in [0,1] -- may look funny"),
3983
		    (nc == 4)? "3:4" : "3");
3983
		    (nc == 4)? "3:4" : "3");
3984
	if (!SymbolRange(REAL(p), 2 * nr, &pmax, &pmin))
3984
	if (!SymbolRange(REAL(p), 2 * nr, &pmax, &pmin))
3985
	    error(_("invalid thermometers[,1:2]"));
3985
	    error(_("invalid thermometers[,1:2]"));
3986
	for (i = 0; i < nr; i++) {
3986
	for (i = 0; i < nr; i++) {
3987
	    xx = REAL(x)[i];
3987
	    xx = REAL(x)[i];
3988
	    yy = REAL(y)[i];
3988
	    yy = REAL(y)[i];
3989
	    if (R_FINITE(xx) && R_FINITE(yy)) {
3989
	    if (R_FINITE(xx) && R_FINITE(yy)) {
3990
		p0 = REAL(p)[i];
3990
		p0 = REAL(p)[i];
3991
		p1 = REAL(p)[i + nr];
3991
		p1 = REAL(p)[i + nr];
3992
		p2 = REAL(p)[i + 2 * nr];
3992
		p2 = REAL(p)[i + 2 * nr];
3993
		p3 = (nc == 4)? REAL(p)[i + 3 * nr] : 0.;
3993
		p3 = (nc == 4)? REAL(p)[i + 3 * nr] : 0.;
3994
		if (R_FINITE(p0) && R_FINITE(p1) &&
3994
		if (R_FINITE(p0) && R_FINITE(p1) &&
3995
		    R_FINITE(p2) && R_FINITE(p3)) {
3995
		    R_FINITE(p2) && R_FINITE(p3)) {
3996
		    if (p2 < 0) p2 = 0; else if (p2 > 1) p2 = 1;
3996
		    if (p2 < 0) p2 = 0; else if (p2 > 1) p2 = 1;
3997
		    if (p3 < 0) p3 = 0; else if (p3 > 1) p3 = 1;
3997
		    if (p3 < 0) p3 = 0; else if (p3 > 1) p3 = 1;
3998
		    GConvert(&xx, &yy, USER, NDC, dd);
3998
		    GConvert(&xx, &yy, USER, NDC, dd);
3999
		    if (inches > 0) {
3999
		    if (inches > 0) {
4000
			p0 *= inches / pmax;
4000
			p0 *= inches / pmax;
4001
			p1 *= inches / pmax;
4001
			p1 *= inches / pmax;
4002
			rx = GConvertXUnits(0.5 * p0, INCHES, NDC, dd);
4002
			rx = GConvertXUnits(0.5 * p0, INCHES, NDC, dd);
4003
			ry = GConvertYUnits(0.5 * p1, INCHES, NDC, dd);
4003
			ry = GConvertYUnits(0.5 * p1, INCHES, NDC, dd);
4004
		    }
4004
		    }
4005
		    else {
4005
		    else {
4006
			rx = GConvertXUnits(0.5 * p0, USER, NDC, dd);
4006
			rx = GConvertXUnits(0.5 * p0, USER, NDC, dd);
4007
			ry = GConvertYUnits(0.5 * p1, USER, NDC, dd);
4007
			ry = GConvertYUnits(0.5 * p1, USER, NDC, dd);
4008
		    }
4008
		    }
4009
		    GRect(xx - rx, yy - ry, xx + rx, yy + ry, NDC,
4009
		    GRect(xx - rx, yy - ry, xx + rx, yy + ry, NDC,
4010
			  INTEGER(bg)[i%nbg], INTEGER(fg)[i%nfg], dd);
4010
			  INTEGER(bg)[i%nbg], INTEGER(fg)[i%nfg], dd);
4011
		    GRect(xx - rx,  yy - (1 - 2 * p2) * ry,
4011
		    GRect(xx - rx,  yy - (1 - 2 * p2) * ry,
4012
			  xx + rx,  yy - (1 - 2 * p3) * ry,
4012
			  xx + rx,  yy - (1 - 2 * p3) * ry,
4013
			  NDC,
4013
			  NDC,
4014
			  INTEGER(fg)[i%nfg], INTEGER(fg)[i%nfg], dd);
4014
			  INTEGER(fg)[i%nfg], INTEGER(fg)[i%nfg], dd);
4015
		    GLine(xx - rx, yy, xx - 1.5 * rx, yy, NDC, dd);
4015
		    GLine(xx - rx, yy, xx - 1.5 * rx, yy, NDC, dd);
4016
		    GLine(xx + rx, yy, xx + 1.5 * rx, yy, NDC, dd);
4016
		    GLine(xx + rx, yy, xx + 1.5 * rx, yy, NDC, dd);
4017
 
4017
 
4018
		}
4018
		}
4019
	    }
4019
	    }
4020
	}
4020
	}
4021
	break;
4021
	break;
4022
    case 6: /* boxplots (wid, hei, loWhsk, upWhsk, medProp) */
4022
    case 6: /* boxplots (wid, hei, loWhsk, upWhsk, medProp) */
4023
	if (nc != 5)
4023
	if (nc != 5)
4024
	    error(_("invalid boxplots data (need 5 columns)"));
4024
	    error(_("invalid boxplots data (need 5 columns)"));
4025
	pmax = -DBL_MAX;
4025
	pmax = -DBL_MAX;
4026
	pmin =	DBL_MAX;
4026
	pmin =	DBL_MAX;
4027
	for(i = 0; i < nr; i++) {
4027
	for(i = 0; i < nr; i++) {
4028
	    p4 = REAL(p)[i + 4 * nr];	/* median proport. in [0,1] */
4028
	    p4 = REAL(p)[i + 4 * nr];	/* median proport. in [0,1] */
4029
	    if (pmax < p4) pmax = p4;
4029
	    if (pmax < p4) pmax = p4;
4030
	    if (pmin > p4) pmin = p4;
4030
	    if (pmin > p4) pmin = p4;
4031
	}
4031
	}
4032
	if (pmin < 0. || pmax > 1.) /* S-PLUS has an error here */
4032
	if (pmin < 0. || pmax > 1.) /* S-PLUS has an error here */
4033
	    warning(_("boxplots[,5] outside [0,1] -- may look funny"));
4033
	    warning(_("boxplots[,5] outside [0,1] -- may look funny"));
4034
	if (!SymbolRange(REAL(p), 4 * nr, &pmax, &pmin))
4034
	if (!SymbolRange(REAL(p), 4 * nr, &pmax, &pmin))
4035
	    error(_("invalid boxplots[, 1:4]"));
4035
	    error(_("invalid boxplots[, 1:4]"));
4036
	for (i = 0; i < nr; i++) {
4036
	for (i = 0; i < nr; i++) {
4037
	    xx = REAL(x)[i];
4037
	    xx = REAL(x)[i];
4038
	    yy = REAL(y)[i];
4038
	    yy = REAL(y)[i];
4039
	    if (R_FINITE(xx) && R_FINITE(yy)) {
4039
	    if (R_FINITE(xx) && R_FINITE(yy)) {
4040
		p0 = REAL(p)[i];	/* width */
4040
		p0 = REAL(p)[i];	/* width */
4041
		p1 = REAL(p)[i + nr];	/* height */
4041
		p1 = REAL(p)[i + nr];	/* height */
4042
		p2 = REAL(p)[i + 2 * nr];/* lower whisker */
4042
		p2 = REAL(p)[i + 2 * nr];/* lower whisker */
4043
		p3 = REAL(p)[i + 3 * nr];/* upper whisker */
4043
		p3 = REAL(p)[i + 3 * nr];/* upper whisker */
4044
		p4 = REAL(p)[i + 4 * nr];/* median proport. in [0,1] */
4044
		p4 = REAL(p)[i + 4 * nr];/* median proport. in [0,1] */
4045
		if (R_FINITE(p0) && R_FINITE(p1) &&
4045
		if (R_FINITE(p0) && R_FINITE(p1) &&
4046
		    R_FINITE(p2) && R_FINITE(p3) && R_FINITE(p4)) {
4046
		    R_FINITE(p2) && R_FINITE(p3) && R_FINITE(p4)) {
4047
		    GConvert(&xx, &yy, USER, NDC, dd);
4047
		    GConvert(&xx, &yy, USER, NDC, dd);
4048
		    if (inches > 0) {
4048
		    if (inches > 0) {
4049
			p0 *= inches / pmax;
4049
			p0 *= inches / pmax;
4050
			p1 *= inches / pmax;
4050
			p1 *= inches / pmax;
4051
			p2 *= inches / pmax;
4051
			p2 *= inches / pmax;
4052
			p3 *= inches / pmax;
4052
			p3 *= inches / pmax;
4053
			p0 = GConvertXUnits(p0, INCHES, NDC, dd);
4053
			p0 = GConvertXUnits(p0, INCHES, NDC, dd);
4054
			p1 = GConvertYUnits(p1, INCHES, NDC, dd);
4054
			p1 = GConvertYUnits(p1, INCHES, NDC, dd);
4055
			p2 = GConvertYUnits(p2, INCHES, NDC, dd);
4055
			p2 = GConvertYUnits(p2, INCHES, NDC, dd);
4056
			p3 = GConvertYUnits(p3, INCHES, NDC, dd);
4056
			p3 = GConvertYUnits(p3, INCHES, NDC, dd);
4057
		    }
4057
		    }
4058
		    else {
4058
		    else {
4059
			p0 = GConvertXUnits(p0, USER, NDC, dd);
4059
			p0 = GConvertXUnits(p0, USER, NDC, dd);
4060
			p1 = GConvertYUnits(p1, USER, NDC, dd);
4060
			p1 = GConvertYUnits(p1, USER, NDC, dd);
4061
			p2 = GConvertYUnits(p2, USER, NDC, dd);
4061
			p2 = GConvertYUnits(p2, USER, NDC, dd);
4062
			p3 = GConvertYUnits(p3, USER, NDC, dd);
4062
			p3 = GConvertYUnits(p3, USER, NDC, dd);
4063
		    }
4063
		    }
4064
		    rx = 0.5 * p0;
4064
		    rx = 0.5 * p0;
4065
		    ry = 0.5 * p1;
4065
		    ry = 0.5 * p1;
4066
		    p4 = (1 - p4) * (yy - ry) + p4 * (yy + ry);
4066
		    p4 = (1 - p4) * (yy - ry) + p4 * (yy + ry);
4067
		    /* Box */
4067
		    /* Box */
4068
		    GRect(xx - rx, yy - ry, xx + rx, yy + ry, NDC,
4068
		    GRect(xx - rx, yy - ry, xx + rx, yy + ry, NDC,
4069
			  INTEGER(bg)[i%nbg], INTEGER(fg)[i%nfg], dd);
4069
			  INTEGER(bg)[i%nbg], INTEGER(fg)[i%nfg], dd);
4070
		    /* Median */
4070
		    /* Median */
4071
		    GLine(xx - rx, p4, xx + rx, p4, NDC, dd);
4071
		    GLine(xx - rx, p4, xx + rx, p4, NDC, dd);
4072
		    /* Lower Whisker */
4072
		    /* Lower Whisker */
4073
		    GLine(xx, yy - ry, xx, yy - ry - p2, NDC, dd);
4073
		    GLine(xx, yy - ry, xx, yy - ry - p2, NDC, dd);
4074
		    /* Upper Whisker */
4074
		    /* Upper Whisker */
4075
		    GLine(xx, yy + ry, xx, yy + ry + p3, NDC, dd);
4075
		    GLine(xx, yy + ry, xx, yy + ry + p3, NDC, dd);
4076
		}
4076
		}
4077
	    }
4077
	    }
4078
	}
4078
	}
4079
	break;
4079
	break;
4080
    default:
4080
    default:
4081
	error(_("invalid symbol type"));
4081
	error(_("invalid symbol type"));
4082
    }
4082
    }
4083
    GMode(0, dd);
4083
    GMode(0, dd);
4084
    GRestorePars(dd);
4084
    GRestorePars(dd);
4085
    if (GRecording(call, dd))
4085
    if (GRecording(call, dd))
4086
	recordGraphicOperation(op, originalArgs, dd);
4086
	recordGraphicOperation(op, originalArgs, dd);
4087
    UNPROTECT(5);
4087
    UNPROTECT(5);
4088
    return R_NilValue;
4088
    return R_NilValue;
4089
}
4089
}
4090
 
4090
 
4091
SEXP attribute_hidden do_xspline(SEXP call, SEXP op, SEXP args, SEXP env)
4091
SEXP attribute_hidden do_xspline(SEXP call, SEXP op, SEXP args, SEXP env)
4092
{
4092
{
4093
    SEXP sx, sy, ss, col, border, res, ans = R_NilValue;
4093
    SEXP sx, sy, ss, col, border, res, ans = R_NilValue;
4094
    int i, nx;
4094
    int i, nx;
4095
    int ncol, nborder;
4095
    int ncol, nborder;
4096
    double *x, *y;
4096
    double *x, *y;
4097
    Rboolean open, repEnds, draw;
4097
    Rboolean open, repEnds, draw;
4098
    double *xx;
4098
    double *xx;
4099
    double *yy;
4099
    double *yy;
4100
    void *vmaxsave;
4100
    void *vmaxsave;
4101
    R_GE_gcontext gc;
4101
    R_GE_gcontext gc;
4102
 
4102
 
4103
    SEXP originalArgs = args;
4103
    SEXP originalArgs = args;
4104
    DevDesc *dd = CurrentDevice();
4104
    DevDesc *dd = CurrentDevice();
4105
    gcontextFromGP(&gc, dd);
4105
    gcontextFromGP(&gc, dd);
4106
 
4106
 
4107
    GCheckState(dd);
4107
    GCheckState(dd);
4108
 
4108
 
4109
    if (length(args) < 6) error(_("too few arguments"));
4109
    if (length(args) < 6) error(_("too few arguments"));
4110
    /* (x,y) is checked in R via xy.coords() ; no need here : */
4110
    /* (x,y) is checked in R via xy.coords() ; no need here : */
4111
    sx = SETCAR(args, coerceVector(CAR(args), REALSXP));  args = CDR(args);
4111
    sx = SETCAR(args, coerceVector(CAR(args), REALSXP));  args = CDR(args);
4112
    sy = SETCAR(args, coerceVector(CAR(args), REALSXP));  args = CDR(args);
4112
    sy = SETCAR(args, coerceVector(CAR(args), REALSXP));  args = CDR(args);
4113
    nx = LENGTH(sx);
4113
    nx = LENGTH(sx);
4114
    ss = SETCAR(args, coerceVector(CAR(args), REALSXP));  args = CDR(args);
4114
    ss = SETCAR(args, coerceVector(CAR(args), REALSXP));  args = CDR(args);
4115
    open = asLogical(CAR(args)); args = CDR(args);
4115
    open = asLogical(CAR(args)); args = CDR(args);
4116
    repEnds = asLogical(CAR(args)); args = CDR(args);
4116
    repEnds = asLogical(CAR(args)); args = CDR(args);
4117
    draw = asLogical(CAR(args)); args = CDR(args);
4117
    draw = asLogical(CAR(args)); args = CDR(args);
4118
 
4118
 
4119
    PROTECT(col = FixupCol(CAR(args), R_TRANWHITE));	args = CDR(args);
4119
    PROTECT(col = FixupCol(CAR(args), R_TRANWHITE));	args = CDR(args);
4120
    ncol = LENGTH(col);
4120
    ncol = LENGTH(col);
4121
    if(ncol < 1)
4121
    if(ncol < 1)
4122
	error(_("incorrect length for '%s' argument"), "col");
4122
	error(_("incorrect length for '%s' argument"), "col");
4123
    if(ncol > 1)
4123
    if(ncol > 1)
4124
	warning(_("incorrect length for '%s' argument"), "col");
4124
	warning(_("incorrect length for '%s' argument"), "col");
4125
 
4125
 
4126
    PROTECT(border = FixupCol(CAR(args), Rf_gpptr(dd)->fg)); args = CDR(args);
4126
    PROTECT(border = FixupCol(CAR(args), Rf_gpptr(dd)->fg)); args = CDR(args);
4127
    nborder = LENGTH(border);
4127
    nborder = LENGTH(border);
4128
    if(nborder < 1)
4128
    if(nborder < 1)
4129
	error(_("incorrect length for '%s' argument"), "border");
4129
	error(_("incorrect length for '%s' argument"), "border");
4130
    if(nborder > 1)
4130
    if(nborder > 1)
4131
	warning(_("incorrect length for '%s' argument"), "border");
4131
	warning(_("incorrect length for '%s' argument"), "border");
4132
 
4132
 
4133
    GSavePars(dd);
4133
    GSavePars(dd);
4134
    ProcessInlinePars(args, dd, call);
4134
    ProcessInlinePars(args, dd, call);
4135
 
4135
 
4136
    GMode(1, dd);
4136
    GMode(1, dd);
4137
 
4137
 
4138
    x = REAL(sx);
4138
    x = REAL(sx);
4139
    y = REAL(sy);
4139
    y = REAL(sy);
4140
    vmaxsave = vmaxget();
4140
    vmaxsave = vmaxget();
4141
    xx = (double *) R_alloc(nx, sizeof(double));
4141
    xx = (double *) R_alloc(nx, sizeof(double));
4142
    yy = (double *) R_alloc(nx, sizeof(double));
4142
    yy = (double *) R_alloc(nx, sizeof(double));
4143
    if (!xx || !yy)
4143
    if (!xx || !yy)
4144
	error(_("unable to allocate memory (in do_xspline)"));
4144
	error(_("unable to allocate memory (in do_xspline)"));
4145
    for (i = 0; i < nx; i++) {
4145
    for (i = 0; i < nx; i++) {
4146
	xx[i] = x[i];
4146
	xx[i] = x[i];
4147
	yy[i] = y[i];
4147
	yy[i] = y[i];
4148
	GConvert(&(xx[i]), &(yy[i]), USER, DEVICE, dd);
4148
	GConvert(&(xx[i]), &(yy[i]), USER, DEVICE, dd);
4149
    }
4149
    }
4150
    GClip(dd);
4150
    GClip(dd);
4151
    gc.col = INTEGER(border)[0];
4151
    gc.col = INTEGER(border)[0];
4152
    gc.fill = INTEGER(col)[0];
4152
    gc.fill = INTEGER(col)[0];
4153
    res = GEXspline(nx, xx, yy, REAL(ss), open, repEnds, draw, &gc,
4153
    res = GEXspline(nx, xx, yy, REAL(ss), open, repEnds, draw, &gc,
4154
		    (GEDevDesc*) dd);
4154
		    (GEDevDesc*) dd);
4155
    vmaxset(vmaxsave);
4155
    vmaxset(vmaxsave);
4156
    UNPROTECT(2);
4156
    UNPROTECT(2);
4157
 
4157
 
4158
    if(!draw) {
4158
    if(!draw) {
4159
	SEXP nm, tmpx, tmpy;
4159
	SEXP nm, tmpx, tmpy;
4160
	double *xx, *yy, *x0, *y0;
4160
	double *xx, *yy, *x0, *y0;
4161
	PROTECT(ans = res);
4161
	PROTECT(ans = res);
4162
	PROTECT(nm = allocVector(STRSXP, 2));
4162
	PROTECT(nm = allocVector(STRSXP, 2));
4163
	SET_STRING_ELT(nm, 0, mkChar("x"));
4163
	SET_STRING_ELT(nm, 0, mkChar("x"));
4164
	SET_STRING_ELT(nm, 1, mkChar("y"));
4164
	SET_STRING_ELT(nm, 1, mkChar("y"));
4165
	setAttrib(ans, R_NamesSymbol, nm);
4165
	setAttrib(ans, R_NamesSymbol, nm);
4166
	nx = LENGTH(VECTOR_ELT(ans, 0));
4166
	nx = LENGTH(VECTOR_ELT(ans, 0));
4167
	x0 = REAL(VECTOR_ELT(ans, 0));
4167
	x0 = REAL(VECTOR_ELT(ans, 0));
4168
	y0 = REAL(VECTOR_ELT(ans, 1));
4168
	y0 = REAL(VECTOR_ELT(ans, 1));
4169
	PROTECT(tmpx = allocVector(REALSXP, nx));
4169
	PROTECT(tmpx = allocVector(REALSXP, nx));
4170
	PROTECT(tmpy = allocVector(REALSXP, nx));
4170
	PROTECT(tmpy = allocVector(REALSXP, nx));
4171
	xx = REAL(tmpx);
4171
	xx = REAL(tmpx);
4172
	yy = REAL(tmpy);
4172
	yy = REAL(tmpy);
4173
	for (i = 0; i < nx; i++) {
4173
	for (i = 0; i < nx; i++) {
4174
	    xx[i] = x0[i];
4174
	    xx[i] = x0[i];
4175
	    yy[i] = y0[i];
4175
	    yy[i] = y0[i];
4176
	    GConvert(&(xx[i]), &(yy[i]), DEVICE, USER, dd);
4176
	    GConvert(&(xx[i]), &(yy[i]), DEVICE, USER, dd);
4177
	}
4177
	}
4178
	SET_VECTOR_ELT(ans, 0, tmpx);
4178
	SET_VECTOR_ELT(ans, 0, tmpx);
4179
	SET_VECTOR_ELT(ans, 1, tmpy);
4179
	SET_VECTOR_ELT(ans, 1, tmpy);
4180
	UNPROTECT(4);
4180
	UNPROTECT(4);
4181
    }
4181
    }
4182
    
4182
    
4183
    GMode(0, dd);
4183
    GMode(0, dd);
4184
    GRestorePars(dd);
4184
    GRestorePars(dd);
4185
    /* NOTE: only record operation if no "error"  */
4185
    /* NOTE: only record operation if no "error"  */
4186
    if (GRecording(call, dd))
4186
    if (GRecording(call, dd))
4187
	recordGraphicOperation(op, originalArgs, dd);
4187
	recordGraphicOperation(op, originalArgs, dd);
4188
    return ans;
4188
    return ans;
4189
}
4189
}