| /* |
| * R : A Computer Language for Statistical Data Analysis |
| * Copyright (C) 2001-3 Paul Murrell |
| * 2003-2014 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 |
| * https://www.R-project.org/Licenses/ |
| */ |
| |
| #include "grid.h" |
| #include <string.h> |
| |
| /* Some access methods for viewports */ |
| SEXP viewportX(SEXP vp) { |
| return VECTOR_ELT(vp, VP_X); |
| } |
| |
| SEXP viewportY(SEXP vp) { |
| return VECTOR_ELT(vp, VP_Y); |
| } |
| |
| SEXP viewportWidth(SEXP vp) { |
| return VECTOR_ELT(vp, VP_WIDTH); |
| } |
| |
| SEXP viewportHeight(SEXP vp) { |
| return VECTOR_ELT(vp, VP_HEIGHT); |
| } |
| |
| Rboolean viewportClip(SEXP vp) { |
| return LOGICAL(VECTOR_ELT(vp, VP_CLIP))[0]; |
| } |
| |
| double viewportXScaleMin(SEXP vp) { |
| return numeric(VECTOR_ELT(vp, VP_XSCALE), 0); |
| } |
| |
| double viewportXScaleMax(SEXP vp) { |
| return numeric(VECTOR_ELT(vp, VP_XSCALE), 1); |
| } |
| |
| double viewportYScaleMin(SEXP vp) { |
| return numeric(VECTOR_ELT(vp, VP_YSCALE), 0); |
| } |
| |
| double viewportYScaleMax(SEXP vp) { |
| return numeric(VECTOR_ELT(vp, VP_YSCALE), 1); |
| } |
| |
| double viewportAngle(SEXP vp) { |
| return numeric(VECTOR_ELT(vp, VP_ANGLE), 0); |
| } |
| |
| SEXP viewportLayout(SEXP vp) { |
| return VECTOR_ELT(vp, VP_LAYOUT); |
| } |
| |
| double viewportHJust(SEXP vp) { |
| return REAL(VECTOR_ELT(vp, VP_VALIDJUST))[0]; |
| } |
| |
| double viewportVJust(SEXP vp) { |
| return REAL(VECTOR_ELT(vp, VP_VALIDJUST))[1]; |
| } |
| |
| SEXP viewportLayoutPosRow(SEXP vp) { |
| return VECTOR_ELT(vp, VP_VALIDLPOSROW); |
| } |
| |
| SEXP viewportLayoutPosCol(SEXP vp) { |
| return VECTOR_ELT(vp, VP_VALIDLPOSCOL); |
| } |
| |
| SEXP viewportgpar(SEXP vp) { |
| return VECTOR_ELT(vp, PVP_GPAR); |
| } |
| |
| const char* viewportFontFamily(SEXP vp) { |
| return CHAR(STRING_ELT(VECTOR_ELT(VECTOR_ELT(vp, PVP_GPAR), GP_FONTFAMILY), |
| 0)); |
| } |
| |
| int viewportFont(SEXP vp) { |
| return INTEGER(VECTOR_ELT(VECTOR_ELT(vp, PVP_GPAR), GP_FONT))[0]; |
| } |
| |
| double viewportFontSize(SEXP vp) { |
| return REAL(VECTOR_ELT(VECTOR_ELT(vp, PVP_GPAR), GP_FONTSIZE))[0]; |
| } |
| |
| double viewportLineHeight(SEXP vp) { |
| return REAL(VECTOR_ELT(VECTOR_ELT(vp, PVP_GPAR), GP_LINEHEIGHT))[0]; |
| } |
| |
| double viewportCex(SEXP vp) { |
| return numeric(VECTOR_ELT(VECTOR_ELT(vp, PVP_GPAR), GP_CEX), 0); |
| } |
| |
| SEXP viewportTransform(SEXP vp) { |
| return VECTOR_ELT(vp, PVP_TRANS); |
| } |
| |
| SEXP viewportLayoutWidths(SEXP vp) { |
| return VECTOR_ELT(vp, PVP_WIDTHS); |
| } |
| |
| SEXP viewportLayoutHeights(SEXP vp) { |
| return VECTOR_ELT(vp, PVP_HEIGHTS); |
| } |
| |
| SEXP viewportWidthCM(SEXP vp) { |
| return VECTOR_ELT(vp, PVP_WIDTHCM); |
| } |
| |
| SEXP viewportHeightCM(SEXP vp) { |
| return VECTOR_ELT(vp, PVP_HEIGHTCM); |
| } |
| |
| SEXP viewportRotation(SEXP vp) { |
| return VECTOR_ELT(vp, PVP_ROTATION); |
| } |
| |
| SEXP viewportClipRect(SEXP vp) { |
| return VECTOR_ELT(vp, PVP_CLIPRECT); |
| } |
| |
| SEXP viewportParent(SEXP vp) { |
| return VECTOR_ELT(vp, PVP_PARENT); |
| } |
| |
| SEXP viewportChildren(SEXP vp) { |
| return VECTOR_ELT(vp, PVP_CHILDREN); |
| } |
| |
| SEXP viewportDevWidthCM(SEXP vp) { |
| return VECTOR_ELT(vp, PVP_DEVWIDTHCM); |
| } |
| |
| SEXP viewportDevHeightCM(SEXP vp) { |
| return VECTOR_ELT(vp, PVP_DEVHEIGHTCM); |
| } |
| |
| SEXP viewportParentGPar(SEXP vp) { |
| return VECTOR_ELT(vp, PVP_PARENTGPAR); |
| } |
| |
| void fillViewportLocationFromViewport(SEXP vp, LViewportLocation *vpl) |
| { |
| vpl->x = viewportX(vp); |
| vpl->y = viewportY(vp); |
| vpl->width = viewportWidth(vp); |
| vpl->height = viewportHeight(vp); |
| vpl->hjust = viewportHJust(vp); |
| vpl->vjust = viewportVJust(vp); |
| } |
| |
| void fillViewportContextFromViewport(SEXP vp, |
| LViewportContext *vpc) |
| { |
| vpc->xscalemin = viewportXScaleMin(vp); |
| vpc->xscalemax = viewportXScaleMax(vp); |
| vpc->yscalemin = viewportYScaleMin(vp); |
| vpc->yscalemax = viewportYScaleMax(vp); |
| } |
| |
| void copyViewportContext(LViewportContext vpc1, LViewportContext *vpc2) |
| { |
| vpc2->xscalemin = vpc1.xscalemin; |
| vpc2->xscalemax = vpc1.xscalemax; |
| vpc2->yscalemin = vpc1.yscalemin; |
| vpc2->yscalemax = vpc1.yscalemax; |
| } |
| |
| void gcontextFromViewport(SEXP vp, const pGEcontext gc, pGEDevDesc dd) { |
| gcontextFromgpar(viewportgpar(vp), 0, gc, dd); |
| } |
| |
| /* 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'; // This picks up the device default |
| /* 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); |
| } |
| |
| void initVP(pGEDevDesc dd) |
| { |
| SEXP vpfnname, vpfn, vp; |
| SEXP xscale, yscale; |
| SEXP currentgp = gridStateElement(dd, GSS_GPAR); |
| SEXP gsd = (SEXP) dd->gesd[gridRegisterIndex]->systemSpecific; |
| PROTECT(vpfnname = findFun(install("grid.top.level.vp"), R_gridEvalEnv)); |
| PROTECT(vpfn = lang1(vpfnname)); |
| PROTECT(vp = eval(vpfn, R_GlobalEnv)); |
| /* |
| * Set the "native" scale of the top viewport to be the |
| * natural device coordinate system (e.g., points in |
| * postscript, pixels in X11, ...) |
| */ |
| PROTECT(xscale = allocVector(REALSXP, 2)); |
| REAL(xscale)[0] = dd->dev->left; |
| REAL(xscale)[1] = dd->dev->right; |
| SET_VECTOR_ELT(vp, VP_XSCALE, xscale); |
| PROTECT(yscale = allocVector(REALSXP, 2)); |
| REAL(yscale)[0] = dd->dev->bottom; |
| REAL(yscale)[1] = dd->dev->top; |
| SET_VECTOR_ELT(vp, VP_YSCALE, yscale); |
| SET_VECTOR_ELT(vp, PVP_GPAR, currentgp); |
| vp = doSetViewport(vp, TRUE, TRUE, dd); |
| SET_VECTOR_ELT(gsd, GSS_VP, vp); |
| UNPROTECT(5); |
| } |
| |