The R Project SVN R

Rev

Rev 526 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 526 Rev 581
Line 1... Line -...
1
/*
-
 
2
 *  R : A Computer Langage for Statistical Data Analysis
-
 
3
 *  Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
-
 
4
 *
-
 
5
 *  This program is free software; you can redistribute it and/or modify
-
 
6
 *  it under the terms of the GNU General Public License as published by
-
 
7
 *  the Free Software Foundation; either version 2 of the License, or
-
 
8
 *  (at your option) any later version.
-
 
9
 *
-
 
10
 *  This program is distributed in the hope that it will be useful,
-
 
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
-
 
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-
 
13
 *  GNU General Public License for more details.
-
 
14
 *
-
 
15
 *  You should have received a copy of the GNU General Public License
-
 
16
 *  along with this program; if not, write to the Free Software
-
 
17
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
 
18
 */
-
 
19
 
-
 
20
#include "Defn.h"
-
 
21
#include "Mathlib.h"
-
 
22
#include "Graphics.h"
-
 
23
 
-
 
24
typedef struct SEG {
-
 
25
	struct SEG *next;
-
 
26
	double x0;
-
 
27
	double y0;
-
 
28
	double x1;
-
 
29
	double y1;
-
 
30
} SEG, *SEGP;
-
 
31
 
-
 
32
static SEGP *SegDB;
-
 
33
 
-
 
34
static int intersect(double z0, double z1, double zc, double *f)
-
 
35
{
-
 
36
	if((z0 - zc) * (z1 - zc) < 0.0) {
-
 
37
		*f = (zc - z0) / (z1 -  z0);
-
 
38
		return 1;
-
 
39
	}
-
 
40
	return 0;
-
 
41
}
-
 
42
 
-
 
43
static SEGP NewSeg(double x0, double y0, double x1, double y1, SEGP prev)
-
 
44
{
-
 
45
	SEGP seg = (SEGP)R_alloc(1, sizeof(SEG));
-
 
46
	seg->x0 = x0;
-
 
47
	seg->y0 = y0;
-
 
48
	seg->x1 = x1;
-
 
49
	seg->y1 = y1;
-
 
50
	seg->next = prev;
-
 
51
	return seg;
-
 
52
}
-
 
53
 
-
 
54
static void SwapSeg(SEGP seg)
-
 
55
{
-
 
56
	double x, y;
-
 
57
	x = seg->x0;
-
 
58
	y = seg->y0;
-
 
59
	seg->x0 = seg->x1;
-
 
60
	seg->y0 = seg->y1;
-
 
61
	seg->x1 = x;
-
 
62
	seg->y1 = y;
-
 
63
}
-
 
64
 
-
 
65
	/* Determine the entry direction to the next cell */
-
 
66
	/* and update the cell indices */
-
 
67
 
-
 
68
static int SegDir(double xend, double yend, double *x, double *y, int *i, int *j, int nx, int ny)
-
 
69
{
-
 
70
	if(yend == y[*j]) {
-
 
71
		if(*j == 0) return 0;
-
 
72
		*j = *j - 1;
-
 
73
		return 3;
-
 
74
	}
-
 
75
	if(xend == x[*i]) {
-
 
76
		if(*i == 0) return 0;
-
 
77
		*i = *i - 1;
-
 
78
		return 4;
-
 
79
	}
-
 
80
	if(yend == y[*j+1]) {
-
 
81
		if(*j >= ny - 1) return 0;
-
 
82
		*j = *j + 1;
-
 
83
		return 1;
-
 
84
	}
-
 
85
	if(xend == x[*i+1]) {
-
 
86
		if(*i >= nx - 1) return 0;
-
 
87
		*i = *i + 1;
-
 
88
		return 2;
-
 
89
	}
-
 
90
	return 0;
-
 
91
}
-
 
92
 
-
 
93
	/* Search seglist for a segment with endpoint (xend, yend). */
-
 
94
	/* The cell entry direction is dir, and if tail=1/0 we are */
-
 
95
	/* building the tail/head of a contour.  The matching segment */
-
 
96
	/* is pointed to by seg and the updated segment list (with */
-
 
97
	/* the matched segment stripped is returned by the funtion. */
-
 
98
 
-
 
99
static SEGP SegUpdate(double xend, double yend, int dir, int tail, SEGP seglist, SEGP* seg)
-
 
