Rev 59177 | Rev 59267 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** R : A Computer Language for Statistical Data Analysis* Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka* Copyright (C) 1997--2012 The R Core Team** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, a copy is available at* http://www.r-project.org/Licenses/*/#ifdef HAVE_CONFIG_H#include <config.h>#endif#include <Defn.h>#include <float.h> /* for DBL_MAX */#include <Rmath.h>#include <Graphics.h>#include <Colors.h> /* for isNAcol */#include <Print.h>#include <R_ext/Boolean.h>/* filled contours and perspective plots were originally here,now in graphics/src/plot3d.c .*//* Stuff for labels on contour plotsOriginally written by Nicholas HildrethAdapted by Paul Murrell*/staticvoid FindCorners(double width, double height, SEXP label,double x0, double y0, double x1, double y1,pGEDevDesc dd) {double delta = height / width;double dx = GConvertXUnits(x1 - x0, USER, INCHES, dd) * delta;double dy = GConvertYUnits(y1 - y0, USER, INCHES, dd) * delta;dx = GConvertYUnits(dx, INCHES, USER, dd);dy = GConvertXUnits(dy, INCHES, USER, dd);REAL(label)[0] = x0 + dy;REAL(label)[4] = y0 - dx;REAL(label)[1] = x0 - dy;REAL(label)[5] = y0 + dx;REAL(label)[3] = x1 + dy;REAL(label)[7] = y1 - dx;REAL(label)[2] = x1 - dy;REAL(label)[6] = y1 + dx;}staticint TestLabelIntersection(SEXP label1, SEXP label2) {int i, j, l1, l2;double Ax, Bx, Ay, By, ax, ay, bx, by;double dom;double result1, result2;for (i = 0; i < 4; i++) {Ax = REAL(label1)[i];Ay = REAL(label1)[i+4];Bx = REAL(label1)[(i+1)%4];By = REAL(label1)[(i+1)%4+4];for (j = 0; j < 4; j++) {ax = REAL(label2)[j];ay = REAL(label2)[j+4];bx = REAL(label2)[(j+1)%4];by = REAL(label2)[(j+1)%4+4];dom = Bx*by - Bx*ay - Ax*by + Ax*ay - bx*By + bx*Ay + ax*By - ax*Ay;if (dom == 0.0) {result1 = -1;result2 = -1;}else {result1 = (bx*Ay - ax*Ay - ay*bx - Ax*by + Ax*ay + by*ax) / dom;if (bx - ax == 0.0) {if (by - ay == 0.0)result2 = -1;elseresult2 = (Ay + (By - Ay) * result1 - ay) / (by - ay);}elseresult2 = (Ax + (Bx - Ax) * result1 - ax) / (bx - ax);}l1 = (result1 >= 0.0) && (result1 <= 1.0);l2 = (result2 >= 0.0) && (result2 <= 1.0);if (l1 && l2) return 1;}}return 0;}/*** Checks whether a label window is inside view region ***/static int LabelInsideWindow(SEXP label, pGEDevDesc dd) {int i = 0;double x, y;while (i < 4) {x = REAL(label)[i];y = REAL(label)[i+4];GConvert(&x, &y, USER, NDC, dd);/* x = GConvertXUnits(REAL(label)[i], USER, NDC, dd);y = GConvertYUnits(REAL(label)[i+4], USER, NDC, dd); */if ((x < 0) || (x > 1) ||(y < 0) || (y > 1))return 1;i += 1;}return 0;}/* C o n t o u r P l o t t i n g */typedef struct SEG {struct SEG *next;double x0;double y0;double x1;double y1;} SEG, *SEGP;static SEGP *ctr_SegDB;static int ctr_intersect(double z0, double z1, double zc, double *f){if ((z0 - zc) * (z1 - zc) < 0.0) {*f = (zc - z0) / (z1 - z0);return 1;}return 0;}static SEGP ctr_newseg(double x0, double y0, double x1, double y1, SEGP prev){SEGP seg = (SEGP)R_alloc(1, sizeof(SEG));seg->x0 = x0;seg->y0 = y0;seg->x1 = x1;seg->y1 = y1;seg->next = prev;return seg;}static void ctr_swapseg(SEGP seg){double x, y;x = seg->x0;y = seg->y0;seg->x0 = seg->x1;seg->y0 = seg->y1;seg->x1 = x;seg->y1 = y;}/* ctr_segdir(): Determine the entry direction to the next cell *//* and update the cell indices */#define XMATCH(x0,x1) (fabs(x0-x1) == 0)#define YMATCH(y0,y1) (fabs(y0-y1) == 0)static int ctr_segdir(double xend, double yend, double *x, double *y,int *i, int *j, int nx, int ny){if (YMATCH(yend, y[*j])) {if (*j == 0)return 0;*j = *j - 1;return 3;}if (XMATCH(xend, x[*i])) {if (*i == 0)return 0;*i = *i - 1;return 4;}if (YMATCH(yend, y[*j + 1])) {if (*j >= ny - 1)return 0;*j = *j + 1;return 1;}if (XMATCH(xend, x[*i + 1])) {if (*i >= nx - 1)return 0;*i = *i + 1;return 2;}return 0;}/* Search seglist for a segment with endpoint (xend, yend). *//* The cell entry direction is dir, and if tail=1/0 we are *//* building the tail/head of a contour. The matching segment *//* is pointed to by seg and the updated segment list (with *//* the matched segment stripped) is returned by the funtion. */static SEGP ctr_segupdate(double xend, double yend, int dir, Rboolean tail,SEGP seglist, SEGP* seg){if (seglist == NULL) {*seg = NULL;return NULL;}switch (dir) {case 1:case 3:if (YMATCH(yend,seglist->y0)) {if (!tail)ctr_swapseg(seglist);*seg = seglist;return seglist->next;}if (YMATCH(yend,seglist->y1)) {if (tail)ctr_swapseg(seglist);*seg = seglist;return seglist->next;}break;case 2:case 4:if (XMATCH(xend,seglist->x0)) {if (!tail)ctr_swapseg(seglist);*seg = seglist;return seglist->next;}if (XMATCH(xend,seglist->x1)) {if (tail)ctr_swapseg(seglist);*seg = seglist;return seglist->next;}break;}seglist->next = ctr_segupdate(xend, yend, dir, tail, seglist->next, seg);return seglist;}/* labelList, label1, and label2 are all SEXPs rather than being allocatedusing R_alloc because they need to persist across calls to contour().In do_contour() there is a vmaxget() ... vmaxset() around each call tocontour() to release all of the memory used in the drawing of thecontour _lines_ at each contour level. We need to keep track of thecontour _labels_ for _all_ contour levels, hence we have to use adifferent memory allocation mechanism.*/static SEXP labelList;staticdouble distFromEdge(double *xxx, double *yyy, int iii, pGEDevDesc dd) {return fmin2(fmin2(xxx[iii]-gpptr(dd)->usr[0], gpptr(dd)->usr[1]-xxx[iii]),fmin2(yyy[iii]-gpptr(dd)->usr[2], gpptr(dd)->usr[3]-yyy[iii]));}staticRboolean useStart(double *xxx, double *yyy, int ns, pGEDevDesc dd) {if (distFromEdge(xxx, yyy, 0, dd) < distFromEdge(xxx, yyy, ns-1, dd))return TRUE;elsereturn FALSE;}staticint findGapUp(double *xxx, double *yyy, int ns, double labelDistance,pGEDevDesc dd) {double dX, dY;double dXC, dYC;double distanceSum = 0;int n = 0;int jjj = 1;while ((jjj < ns) && (distanceSum < labelDistance)) {/* Find a gap big enough for the labeluse several segments if necessary*/dX = xxx[jjj] - xxx[jjj - n - 1]; /* jjj - n - 1 == 0 */dY = yyy[jjj] - yyy[jjj - n - 1];dXC = GConvertXUnits(dX, USER, INCHES, dd);dYC = GConvertYUnits(dY, USER, INCHES, dd);distanceSum = hypot(dXC, dYC);jjj++;n++;}if (distanceSum < labelDistance)return 0;elsereturn n;}staticint findGapDown(double *xxx, double *yyy, int ns, double labelDistance,pGEDevDesc dd) {double dX, dY;double dXC, dYC;double distanceSum = 0;int n = 0;int jjj = ns - 2;while ((jjj > -1) && (distanceSum < labelDistance)) {/* Find a gap big enough for the labeluse several segments if necessary*/dX = xxx[jjj] - xxx[jjj + n + 1]; /*jjj + n + 1 == ns -1 */dY = yyy[jjj] - yyy[jjj + n + 1];dXC = GConvertXUnits(dX, USER, INCHES, dd);dYC = GConvertYUnits(dY, USER, INCHES, dd);distanceSum = hypot(dXC, dYC);jjj--;n++;}if (distanceSum < labelDistance)return 0;elsereturn n;}/** Generate a list of segments for a single level*/static SEGP* contourLines(double *x, int nx, double *y, int ny,double *z, double zc, double atom){double f, xl, xh, yl, yh, zll, zhl, zlh, zhh, xx[4], yy[4];int i, j, k, l, m, nacode;SEGP seglist;SEGP *segmentDB;/* Initialize the segment data base *//* Note we must be careful about resetting *//* the top of the stack, otherwise we run out of *//* memory after a sequence of displaylist replays *//** This reset is done out in GEcontourLines*/segmentDB = (SEGP*)R_alloc(nx*ny, sizeof(SEGP));for (i = 0; i < nx; i++)for (j = 0; j < ny; j++)segmentDB[i + j * nx] = NULL;for (i = 0; i < nx - 1; i++) {xl = x[i];xh = x[i + 1];for (j = 0; j < ny - 1; j++) {yl = y[j];yh = y[j + 1];k = i + j * nx;zll = z[k];zhl = z[k + 1];zlh = z[k + nx];zhh = z[k + nx + 1];/* If the value at a corner is exactly equal to a contour level,* change that value by a tiny amount */if (zll == zc) zll += atom;if (zhl == zc) zhl += atom;if (zlh == zc) zlh += atom;if (zhh == zc) zhh += atom;#ifdef DEBUG_contour/* Haven't seen this happening (MM): */if (zll == zc) REprintf(" [%d,%d] ll: %g\n",i,j, zll);if (zhl == zc) REprintf(" [%d,%d] hl: %g\n",i,j, zhl);if (zlh == zc) REprintf(" [%d,%d] lh: %g\n",i,j, zlh);if (zhh == zc) REprintf(" [%d,%d] hh: %g\n",i,j, zhh);#endif/* Check for intersections with sides */nacode = 0;if (R_FINITE(zll)) nacode += 1;if (R_FINITE(zhl)) nacode += 2;if (R_FINITE(zlh)) nacode += 4;if (R_FINITE(zhh)) nacode += 8;k = 0;switch (nacode) {case 15:if (ctr_intersect(zll, zhl, zc, &f)) {xx[k] = xl + f * (xh - xl);yy[k] = yl; k++;}if (ctr_intersect(zll, zlh, zc, &f)) {yy[k] = yl + f * (yh - yl);xx[k] = xl; k++;}if (ctr_intersect(zhl, zhh, zc, &f)) {yy[k] = yl + f * (yh - yl);xx[k] = xh; k++;}if (ctr_intersect(zlh, zhh, zc, &f)) {xx[k] = xl + f * (xh - xl);yy[k] = yh; k++;}break;case 14:if (ctr_intersect(zhl, zhh, zc, &f)) {yy[k] = yl + f * (yh - yl);xx[k] = xh; k++;}if (ctr_intersect(zlh, zhh, zc, &f)) {xx[k] = xl + f * (xh - xl);yy[k] = yh; k++;}if (ctr_intersect(zlh, zhl, zc, &f)) {xx[k] = xl + f * (xh - xl);yy[k] = yh + f * (yl - yh);k++;}break;case 13:if (ctr_intersect(zll, zlh, zc, &f)) {yy[k] = yl + f * (yh - yl);xx[k] = xl; k++;}if (ctr_intersect(zlh, zhh, zc, &f)) {xx[k] = xl + f * (xh - xl);yy[k] = yh; k++;}if (ctr_intersect(zll, zhh, zc, &f)) {xx[k] = xl + f * (xh - xl);yy[k] = yl + f * (yh - yl);k++;}break;case 11:if (ctr_intersect(zhl, zhh, zc, &f)) {yy[k] = yl + f * (yh - yl);xx[k] = xh; k++;}if (ctr_intersect(zll, zhl, zc, &f)) {xx[k] = xl + f * (xh - xl);yy[k] = yl; k++;}if (ctr_intersect(zll, zhh, zc, &f)) {xx[k] = xl + f * (xh - xl);yy[k] = yl + f * (yh - yl);k++;}break;case 7:if (ctr_intersect(zll, zlh, zc, &f)) {yy[k] = yl + f * (yh - yl);xx[k] = xl; k++;}if (ctr_intersect(zll, zhl, zc, &f)) {xx[k] = xl + f * (xh - xl);yy[k] = yl; k++;}if (ctr_intersect(zlh, zhl, zc, &f)) {xx[k] = xl + f * (xh - xl);yy[k] = yh + f * (yl - yh);k++;}break;}/* We now have k(=2,4) endpoints *//* Decide which to join */seglist = NULL;if (k > 0) {if (k == 2) {seglist = ctr_newseg(xx[0], yy[0], xx[1], yy[1], seglist);}else if (k == 4) {for (k = 3; k >= 1; k--) {m = k;xl = xx[k];for (l = 0; l < k; l++) {if (xx[l] > xl) {xl = xx[l];m = l;}}if (m != k) {xl = xx[k];yl = yy[k];xx[k] = xx[m];yy[k] = yy[m];xx[m] = xl;yy[m] = yl;}}seglist = ctr_newseg(xx[0], yy[0], xx[1], yy[1], seglist);seglist = ctr_newseg(xx[2], yy[2], xx[3], yy[3], seglist);}else error("k != 2 or 4");}segmentDB[i + j * nx] = seglist;}}return segmentDB;}#define CONTOUR_LIST_STEP 100#define CONTOUR_LIST_LEVEL 0#define CONTOUR_LIST_X 1#define CONTOUR_LIST_Y 2static SEXP growList(SEXP oldlist) {int i, len;SEXP templist;len = LENGTH(oldlist);templist = PROTECT(allocVector(VECSXP, len + CONTOUR_LIST_STEP));for (i=0; i<len; i++)SET_VECTOR_ELT(templist, i, VECTOR_ELT(oldlist, i));UNPROTECT(1);return templist;}/** Store the list of segments for a single level in the SEXP* list that will be returned to the user*/staticint addContourLines(double *x, int nx, double *y, int ny,double *z, double zc, double atom,SEGP* segmentDB, int nlines, SEXP container){double xend, yend;int i, ii, j, jj, ns, dir, nc;SEGP seglist, seg, s, start, end;SEXP ctr, level, xsxp, ysxp, names;/* Begin following contours. *//* 1. Grab a segment *//* 2. Follow its tail *//* 3. Follow its head *//* 4. Save the contour */for (i = 0; i < nx - 1; i++)for (j = 0; j < ny - 1; j++) {while ((seglist = segmentDB[i + j * nx])) {ii = i; jj = j;start = end = seglist;segmentDB[i + j * nx] = seglist->next;xend = seglist->x1;yend = seglist->y1;while ((dir = ctr_segdir(xend, yend, x, y,&ii, &jj, nx, ny))) {segmentDB[ii + jj * nx]= ctr_segupdate(xend, yend, dir, TRUE,/* = tail */segmentDB[ii + jj * nx], &seg);if (!seg) break;end->next = seg;end = seg;xend = end->x1;yend = end->y1;}end->next = NULL; /* <<< new for 1.2.3 */ii = i; jj = j;xend = seglist->x0;yend = seglist->y0;while ((dir = ctr_segdir(xend, yend, x, y,&ii, &jj, nx, ny))) {segmentDB[ii + jj * nx]= ctr_segupdate(xend, yend, dir, FALSE,/* ie. head */segmentDB[ii+jj*nx], &seg);if (!seg) break;seg->next = start;start = seg;xend = start->x0;yend = start->y0;}/* ns := #{segments of polyline} -- need to allocate */s = start;ns = 0;/* max_contour_segments: prevent inf.loop (shouldn't be needed) */while (s && ns < max_contour_segments) {ns++;s = s->next;}if(ns == max_contour_segments)warning(_("contour(): circular/long seglist -- set %s > %d?"),"options(\"max.contour.segments\")", max_contour_segments);/** "write" the contour locations into the list of contours*/ctr = PROTECT(allocVector(VECSXP, 3));level = PROTECT(allocVector(REALSXP, 1));xsxp = PROTECT(allocVector(REALSXP, ns + 1));ysxp = PROTECT(allocVector(REALSXP, ns + 1));REAL(level)[0] = zc;SET_VECTOR_ELT(ctr, CONTOUR_LIST_LEVEL, level);s = start;REAL(xsxp)[0] = s->x0;REAL(ysxp)[0] = s->y0;ns = 1;while (s->next && ns < max_contour_segments) {s = s->next;REAL(xsxp)[ns] = s->x0;REAL(ysxp)[ns++] = s->y0;}REAL(xsxp)[ns] = s->x1;REAL(ysxp)[ns] = s->y1;SET_VECTOR_ELT(ctr, CONTOUR_LIST_X, xsxp);SET_VECTOR_ELT(ctr, CONTOUR_LIST_Y, ysxp);/** Set the names attribute for the contour* So that users can extract components using* meaningful names*/PROTECT(names = allocVector(STRSXP, 3));SET_STRING_ELT(names, 0, mkChar("level"));SET_STRING_ELT(names, 1, mkChar("x"));SET_STRING_ELT(names, 2, mkChar("y"));setAttrib(ctr, R_NamesSymbol, names);/** We're about to add another line to the list ...*/nlines += 1;nc = LENGTH(VECTOR_ELT(container, 0));if (nlines == nc)/* Where does this get UNPROTECTed? */SET_VECTOR_ELT(container, 0,growList(VECTOR_ELT(container, 0)));SET_VECTOR_ELT(VECTOR_ELT(container, 0), nlines - 1, ctr);UNPROTECT(5);}}return nlines;}/** Given nx x values, ny y values, nx*ny z values,* and nl cut-values in z ...* ... produce a list of contour lines:* list of sub-lists* sub-list = x vector, y vector, and cut-value.*/SEXP GEcontourLines(double *x, int nx, double *y, int ny,double *z, double *levels, int nl){const void *vmax;int i, nlines, len;double atom, zmin, zmax;SEGP* segmentDB;SEXP container, mainlist, templist;/** "tie-breaker" values*/zmin = DBL_MAX;zmax = DBL_MIN;for (i = 0; i < nx * ny; i++)if (R_FINITE(z[i])) {if (zmax < z[i]) zmax = z[i];if (zmin > z[i]) zmin = z[i];}if (zmin >= zmax) {if (zmin == zmax)warning(_("all z values are equal"));elsewarning(_("all z values are NA"));return R_NilValue;}/* change to 1e-3, reconsidered because of PR#897* but 1e-7, and even 2*DBL_EPSILON do not prevent inf.loop in contour().* maybe something like 16 * DBL_EPSILON * (..).* see also max_contour_segments above */atom = 1e-3 * (zmax - zmin);/** Create a "container" which is a list with only 1 element.* The element is the list of lines that will be built up.* I create the container because this allows me to PROTECT* the container once here and then UNPROTECT it at the end of* this function and, as long as I always work with* VECTOR_ELT(container, 0) and SET_VECTOR_ELT(container, 0)* in functions called from here, I don't need to worry about* protectin the list that I am building up.* Why bother? Because the list I am building can potentially* grow and it's awkward to get the PROTECTs/UNPROTECTs right* when you're in a loop and growing a list.*/container = PROTECT(allocVector(VECSXP, 1));/** Create "large" list (will trim excess at the end if necesary)*/SET_VECTOR_ELT(container, 0, allocVector(VECSXP, CONTOUR_LIST_STEP));nlines = 0;/** Add lines for each contour level*/for (i = 0; i < nl; i++) {/** The vmaxget/set is to manage the memory that gets* R_alloc'ed in the creation of the segmentDB structure*/vmax = vmaxget();/** Generate a segment database*/segmentDB = contourLines(x, nx, y, ny, z, levels[i], atom);/** Add lines to the list based on the segment database*/nlines = addContourLines(x, nx, y, ny, z, levels[i],atom, segmentDB, nlines,container);vmaxset(vmax);}/** Trim the list of lines to the appropriate length.*/len = LENGTH(VECTOR_ELT(container, 0));if (nlines < len) {mainlist = VECTOR_ELT(container, 0);templist = PROTECT(allocVector(VECSXP, nlines));for (i=0; i<nlines; i++)SET_VECTOR_ELT(templist, i, VECTOR_ELT(mainlist, i));mainlist = templist;UNPROTECT(1); /* UNPROTECT templist */} elsemainlist = VECTOR_ELT(container, 0);UNPROTECT(1); /* UNPROTECT container */return mainlist;}SEXP attribute_hidden do_contourLines(SEXP call, SEXP op, SEXP args, SEXP env){SEXP c, x, y, z;int nx, ny, nc;x = CAR(args);internalTypeCheck(call, x, REALSXP);nx = LENGTH(x);args = CDR(args);y = CAR(args);internalTypeCheck(call, y, REALSXP);ny = LENGTH(y);args = CDR(args);z = CAR(args);internalTypeCheck(call, z, REALSXP);args = CDR(args);/* levels */c = CAR(args);internalTypeCheck(call, c, REALSXP);nc = LENGTH(c);args = CDR(args);return GEcontourLines(REAL(x), nx, REAL(y), ny, REAL(z), REAL(c), nc);}/** The *base graphics* function contour() and the *general base** function contourLines() use the same code to generate contour lines* (i.e., the function contourLines())** I had a look at extracting the code that draws the labels* into a *general base* function* (e.g., into some sort of labelLines() function),* but the code is too base-graphics-specific (e.g., one of the* labelling methods seeks the location closest to the edge of the* plotting region) so I've left it alone for now.** This does mean that the contourLines() function is part of the* graphics engine, but the contour() function is part of the* base graphics system.*/static void contour(SEXP x, int nx, SEXP y, int ny, SEXP z,double zc,SEXP labels, int cnum,Rboolean drawLabels, int method,double atom, pGEDevDesc dd){/* draw a contour for one given contour level 'zc' */const void *vmax;double xend, yend;int i, ii, j, jj, ns, dir;SEGP seglist, seg, s, start, end;double *xxx, *yyy;double variance, dX, dY, deltaX, deltaY;double dXC, dYC;int range=0, indx=0, n; /* -Wall */double lowestVariance;double squareSum;int iii, jjj;double distanceSum, labelDistance, avgGradient;char buffer[255];int result;double ux, uy, vx, vy;double xStart, yStart;double dx, dy, dxy;double labelHeight;SEXP label1 = PROTECT(allocVector(REALSXP, 8));SEXP label2;SEXP lab;Rboolean gotLabel = FALSE;Rboolean ddl;/* Don't draw label -- currently unused, i.e. always FALSE*/#ifdef DEBUG_contourRprintf("contour(lev = %g):\n", zc);#endifvmax = vmaxget();ctr_SegDB = contourLines(REAL(x), nx, REAL(y), ny, REAL(z), zc, atom);/* we need to keep ctr_SegDB available, so vmaxset(vmax); was wrong *//* The segment database is now assembled. *//* Begin following contours. *//* 1. Grab a segment *//* 2. Follow its tail *//* 3. Follow its head *//* 4. Draw the contour */for (i = 0; i < nx - 1; i++)for (j = 0; j < ny - 1; j++) {while ((seglist = ctr_SegDB[i + j * nx])) {ii = i; jj = j;start = end = seglist;ctr_SegDB[i + j * nx] = seglist->next;xend = seglist->x1;yend = seglist->y1;while ((dir = ctr_segdir(xend, yend, REAL(x), REAL(y),&ii, &jj, nx, ny))) {ctr_SegDB[ii + jj * nx]= ctr_segupdate(xend, yend, dir, TRUE,/* = tail */ctr_SegDB[ii + jj * nx], &seg);if (!seg) break;end->next = seg;end = seg;xend = end->x1;yend = end->y1;}end->next = NULL; /* <<< new for 1.2.3 */ii = i; jj = j;xend = seglist->x0;yend = seglist->y0;while ((dir = ctr_segdir(xend, yend, REAL(x), REAL(y),&ii, &jj, nx, ny))) {ctr_SegDB[ii + jj * nx]= ctr_segupdate(xend, yend, dir, FALSE,/* ie. head */ctr_SegDB[ii+jj*nx], &seg);if (!seg) break;seg->next = start;start = seg;xend = start->x0;yend = start->y0;}/* ns := #{segments of polyline} -- need to allocate */s = start;ns = 0;/* max_contour_segments: prevent inf.loop (shouldn't be needed) */while (s && ns < max_contour_segments) {ns++;s = s->next;}if(ns == max_contour_segments)warning(_("contour(): circular/long seglist -- set %s > %d?"),"options(\"max.contour.segments\")", max_contour_segments);/* contour midpoint : use for labelling sometime (not yet!)int ns2;if (ns > 3) ns2 = ns/2; else ns2 = -1;*/vmax = vmaxget();xxx = (double *) R_alloc(ns + 1, sizeof(double));yyy = (double *) R_alloc(ns + 1, sizeof(double));/* now have the space, go through again: */s = start;ns = 0;xxx[ns] = s->x0;yyy[ns++] = s->y0;while (s->next && ns < max_contour_segments) {s = s->next;xxx[ns] = s->x0;yyy[ns++] = s->y0;}xxx[ns] = s->x1;yyy[ns++] = s->y1;#ifdef DEBUG_contourRprintf(" [%2d,%2d]: (x,y)[1:%d] = ", i,j, ns);if(ns >= 5)Rprintf(" (%g,%g), (%g,%g), ..., (%g,%g)\n",xxx[0],yyy[0], xxx[1],yyy[1], xxx[ns-1],yyy[ns-1]);elsefor(iii = 0; iii < ns; iii++)Rprintf(" (%g,%g)%s", xxx[iii],yyy[iii],(iii < ns-1) ? "," : "\n");#endif// GMode(1, dd);if (drawLabels) {/* If user supplied labels, use i'th one of themOtherwise stringify the z-value of the contour */cetype_t enc = CE_NATIVE;buffer[0] = ' ';if (!isNull(labels)) {int numl = length(labels);strcpy(&buffer[1], CHAR(STRING_ELT(labels, cnum % numl)));enc = getCharCE(STRING_ELT(labels, cnum % numl));}else {PROTECT(lab = allocVector(REALSXP, 1));REAL(lab)[0] = zc;lab = labelformat(lab);strcpy(&buffer[1], CHAR(STRING_ELT(lab, 0))); /* ASCII */UNPROTECT(1);}buffer[strlen(buffer)+1] = '\0';buffer[strlen(buffer)] = ' ';labelDistance = GStrWidth(buffer, enc, INCHES, dd);labelHeight = GStrHeight(buffer, enc, INCHES, dd);if (labelDistance > 0) {/* Try to find somewhere to draw the label */switch (method) {case 0: /* draw label at one end of contouroverwriting contour line*/if (useStart(xxx, yyy, ns, dd) )indx = 0;elseindx = ns - 1;break;case 1: /* draw label at one end of contourembedded in contourno overlapping labels*/indx = 0;range = 0;gotLabel = FALSE;if (useStart(xxx, yyy, ns, dd)) {iii = 0;n = findGapUp(xxx, yyy, ns, labelDistance, dd);}else {n = findGapDown(xxx, yyy, ns, labelDistance, dd);iii = ns - n - 1;}if (n > 0) {/** Find 4 corners of label extents **/FindCorners(labelDistance, labelHeight, label1,xxx[iii], yyy[iii],xxx[iii+n], yyy[iii+n], dd);/** Test corners for intersection with previous labels **/label2 = labelList;result = 0;while ((result == 0) && (label2 != R_NilValue)) {result = TestLabelIntersection(label1, CAR(label2));label2 = CDR(label2);}if (result == 0) {result = LabelInsideWindow(label1, dd);if (result == 0) {indx = iii;range = n;gotLabel = TRUE;}}}break;case 2: /* draw label on flattest portion of contourembedded in contour lineno overlapping labels*//* Look for flatest sequence of contour gradients */lowestVariance = 9999999; /* A large number */indx = 0;range = 0;gotLabel = FALSE;for (iii = 0; iii < ns; iii++) {distanceSum = 0;avgGradient = 0;squareSum = 0;n = 0;jjj = (iii + 1);while ((jjj < ns-1) &&(distanceSum < labelDistance)) {/* Find a gap big enough for the labeluse several segments if necessary*/dX = xxx[jjj] - xxx[jjj - n - 1];dY = yyy[jjj] - yyy[jjj - n - 1];dXC = GConvertXUnits(dX, USER, INCHES, dd);dYC = GConvertYUnits(dY, USER, INCHES, dd);distanceSum = hypot(dXC, dYC);/* Calculate the variance of the gradientsof the segments that will make way for thelabel*/deltaX = xxx[jjj] - xxx[jjj - 1];deltaY = yyy[jjj] - yyy[jjj - 1];if (deltaX == 0) {deltaX = 1;}avgGradient += (deltaY/deltaX);squareSum += avgGradient * avgGradient;jjj = (jjj + 1);n += 1;}if (distanceSum < labelDistance)break;/** Find 4 corners of label extents **/FindCorners(labelDistance, labelHeight, label1,xxx[iii], yyy[iii],xxx[iii+n], yyy[iii+n], dd);/** Test corners for intersection with previous labels **/label2 = labelList;result = 0;while ((result == 0) && (label2 != R_NilValue)) {result = TestLabelIntersection(label1, CAR(label2));label2 = CDR(label2);}if (result == 0)result = LabelInsideWindow(label1, dd);if (result == 0) {variance = (squareSum - (avgGradient * avgGradient) / n) / n;avgGradient /= n;if (variance < lowestVariance) {lowestVariance = variance;indx = iii;range = n;}}if (lowestVariance < 9999999)gotLabel = TRUE;}} /* switch (method) */if (method == 0) {GPolyline(ns, xxx, yyy, USER, dd);GText(xxx[indx], yyy[indx], USER, buffer,CE_NATIVE/*FIX*/,.5, .5, 0, dd);}else {if (indx > 0)GPolyline(indx+1, xxx, yyy, USER, dd);if (ns-1-indx-range > 0)GPolyline(ns-indx-range, xxx+indx+range, yyy+indx+range,USER, dd);if (gotLabel) {/* find which plot edge we are closest to */int closest; /* 0 = indx, 1 = indx+range */double dx1, dx2, dy1, dy2, dmin;dx1 = fmin2((xxx[indx] - gpptr(dd)->usr[0]),(gpptr(dd)->usr[1] - xxx[indx]));dx2 = fmin2((gpptr(dd)->usr[1] - xxx[indx+range]),(xxx[indx+range] - gpptr(dd)->usr[0]));if (dx1 < dx2) {closest = 0;dmin = dx1;} else {closest = 1;dmin = dx2;}dy1 = fmin2((yyy[indx] - gpptr(dd)->usr[2]),(gpptr(dd)->usr[3] - yyy[indx]));if (closest && (dy1 < dmin)) {closest = 0;dmin = dy1;} else if (dy1 < dmin)dmin = dy1;dy2 = fmin2((gpptr(dd)->usr[3] - yyy[indx+range]),(yyy[indx+range] - gpptr(dd)->usr[2]));if (!closest && (dy2 < dmin))closest = 1;dx = GConvertXUnits(xxx[indx+range] - xxx[indx],USER, INCHES, dd);dy = GConvertYUnits(yyy[indx+range] - yyy[indx],USER, INCHES, dd);dxy = hypot(dx, dy);/* save the current label for checking overlap */label2 = allocVector(REALSXP, 8);FindCorners(labelDistance, labelHeight, label2,xxx[indx], yyy[indx],xxx[indx+range], yyy[indx+range], dd);UNPROTECT_PTR(labelList);labelList = PROTECT(CONS(label2, labelList));ddl = FALSE;/* draw an extra bit of segment if the labeldoesn't fill the gap */if (closest) {xStart = xxx[indx+range] -(xxx[indx+range] - xxx[indx]) *labelDistance / dxy;yStart = yyy[indx+range] -(yyy[indx+range] - yyy[indx]) *labelDistance / dxy;if (labelDistance / dxy < 1)GLine(xxx[indx], yyy[indx],xStart, yStart,USER, dd);} else {xStart = xxx[indx] +(xxx[indx+range] - xxx[indx]) *labelDistance / dxy;yStart = yyy[indx] +(yyy[indx+range] - yyy[indx]) *labelDistance / dxy;if (labelDistance / dxy < 1)GLine(xStart, yStart,xxx[indx+range], yyy[indx+range],USER, dd);}/*** Draw contour labels ***/if (xxx[indx] < xxx[indx+range]) {if (closest) {ux = xStart;uy = yStart;vx = xxx[indx+range];vy = yyy[indx+range];} else {ux = xxx[indx];uy = yyy[indx];vx = xStart;vy = yStart;}}else {if (closest) {ux = xxx[indx+range];uy = yyy[indx+range];vx = xStart;vy = yStart;} else {ux = xStart;uy = yStart;vx = xxx[indx];vy = yyy[indx];}}if (!ddl) {/* convert to INCHES for calculation ofangle to draw text*/GConvert(&ux, &uy, USER, INCHES, dd);GConvert(&vx, &vy, USER, INCHES, dd);/* 0, .5 => left, centre justified */GText (ux, uy, INCHES, buffer,CE_NATIVE/*FIX*/,0, .5,(180 / 3.14) * atan2(vy - uy, vx - ux),dd);}} /* if (gotLabel) */} /* if (method == 0) else ... */} /* if (labelDistance > 0) */} /* if (drawLabels) */else {GPolyline(ns, xxx, yyy, USER, dd);}// GMode(0, dd);vmaxset(vmax);} /* while */} /* for(i .. ) for(j ..) */vmaxset(vmax); /* now we are done with ctr_SegDB */UNPROTECT_PTR(label1); /* pwwwargh! This is messy, but last thingprotected is likely labelList, and that needsto be preserved across calls */}SEXP attribute_hidden Rg_contourDef(void){return ScalarLogical(GEcurrentDevice()->dev->useRotatedTextInContour);}/* contour(x, y, z, levels, labels, labcex, drawlabels,* method, vfont, col = col, lty = lty, lwd = lwd)*/SEXP attribute_hidden do_contour(SEXP call, SEXP op, SEXP args, SEXP env){SEXP oargs, c, x, y, z, vfont, col, rawcol, lty, lwd, labels;int i, j, nx, ny, nc, ncol, nlty, nlwd;int ltysave, fontsave = 1 /* -Wall */;rcolor colsave;double cexsave, lwdsave;double atom, zmin, zmax;const void *vmax, *vmax0;char familysave[201];int method;Rboolean drawLabels;double labcex;pGEDevDesc dd = GEcurrentDevice();SEXP result = R_NilValue;GCheckState(dd);if (length(args) < 4)error(_("too few arguments"));PrintDefaults(); /* prepare for labelformat */oargs = args;x = CAR(args);internalTypeCheck(call, x, REALSXP);nx = LENGTH(x);args = CDR(args);y = CAR(args);internalTypeCheck(call, y, REALSXP);ny = LENGTH(y);args = CDR(args);z = CAR(args);internalTypeCheck(call, z, REALSXP);args = CDR(args);/* levels */c = CAR(args);internalTypeCheck(call, c, REALSXP);nc = LENGTH(c);args = CDR(args);labels = CAR(args);if (!isNull(labels))internalTypeCheck(call, labels, STRSXP);args = CDR(args);labcex = asReal(CAR(args));args = CDR(args);drawLabels = (Rboolean)asLogical(CAR(args));args = CDR(args);method = asInteger(CAR(args)); args = CDR(args);if (method < 1 || method > 3)error(_("invalid '%s' value"), "method");PROTECT(vfont = FixupVFont(CAR(args)));if (!isNull(vfont)) {strncpy(familysave, gpptr(dd)->family, 201);strncpy(gpptr(dd)->family, "Her ", 201);gpptr(dd)->family[3] = (char) INTEGER(vfont)[0];fontsave = gpptr(dd)->font;gpptr(dd)->font = INTEGER(vfont)[1];}args = CDR(args);rawcol = CAR(args);PROTECT(col = FixupCol(rawcol, R_TRANWHITE));ncol = length(col);args = CDR(args);PROTECT(lty = FixupLty(CAR(args), gpptr(dd)->lty));nlty = length(lty);args = CDR(args);PROTECT(lwd = FixupLwd(CAR(args), gpptr(dd)->lwd));nlwd = length(lwd);args = CDR(args);if (nx < 2 || ny < 2)error(_("insufficient 'x' or 'y' values"));if (nrows(z) != nx || ncols(z) != ny)error(_("dimension mismatch"));if (nc < 1)error(_("no contour values"));for (i = 0; i < nx; i++) {if (!R_FINITE(REAL(x)[i]))error(_("missing 'x' values"));if (i > 0 && REAL(x)[i] < REAL(x)[i - 1])error(_("increasing 'x' values expected"));}for (i = 0; i < ny; i++) {if (!R_FINITE(REAL(y)[i]))error(_("missing 'y' values"));if (i > 0 && REAL(y)[i] < REAL(y)[i - 1])error(_("increasing 'y' values expected"));}for (i = 0; i < nc; i++)if (!R_FINITE(REAL(c)[i]))error(_("invalid NA contour values"));zmin = DBL_MAX;zmax = DBL_MIN;for (i = 0; i < nx * ny; i++)if (R_FINITE(REAL(z)[i])) {if (zmax < REAL(z)[i]) zmax = REAL(z)[i];if (zmin > REAL(z)[i]) zmin = REAL(z)[i];}if (zmin >= zmax) {if (zmin == zmax)warning(_("all z values are equal"));elsewarning(_("all z values are NA"));UNPROTECT(4);return R_NilValue;}/* change to 1e-3, reconsidered because of PR#897* but 1e-7, and even 2*DBL_EPSILON do not prevent inf.loop in contour().* maybe something like 16 * DBL_EPSILON * (..).* see also max_contour_segments above */atom = 1e-3 * (zmax - zmin);/* Initialize the segment data base *//* Note we must be careful about resetting *//* the top of the stack, otherwise we run out of *//* memory after a sequence of displaylist replays */vmax0 = vmaxget();ctr_SegDB = (SEGP*)R_alloc(nx*ny, sizeof(SEGP));for (i = 0; i < nx; i++)for (j = 0; j < ny; j++)ctr_SegDB[i + j * nx] = NULL;/* Draw the contours -- note the heap release */ltysave = gpptr(dd)->lty;colsave = gpptr(dd)->col;lwdsave = gpptr(dd)->lwd;cexsave = gpptr(dd)->cex;labelList = PROTECT(R_NilValue);/* draw contour for levels[i] */GMode(1, dd);for (i = 0; i < nc; i++) {vmax = vmaxget();gpptr(dd)->lty = INTEGER(lty)[i % nlty];if (gpptr(dd)->lty == NA_INTEGER)gpptr(dd)->lty = ltysave;if (isNAcol(rawcol, i, ncol))gpptr(dd)->col = colsave;elsegpptr(dd)->col = INTEGER(col)[i % ncol];gpptr(dd)->lwd = REAL(lwd)[i % nlwd];if (!R_FINITE(gpptr(dd)->lwd))gpptr(dd)->lwd = lwdsave;gpptr(dd)->cex = labcex;contour(x, nx, y, ny, z, REAL(c)[i], labels, i,drawLabels, method - 1, atom, dd);vmaxset(vmax);}GMode(0, dd);vmaxset(vmax0);gpptr(dd)->lty = ltysave;gpptr(dd)->col = colsave;gpptr(dd)->lwd = lwdsave;gpptr(dd)->cex = cexsave;if(!isNull(vfont)) {strncpy(gpptr(dd)->family, familysave, 201);gpptr(dd)->font = fontsave;}UNPROTECT(5);/* NOTE: only record operation if no "error" *//* NOTE: on replay, call == R_NilValue */if (GRecording(call, dd))GErecordGraphicOperation(op, oargs, dd);return result;}