/* The idea is to produce a transformation for this viewport which * will take any location in INCHES and turn it into a location on the * Device in INCHES. * The reason for working in INCHES is because we want to be able to * do rotations as part of the transformation. * If "incremental" is true, then we just work from the "current" * values of the parent. Otherwise, we have to recurse and recalculate * everything from scratch. */ void calcViewportTransform(SEXP vp, SEXP parent, Rboolean incremental, pGEDevDesc dd) { int i, j; double vpWidthCM, vpHeightCM, rotationAngle; double parentWidthCM, parentHeightCM; double xINCHES, yINCHES; double xadj, yadj; double parentAngle; LViewportLocation vpl; LViewportContext vpc, parentContext; R_GE_gcontext gc, parentgc; LTransform thisLocation, thisRotation, thisJustification, thisTransform; LTransform tempTransform, parentTransform, transform; SEXP currentWidthCM, currentHeightCM, currentRotation; SEXP currentTransform; /* This should never be true when we are doing an incremental * calculation */ if (isNull(parent)) { /* We have a top-level viewport; the parent is the device */ getDeviceSize(dd, &parentWidthCM, &parentHeightCM); /* For a device the transform is the identity transform */ identity(parentTransform); /* For a device, xmin=0, ymin=0, xmax=1, ymax=1, and */ parentContext.xscalemin = 0; parentContext.yscalemin = 0; parentContext.xscalemax = 1; parentContext.yscalemax = 1; /* FIXME: How do I figure out the device fontsize ? * From ps.options etc, ... ? * FIXME: How do I figure out the device lineheight ?? * FIXME: How do I figure out the device cex ?? * FIXME: How do I figure out the device font ?? * FIXME: How do I figure out the device fontfamily ?? */ parentgc.ps = 10; parentgc.lineheight = 1.2; parentgc.cex = 1; parentgc.fontface = 1; parentgc.fontfamily[0] = '\0'; /* The device is not rotated */ parentAngle = 0; fillViewportLocationFromViewport(vp, &vpl); } else { /* Get parent transform (etc ...) * If necessary, recalculate the parent transform (etc ...) */ if (!incremental) calcViewportTransform(parent, viewportParent(parent), 0, dd); /* Get information required to transform viewport location */ parentWidthCM = REAL(viewportWidthCM(parent))[0]; parentHeightCM = REAL(viewportHeightCM(parent))[0]; parentAngle = REAL(viewportRotation(parent))[0]; for (i=0; i<3; i++) for (j=0; j<3; j++) parentTransform[i][j] = REAL(viewportTransform(parent))[i +3*j]; fillViewportContextFromViewport(parent, &parentContext); /* * Don't get gcontext from parent because the most recent * previous gpar setting may have come from a gTree * So we look at this viewport's parentgpar slot instead * * WAS gcontextFromViewport(parent, &parentgc); */ gcontextFromgpar(viewportParentGPar(vp), 0, &parentgc, dd); /* In order for the vp to get its vpl from a layout * it must have specified a layout.pos and the parent * must have a layout * FIXME: Actually, in addition, layout.pos.row and * layout.pos.col must be valid for the layout */ if ((isNull(viewportLayoutPosRow(vp)) && isNull(viewportLayoutPosCol(vp))) || isNull(viewportLayout(parent))) fillViewportLocationFromViewport(vp, &vpl); else if (checkPosRowPosCol(vp, parent)) calcViewportLocationFromLayout(viewportLayoutPosRow(vp), viewportLayoutPosCol(vp), parent, &vpl); } /* NOTE that we are not doing a transformLocn here because * we just want locations and dimensions (in INCHES) relative to * the parent, NOT relative to the device. */ /* First, convert the location of the viewport into CM */ xINCHES = transformXtoINCHES(vpl.x, 0, parentContext, &parentgc, parentWidthCM, parentHeightCM, dd); yINCHES = transformYtoINCHES(vpl.y, 0, parentContext, &parentgc, parentWidthCM, parentHeightCM, dd); /* Calculate the width and height of the viewport in CM too * so that any viewports within this one can do transformations */ vpWidthCM = transformWidthtoINCHES(vpl.width, 0, parentContext, &parentgc, parentWidthCM, parentHeightCM, dd)*2.54; vpHeightCM = transformHeighttoINCHES(vpl.height, 0, parentContext, &parentgc, parentWidthCM, parentHeightCM, dd)*2.54; /* Fall out if location or size are non-finite */ if (!R_FINITE(xINCHES) || !R_FINITE(yINCHES) || !R_FINITE(vpWidthCM) || !R_FINITE(vpHeightCM)) error(_("Non-finite location and/or size for viewport")); /* Determine justification required */ justification(vpWidthCM, vpHeightCM, vpl.hjust, vpl.vjust, &xadj, &yadj); /* Next, produce the transformation to add the location of * the viewport to the location. */ /* Produce transform for this viewport */ translation(xINCHES, yINCHES, thisLocation); if (viewportAngle(vp) != 0) rotation(viewportAngle(vp), thisRotation); else identity(thisRotation); translation(xadj/2.54, yadj/2.54, thisJustification); /* Position relative to origin of rotation THEN rotate. */ multiply(thisJustification, thisRotation, tempTransform); /* Translate to bottom-left corner. */ multiply(tempTransform, thisLocation, thisTransform); /* Combine with parent's transform */ multiply(thisTransform, parentTransform, transform); /* Sum up the rotation angles */ rotationAngle = parentAngle + viewportAngle(vp); /* Finally, allocate the rows and columns for this viewport's * layout if it has one */ if (!isNull(viewportLayout(vp))) { fillViewportContextFromViewport(vp, &vpc); gcontextFromViewport(vp, &gc, dd); calcViewportLayout(vp, vpWidthCM, vpHeightCM, vpc, &gc, dd); } /* Record all of the answers in the viewport * (the layout calculations are done within calcViewportLayout) */ PROTECT(currentWidthCM = ScalarReal(vpWidthCM)); PROTECT(currentHeightCM = ScalarReal(vpHeightCM)); PROTECT(currentRotation = ScalarReal(rotationAngle)); PROTECT(currentTransform = allocMatrix(REALSXP, 3, 3)); for (i=0; i<3; i++) for (j=0; j<3; j++) REAL(currentTransform)[i + 3*j] = transform[i][j]; SET_VECTOR_ELT(vp, PVP_WIDTHCM, currentWidthCM); SET_VECTOR_ELT(vp, PVP_HEIGHTCM, currentHeightCM); SET_VECTOR_ELT(vp, PVP_ROTATION, currentRotation); SET_VECTOR_ELT(vp, PVP_TRANS, currentTransform); UNPROTECT(4); }
SEXP gridCallback(GEevent task, pGEDevDesc dd, SEXP data) { SEXP result = R_NilValue; SEXP valid, scale; SEXP gridState; GESystemDesc *sd; SEXP currentgp; SEXP gsd; SEXP devsize; R_GE_gcontext gc; switch (task) { case GE_InitState: /* Create the initial grid state for a device */ PROTECT(gridState = createGridSystemState()); /* Store that state with the device for easy retrieval */ sd = dd->gesd[gridRegisterIndex]; sd->systemSpecific = (void*) gridState; /* Initialise the grid state for a device */ fillGridSystemState(gridState, dd); /* Also store the state beneath a top-level variable so * that it does not get garbage-collected */ globaliseState(gridState); /* Indicate success */ result = R_BlankString; UNPROTECT(1); break; case GE_FinaliseState: sd = dd->gesd[gridRegisterIndex]; /* Simply detach the system state from the global variable * and it will be garbage-collected */ deglobaliseState((SEXP) sd->systemSpecific); /* Also set the device pointer to NULL */ sd->systemSpecific = NULL; break; case GE_SaveState: break; case GE_RestoreState: gsd = (SEXP) dd->gesd[gridRegisterIndex]->systemSpecific; PROTECT(devsize = allocVector(REALSXP, 2)); getDeviceSize(dd, &(REAL(devsize)[0]), &(REAL(devsize)[1])); SET_VECTOR_ELT(gsd, GSS_DEVSIZE, devsize); UNPROTECT(1); /* Only bother to do any grid drawing setup * if there has been grid output * on this device. */ if (LOGICAL(gridStateElement(dd, GSS_GRIDDEVICE))[0]) { if (LOGICAL(gridStateElement(dd, GSS_ENGINEDLON))[0]) { /* The graphics engine is about to replay the display list * So we "clear" the device and reset the grid graphics state */ /* There are two main situations in which this occurs: * (i) a screen is resized * In this case, it is ok-ish to do a GENewPage * because that has the desired effect and no * undesirable effects because it only happens on * a screen device -- a new page is the same as * clearing the screen * (ii) output on one device is copied to another device * In this case, a GENewPage is NOT a good thing, however, * here we will start with a new device and it will not * have any grid output so this section will not get called * SO we will not get any unwanted blank pages. * * All this is a bit fragile; ultimately, what would be ideal * is a dev->clearPage primitive for all devices in addition * to the dev->newPage primitive */ currentgp = gridStateElement(dd, GSS_GPAR); gcontextFromgpar(currentgp, 0, &gc, dd); GENewPage(&gc, dd); initGPar(dd); initVP(dd); initOtherState(dd); } else { /* * If we have turned off the graphics engine's display list * then we have to redraw the scene ourselves */ SEXP fcall; PROTECT(fcall = lang1(install("draw.all"))); eval(fcall, R_gridEvalEnv); UNPROTECT(1); } } break; case GE_CopyState: break; case GE_CheckPlot: PROTECT(valid = allocVector(LGLSXP, 1)); LOGICAL(valid)[0] = TRUE; UNPROTECT(1); result = valid; case GE_SaveSnapshotState: break; case GE_RestoreSnapshotState: break; case GE_ScalePS: /* * data is a numeric scale factor */ PROTECT(scale = allocVector(REALSXP, 1)); REAL(scale)[0] = REAL(gridStateElement(dd, GSS_SCALE))[0]* REAL(data)[0]; setGridStateElement(dd, GSS_SCALE, scale); UNPROTECT(1); break; } return result; }
void gcontextFromViewport(SEXP vp, const pGEcontext gc, pGEDevDesc dd) { gcontextFromgpar(viewportgpar(vp), 0, gc, dd); }