100
{
-
 
101
	if(seglist == NULL) {
-
 
102
		*seg = NULL;
-
 
103
		return NULL;
-
 
104
	}
-
 
105
	switch(dir) {
-
 
106
	case 1:
-
 
107
	case 3:
-
 
108
		if(yend == seglist->y0) {
-
 
109
			if(!tail) SwapSeg(seglist);
-
 
110
			*seg = seglist;
-
 
111
			return seglist->next;
-
 
112
		}
-
 
113
		if(yend == seglist->y1) {
-
 
114
			if(tail) SwapSeg(seglist);
-
 
115
			*seg = seglist;
-
 
116
			return seglist->next;
-
 
117
		}
-
 
118
		break;
-
 
119
	case 2:
-
 
120
	case 4:
-
 
121
		if(xend == seglist->x0) {
-
 
122
			if(!tail) SwapSeg(seglist);
-
 
123
			*seg = seglist;
-
 
124
			return seglist->next;
-
 
125
		}
-
 
126
		if(xend == seglist->x1) {
-
 
127
			if(tail) SwapSeg(seglist);
-
 
128
			*seg = seglist;
-
 
129
			return seglist->next;
-
 
130
		}
-
 
131
		break;
-
 
132
	}
-
 
133
	seglist->next = SegUpdate(xend, yend, dir, tail, seglist->next, seg);
-
 
134
	return seglist;
-
 
135
}
-
 
136
 
-
 
137
static void contour(SEXP x, int nx, SEXP y, int ny, SEXP z, double zc, double atom)
-
 
