Rboolean checkPosRowPosCol(SEXP vp, SEXP parent) { int ncol = layoutNCol(viewportLayout(parent)); int nrow = layoutNRow(viewportLayout(parent)); if (!isNull(viewportLayoutPosRow(vp)) && (INTEGER(viewportLayoutPosRow(vp))[0] < 1 || INTEGER(viewportLayoutPosRow(vp))[1] > nrow)) error(_("invalid 'layout.pos.row'")); if (!isNull(viewportLayoutPosCol(vp)) && (INTEGER(viewportLayoutPosCol(vp))[0] < 1 || INTEGER(viewportLayoutPosCol(vp))[1] > ncol)) error(_("invalid 'layout.pos.col'")); return TRUE; }
/* 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); }