138
{
-
 
139
	double f, xl, xh, yl, yh, zll, zhl, zlh, zhh, xx[4], yy[4];
-
 
140
	double xend, yend;
-
 
141
	int i, ii, j, jj, k, l, m, nacode, ns, ns2, dir;
-
 
142
	SEGP seglist, seg, s, start, end;
-
 
143
 
-
 
144
	for(i=0 ; i<nx-1 ; i++) {
-
 
145
		xl = REAL(x)[i];
-
 
146
		xh = REAL(x)[i+1];
-
 
147
		for(j=0 ; j<ny-1 ; j++) {
-
 
148
			yl = REAL(y)[j];
-
 
149
			yh = REAL(y)[j+1];
-
 
150
			k = i+j*nx;
-
 
151
			zll = REAL(z)[k];
-
 
152
			zhl = REAL(z)[k+1];
-
 
153
			zlh = REAL(z)[k+nx];
-
 
154
			zhh = REAL(z)[k+nx+1];
-
 
155
			k = 0;
-
 
156
 
-
 
157
				/* If the value at a corner is */
-
 
158
				/* exactly equal to a contour */
-
 
159
				/* level, change the value at */
-
 
160
				/* corner by a tiny amount. */
-
 
161
 
-
 
162
			if(zll == zc) zll = zll + atom;
-
 
163
			if(zhl == zc) zhl = zhl + atom;
-
 
164
			if(zlh == zc) zlh = zlh + atom;
-
 
165
			if(zhh == zc) zhh = zhh + atom;
-
 
166
 
-
 
167
				/* Check for intersections with sides */
-
 
168
 
-
 
169
			nacode = 0;
-
 
170
			if(FINITE(zll)) nacode += 1;
-
 
171
			if(FINITE(zhl)) nacode += 2;
-
 
172
			if(FINITE(zlh)) nacode += 4;
-
 
173
			if(FINITE(zhh)) nacode += 8;
-
 
174
 
-
 
175
			switch(nacode) {
-
 
176
			case 15:
-
 
177
				if(intersect(zll, zhl, zc, &f)) {
-
 
178
					xx[k] = xl + f * (xh - xl);
-
 
179
					yy[k] = yl; k++;
-
 
180
				}
-
 
181
				if(intersect(zll, zlh, zc, &f)) {
-
 
182
					yy[k] = yl + f * (yh - yl);
-
 
183
					xx[k] = xl; k++;
-
 
184
				}
-
 
185
				if(intersect(zhl, zhh, zc, &f)) {
-
 
186
					yy[k] = yl + f * (yh - yl);
-
 
187
					xx[k] = xh; k++;
-
 
188
				}
-
 
189
				if(intersect(zlh, zhh, zc, &f)) {
-
 
190
					xx[k] = xl + f * (xh - xl);
-
 
191
					yy[k] = yh; k++;
-
 
192
				}
-
 
193
				break;
-
 
194
			case 14:
-
 
195
				if(intersect(zhl, zhh, zc, &f)) {
-
 
196
					yy[k] = yl + f * (yh - yl);
-
 
197
					xx[k] = xh; k++;
-
 
198
				}
-
 
199
				if(intersect(zlh, zhh, zc, &f)) {
-
 
200
					xx[k] = xl + f * (xh - xl);
-
 
201
					yy[k] = yh; k++;
-
 
202
				}
-
 
203
				if(intersect(zlh, zhl, zc, &f)) {
-
 
204
					xx[k] = xl + f * (xh - xl);
-
 
205
					yy[k] = yh + f * (yl - yh);
-
 
206
					k++;
-
 
207
				}
-
 
208
				break;
-
 
209
			case 13:
-
 
210
				if(intersect(zll, zlh, zc, &f)) {
-
 
211
					yy[k] = yl + f * (yh - yl);
-
 
212
					xx[k] = xl; k++;
-
 
213
				}
-
 
214
				if(intersect(zlh, zhh, zc, &f)) {
-
 
215
					xx[k] = xl + f * (xh - xl);
-
 
216
					yy[k] = yh; k++;
-
 
217
				}
-
 
218
				if(intersect(zll, zhh, zc, &f)) {
-
 
219
					xx[k] = xl + f * (xh - xl);
-
 
220
					yy[k] = yl + f * (yh - yl);
-
 
221
					k++;
-
 
222
				}
-
 
223
				break;
-
 
224
			case 11:
-
 
225
				if(intersect(zhl, zhh, zc, &f)) {
-
 
226
					yy[k] = yl + f * (yh - yl);
-
 
227
					xx[k] = xh; k++;
-
 
228
				}
-
 
229
				if(intersect(zll, zhl, zc, &f)) {
-
 
230
					xx[k] = xl + f * (xh - xl);
-
 
231
					yy[k] = yl; k++;
-
 
232
				}
-
 
233
				if(intersect(zll, zhh, zc, &f)) {
-
 
234
					xx[k] = xl + f * (xh - xl);
-
 
235
					yy[k] = yl + f * (yh - yl);
-
 
236
					k++;
-
 
237
				}
-
 
238
				break;
-
 
239
			case 7:
-
 
240
				if(intersect(zll, zlh, zc, &f)) {
-
 
241
					yy[k] = yl + f * (yh - yl);
-
 
242
					xx[k] = xl; k++;
-
 
243
				}
-
 
244
				if(intersect(zll, zhl, zc, &f)) {
-
 
245
					xx[k] = xl + f * (xh - xl);
-
 
246
					yy[k] = yl; k++;
-
 
247
				}
-
 
248
				if(intersect(zlh, zhl, zc, &f)) {
-
 
249
					xx[k] = xl + f * (xh - xl);
-
 
250
					yy[k] = yh + f * (yl - yh);
-
 
251
					k++;
-
 
252
				}
-
 
253
				break;
-
 
254
			}
-
 
255
 
-
 
256
				/* We now have k(=2,4) endpoints */
-
 
257
				/* Decide which to join */
-
 
258
 
-
 
259
			seglist = NULL;
-
 
260
 
-
 
261
			if(k > 0) {
-
 
262
				if(k == 2) {
-
 
263
					seglist = NewSeg(xx[0], yy[0], xx[1], yy[1], seglist);
-
 
264
				}
-
 
265
				else if(k == 4) {
-
 
266
					for(k=3 ; k>=1 ; k--) {
-
 
267
						m = k;
-
 
268
						xl = xx[k];
-
 
269
						for(l=0 ; l<k ; l++) {
-
 
270
							if(xx[l] > xl) {
-
 
271
								xl = xx[l];
-
 
272
								m = l;
-
 
273
							}
-
 
274
						}
-
 
275
						if(m != k) {
-
 
276
							xl = xx[k];
-
 
277
							yl = yy[k];
-
 
278
							xx[k] = xx[m];
-
 
279
							yy[k] = yy[m];
-
 
280
							xx[m] = xl;
-
 
281
							yy[m] = yl;
-
 
282
						}
-
 
283
					}
-
 
284
					seglist = NewSeg(xx[0], yy[0], xx[1], yy[1], seglist);
-
 
285
					seglist = NewSeg(xx[2], yy[2], xx[3], yy[3], seglist);
-
 
286
				}
-
 
287
			}
-
 
288
			SegDB[i+j*nx] = seglist;
-
 
289
		}
-
 
290
	}
-
 
291
 
-
 
292
		/* The segment database is now assembled. */
-
 
293
		/* Begin following contours. */
-
 
294
		/* 1. Grab a segment */
-
 
295
		/* 2. Follow its tail */
-
 
296
		/* 3. Follow its head */
-
 
297
		/* 4. Draw the contour */
-
 
298
 
-
 
299
	for(i=0 ; i<nx-1 ; i++)
-
 
300
		for(j=0 ; j<ny-1 ; j++) {
-
 
301
			while(seglist = SegDB[i+j*nx]) {
-
 
302
				ii = i; jj = j;
-
 
303
				start = end = seglist;
-
 
304
				SegDB[i+j*nx] = seglist->next;
-
 
305
				xend = seglist->x1;
-
 
306
				yend = seglist->y1;
-
 
307
				while(dir=SegDir(xend, yend, REAL(x), REAL(y), &ii, &jj, nx, ny)) {
-
 
308
					SegDB[ii+jj*nx] = SegUpdate(xend, yend, dir, 1, SegDB[ii+jj*nx], &seg);
-
 
309
					if(!seg) break;
-
 
310
					end->next = seg;
-
 
311
					end = seg;
-
 
312
					xend = end->x1;
-
 
313
					yend = end->y1;
-
 
314
				}
-
 
315
				ii = i; jj = j;
-
 
316
				xend = seglist->x0;
-
 
317
				yend = seglist->y0;
-
 
318
				while(dir=SegDir(xend, yend, REAL(x), REAL(y), &ii, &jj, nx, ny)) {
-
 
319
					SegDB[ii+jj*nx] = SegUpdate(xend, yend, dir, 0, SegDB[ii+jj*nx], &seg);
-
 
320
					if(!seg) break;
-
 
321
					seg->next = start;
-
 
322
					start = seg;
-
 
323
					xend = start->x0;
-
 
324
					yend = start->y0;
-
 
325
				}
-
 
326
				s = start;
-
 
327
				ns = 0;
-
 
328
				while(s) {
-
 
329
					ns++;
-
 
330
					s = s->next;
-
 
331
				}
-
 
332
				if(ns > 3) ns2 = ns/2;
-
 
333
				else ns2 = -1;
-
 
334
 
-
 
335
				s = start;
-
 
336
				ns = 0;
-
 
337
				GMode(1);
-
 
338
				GStartPath();
-
 
339
				GMoveTo(XMAP(s->x0), YMAP(s->y0));
-
 
340
				while(s) {
-
 
341
					GLineTo(XMAP(s->x1), YMAP(s->y1));
-
 
342
					s = s->next;
-
 
343
				}
-
 
344
				GEndPath();
-
 
345
				GMode(0);
-
 
346
			}
-
 
347
		}
-
 
348
 
-
 
349
}
-
 
350
 
-
 
351
 
-
 
352
SEXP do_contour(SEXP call, SEXP op, SEXP args, SEXP env)
-
 
353
{
-
 
354
	SEXP c, x, y, z, col, lty;
-
 
355
	int i, j, nx, ny, nc, ncol, nlty;
-
 
356
	int ltysave, colsave;
-
 
357
	double atom, zmin, zmax;
-
 
358
	char *vmax, *vmax0;
-
 
359
 
-
 
360
	GCheckState();
-
 
361
 
-
 
362
	if(length(args) < 4) errorcall(call, "too few arguments\n");
-
 
363
 
-
 
364
	x = CAR(args);
-
 
365
	internalTypeCheck(call, x, REALSXP);
-
 
366
	nx = LENGTH(x);
-
 
367
	args = CDR(args);
-
 
368
 
-
 
369
	y = CAR(args);
-
 
370
	internalTypeCheck(call, y, REALSXP);
-
 
371
	ny = LENGTH(y);
-
 
372
	args = CDR(args);
-
 
373
 
-
 
374
	z = CAR(args);
-
 
375
	internalTypeCheck(call, z, REALSXP);
-
 
376
	args = CDR(args);
-
 
377
 
-
 
378
	c = CAR(args);
-
 
379
	internalTypeCheck(call, c, REALSXP);
-
 
380
	nc = LENGTH(c);
-
 
381
	args = CDR(args);
-
 
382
 
-
 
383
	PROTECT(col = FixupCol(GetPar("col", args)));
-
 
384
	ncol = length(col);
-
 
385
 
-
 
386
	PROTECT(lty = FixupLty(GetPar("lty", args)));
-
 
387
	nlty = length(lty);
-
 
388
 
-
 
389
		/* col, lwd and lty vectors here */
-
 
390
 
-
 
391
	if(nx < 2 || ny < 2)
-
 
392
		errorcall(call, "insufficient x or y values\n");
-
 
393
 
-
 
394
	if(nrows(z) != nx || ncols(z) != ny)
-
 
395
		errorcall(call, "dimension mismatch\n");
-
 
396
 
-
 
397
 
-
 
398
	if(nc < 1)
-
 
399
		errorcall(call, "no contour values\n");
-
 
400
 
-
 
401
	for(i=0 ; i<nx ; i++) {
-
 
402
		if(!FINITE(REAL(x)[i]))
-
 
403
			errorcall(call, "missing x values\n");
-
 
404
		if(i > 0 && REAL(x)[i] < REAL(x)[i-1])
-
 
405
			errorcall(call, "increasing x values expected\n");
-
 
406
	}
-
 
407
 
-
 
408
	for(i=0 ; i<ny ; i++) {
-
 
409
		if(!FINITE(REAL(y)[i]))
-
 
410
			errorcall(call, "missing y values\n");
-
 
411
		if(i > 0 && REAL(y)[i] < REAL(y)[i-1])
-
 
412
			errorcall(call, "increasing y values expected\n");
-
 
413
	}
-
 
414
 
-
 
415
	for(i=0 ; i<nc ; i++)
-
 
416
		if(!FINITE(REAL(c)[i]))
-
 
417
			errorcall(call, "illegal NA contour values\n");
-
 
418
 
-
 
419
	zmin = DBL_MAX;
-
 
420
	zmax = DBL_MIN;
-
 
421
	for(i=0 ; i<nx*ny ; i++)
-
 
422
		if(FINITE(REAL(z)[i])) {
-
 
423
			if(zmax < REAL(z)[i]) zmax =  REAL(z)[i];
-
 
424
			if(zmin > REAL(z)[i]) zmin =  REAL(z)[i];
-
 
425
		}
-
 
426
 
-
 
427
	if(zmin >= zmax) {
-
 
428
		if(zmin == zmax)
-
 
429
			warning("all z values are equal\n");
-
 
430
		else
-
 
431
			warning("all z values are NA\n");
-
 
432
		return R_NilValue;
-
 
433
	}
-
 
434
 
-
 
435
	atom = DBL_EPSILON * (zmax - zmin);
-
 
436
 
-
 
437
		/* Initialize the segment data base */
-
 
438
		/* Note we must be careful about resetting */
-
 
439
		/* the top of the stack, otherwise we run out of */
-
 
440
		/* memory after a sequence of displaylist replays */
-
 
441
 
-
 
442
	vmax0 = vmaxget();
-
 
443
	SegDB = (SEGP*)R_alloc(nx*ny, sizeof(SEGP));
-
 
444
 
-
 
445
	for(i=0 ; i<nx ; i++)
-
 
446
		for(j=0 ; j<ny ; j++)
-
 
447
			SegDB[i+j*nx] = NULL;
-
 
448
 
-
 
449
		/* Draw the contours -- note the heap release */
-
 
450
 
-
 
451
	ltysave = GP->lty;
-
 
452
	colsave = GP->col;
-
 
453
	for(i=0 ; i<nc ; i++) {
-
 
454
		vmax = vmaxget();
-
 
455
		GP->lty = INTEGER(lty)[i%nlty];
-
 
456
		if(GP->lty == NA_INTEGER) GP->lty = ltysave;
-
 
457
		GP->col = INTEGER(col)[i%ncol];
-
 
458
		if(GP->col == NA_INTEGER) GP->col = colsave;
-
 
459
		contour(x, nx, y, ny, z, REAL(c)[i], atom);
-
 
460
		vmaxset(vmax);
-
 
461
	}
-
 
462
	vmaxset(vmax0);
-
 
463
	GP->lty = ltysave;
-
 
464
	GP->col = colsave;
-
 
465
	UNPROTECT(2);
-
 
466
	return R_NilValue;
-
 
467
}
-
 
468
 
-
 
469
 
-
 
470
SEXP do_image(SEXP call, SEXP op, SEXP args, SEXP env)
-
 
471
{
-
 
472
	SEXP sx, sy, sz, szlim, sc;
-
 
473
	double *x, *y, *z;
-
 
474
	unsigned *c;
-
 
475
	double xlow, xhigh, ylow, yhigh, zmin, zmax;
-
 
476
	int i, j, nx, ny, nz, ic, nc, colsave, xpdsave;
-
 
477
 
-
 
478
	GCheckState();
-
 
479
 
-
 
480
	checkArity(op,args);
-
 
481
 
-
 
482
	sx = CAR(args);
-
 
483
	internalTypeCheck(call, sx, REALSXP);
-
 
484
	nx = LENGTH(sx);
-
 
485
	args = CDR(args);
-
 
486
 
-
 
487
	sy = CAR(args);
-
 
488
	internalTypeCheck(call, sy, REALSXP);
-
 
489
	ny = LENGTH(sy);
-
 
490
	args = CDR(args);
-
 
491
 
-
 
492
	sz = CAR(args);
-
 
493
	internalTypeCheck(call, sz, REALSXP);
-
 
494
	nz = length(sz);
-
 
495
	args = CDR(args);
-
 
496
 
-
 
497
	szlim = CAR(args);
-
 
498
	internalTypeCheck(call, szlim, REALSXP);
-
 
499
	if(length(szlim) != 2 ||
-
 
500
		!FINITE(REAL(szlim)[0]) ||
-
 
501
		!FINITE(REAL(szlim)[1]) ||
-
 
502
		REAL(szlim)[0] >= REAL(szlim)[1])
-
 
503
			errorcall(call, "invalid z limits\n");
-
 
504
	zmin = REAL(szlim)[0];
-
 
505
	zmax = REAL(szlim)[1];
-
 
506
	args = CDR(args);
-
 
507
 
-
 
508
	PROTECT(sc = FixupCol(CAR(args)));
-
 
509
	nc = length(sc);
-
 
510
 
-
 
511
		/* Shorthand Pointers */
-
 
512
 
-
 
513
	x = REAL(sx);
-
 
514
	y = REAL(sy);
-
 
515
	z = REAL(sz);
-
 
516
	c = (unsigned*)INTEGER(sc);
-
 
517
 
-
 
518
		/* Check of grid coordinates */
-
 
519
		/* We want them to all be finite and */
-
 
520
		/* in strictly ascending order */
-
 
521
 
-
 
522
	if(nx < 2 || ny < 2) goto badxy;
-
 
523
	if(!FINITE(x[0])) goto badxy;
-
 
524
	if(!FINITE(y[0])) goto badxy;
-
 
525
	for(i=1 ; i<nx ; i++)
-
 
526
		if(!FINITE(x[i]) || x[i] <= x[i-1]) goto badxy;
-
 
527
	for(j=1 ; j<ny ; j++)
-
 
528
		if(!FINITE(y[j]) || y[j] <= y[j-1]) goto badxy;
-
 
529
 
-
 
530
	colsave = GP->col;
-
 
531
	xpdsave = GP->xpd;
-
 
532
	GP->xpd = 0;
-
 
533
 
-
 
534
	GMode(1);
-
 
535
 
-
 
536
	for(i=0 ; i<nx ; i++) {
-
 
537
		if(i == 0)
-
 
538
			xlow = XMAP(x[0]);
-
 
539
		else
-
 
540
			xlow = XMAP(0.5 * (x[i] + x[i-1]));
-
 
541
		if(i == nx-1)
-
 
542
			xhigh = XMAP(x[nx-1]);
-
 
543
		else
-
 
544
			xhigh = XMAP(0.5 * (x[i] + x[i+1]));
-
 
545
 
-
 
546
		for(j=0 ; j<ny ; j++) {
-
 
547
			if(FINITE(z[i+j*nx])) {
-
 
548
				ic = floor((nc - 1) * (z[i+j*nx]-zmin)/(zmax - zmin) + 0.5);
-
 
549
				if(ic >= 0 && ic < nc) {
-
 
550
					if(j == 0)
-
 
551
						ylow = YMAP(y[0]);
-
 
552
					else
-
 
553
						ylow = YMAP(0.5 * (y[j] + y[j-1]));
-
 
554
					if(j == ny-1)
-
 
555
						yhigh = YMAP(y[ny-1]);
-
 
556
					else
-
 
557
						yhigh = YMAP(0.5 * (y[j] + y[j+1]));
-
 
558
					GRect(xlow, ylow, xhigh, yhigh, c[ic], NA_INTEGER);
-
 
559
				}
-
 
560
			}
-
 
561
		}
-
 
562
	}
-
 
563
	GMode(0);
-
 
564
	GP->col = colsave;
-
 
565
	GP->xpd = xpdsave;
-
 
566
	R_Visible = 0;
-
 
567
	UNPROTECT(1);
-
 
568
	return R_NilValue;
-
 
569
 
-
 
570
badxy:
-
 
571
	errorcall(call, "invalid x / y limits\n");
-
 
572
}
-