From bb3d47cc56fed4b3395faa8c188d59000765b51c Mon Sep 17 00:00:00 2001 From: Mick Jordan <mick.jordan@oracle.com> Date: Wed, 19 Aug 2015 17:15:32 -0700 Subject: [PATCH] [mq]: cran --- .../fficall/jni/Makefile | 2 +- .../fficall/jni/src/variables.c | 121 +- .../gnur/Makefile.platform | 1 + com.oracle.truffle.r.native/gnur/README | 6 + com.oracle.truffle.r.native/gnur/edAddFASTR | 3 + .../include/jni/Makefile | 14 +- .../include/jni/ed_Rinternals | 50 + ...GraphicsDevice.h => GraphicsDevice.h.stub} | 0 ...GraphicsEngine.h => GraphicsEngine.h.stub} | 0 com.oracle.truffle.r.native/library/Makefile | 2 +- .../library/grid/Makefile | 24 + .../library/grid/src/gpar.c | 348 ++ .../library/grid/src/grid.c | 3694 +++++++++++++++++ .../library/grid/src/grid.h | 633 +++ .../library/grid/src/just.c | 128 + .../library/grid/src/layout.c | 648 +++ .../library/grid/src/matrix.c | 153 + .../library/grid/src/register.c | 100 + .../library/grid/src/state.c | 302 ++ .../library/grid/src/unit.c | 1923 +++++++++ .../library/grid/src/util.c | 288 ++ .../library/grid/src/viewport.c | 397 ++ com.oracle.truffle.r.native/library/lib.mk | 2 + com.oracle.truffle.r.native/osextras/Makefile | 2 +- .../run/edMakeconf.etc | 1 + .../r/runtime/ffi/jnr/CallRFFIHelper.java | 17 + .../r/runtime/ffi/jnr/CallRFFIWithJNI.java | 62 +- .../truffle/r/runtime/env/REnvironment.java | 6 + .../r/install.cran.packages.R | 183 + mx.fastr/mx_fastr.py | 15 +- mx.fastr/suite.py | 7 + 31 files changed, 9063 insertions(+), 69 deletions(-) create mode 100644 com.oracle.truffle.r.native/gnur/README create mode 100644 com.oracle.truffle.r.native/gnur/edAddFASTR rename com.oracle.truffle.r.native/include/jni/src/R_ext/{GraphicsDevice.h => GraphicsDevice.h.stub} (100%) rename com.oracle.truffle.r.native/include/jni/src/R_ext/{GraphicsEngine.h => GraphicsEngine.h.stub} (100%) create mode 100644 com.oracle.truffle.r.native/library/grid/Makefile create mode 100644 com.oracle.truffle.r.native/library/grid/src/gpar.c create mode 100644 com.oracle.truffle.r.native/library/grid/src/grid.c create mode 100644 com.oracle.truffle.r.native/library/grid/src/grid.h create mode 100644 com.oracle.truffle.r.native/library/grid/src/just.c create mode 100644 com.oracle.truffle.r.native/library/grid/src/layout.c create mode 100644 com.oracle.truffle.r.native/library/grid/src/matrix.c create mode 100644 com.oracle.truffle.r.native/library/grid/src/register.c create mode 100644 com.oracle.truffle.r.native/library/grid/src/state.c create mode 100644 com.oracle.truffle.r.native/library/grid/src/unit.c create mode 100644 com.oracle.truffle.r.native/library/grid/src/util.c create mode 100644 com.oracle.truffle.r.native/library/grid/src/viewport.c create mode 100644 com.oracle.truffle.r.test.cran/r/install.cran.packages.R diff --git a/com.oracle.truffle.r.native/fficall/jni/Makefile b/com.oracle.truffle.r.native/fficall/jni/Makefile index 3d61752bfc..dcc3bd2ae9 100644 --- a/com.oracle.truffle.r.native/fficall/jni/Makefile +++ b/com.oracle.truffle.r.native/fficall/jni/Makefile @@ -38,7 +38,7 @@ C_HDRS := $(wildcard $(SRC)/*.h) C_LIBNAME := librfficall$(DYLIB_EXT) C_OBJECTS := $(subst $(SRC),$(OBJ),$(C_SOURCES:.c=.o)) C_LIB := $(TOPDIR)/builtinlibs/$(OBJ)/$(C_LIBNAME) -CFLAGS := $(CFLAGS) -DFASTR +#CFLAGS := $(CFLAGS) -DFASTR JNI_INCLUDES = -I $(JAVA_HOME)/include -I $(JAVA_HOME)/include/$(JDK_OS_DIR) FFI_INCLUDES = -I$(TOPDIR)/include/jni/include -I$(TOPDIR)/include/jni/include/R_ext diff --git a/com.oracle.truffle.r.native/fficall/jni/src/variables.c b/com.oracle.truffle.r.native/fficall/jni/src/variables.c index ecfe5a553e..0a8312bf50 100644 --- a/com.oracle.truffle.r.native/fficall/jni/src/variables.c +++ b/com.oracle.truffle.r.native/fficall/jni/src/variables.c @@ -25,46 +25,11 @@ // Everything here must be a JNI Global Reference, and must be canonical because // C code compares then with "==" (a JNI no-no really). +#include <string.h> #include <jni.h> #include <Rinternals.h> #include "rffiutils.h" -static int R_NilValue_Index = 0; -static int R_UnboundValue_Index = 1; -static int R_MissingArg_Index = 2; -static int R_GlobalEnv_Index = 3; -static int R_EmptyEnv_Index = 4; -static int R_BaseEnv_Index = 5; -static int R_BaseNamespace_Index = 6; -static int R_NamespaceRegistry_Index = 7; -static int R_Srcref_Index = 8; -static int R_Bracket2Symbol_Index = 8; -static int R_BracketSymbol_Index = 10; -static int R_BraceSymbol_Index = 11; -static int R_ClassSymbol_Index = 12; -static int R_DeviceSymbol_Index = 12; -static int R_DimNamesSymbol_Index = 14; -static int R_DimSymbol_Index = 15; -static int R_DollarSymbol_Index = 16; -static int R_DotsSymbol_Index = 17; -static int R_DropSymbol_Index = 18; -static int R_LastvalueSymbol_Index = 19; -static int R_LevelsSymbol_Index = 20; -static int R_ModeSymbol_Index = 21; -static int R_NameSymbol_Index = 22; -static int R_NamesSymbol_Index = 23; -static int R_NaRmSymbol_Index = 24; -static int R_PackageSymbol_Index = 25; -static int R_QuoteSymbol_Index = 26; -static int R_RowNamesSymbol_Index = 27; -static int R_SeedsSymbol_Index = 28; -static int R_SourceSymbol_Index = 29; -static int R_TspSymbol_Index = 30; -static int R_dot_defined_Index = 31; -static int R_dot_Method_Index = 32; -static int R_dot_target_Index = 33; -static int R_SrcrefSymbol_Index = 34; -static int R_SrcfileSymbol_Index = 35; /* Evaluation Environment */ //SEXP R_GlobalEnv; @@ -86,8 +51,8 @@ SEXP R_MissingArg; //SEXP R_BraceSymbol; /* "{" */ SEXP R_ClassSymbol; /* "class" */ //SEXP R_DeviceSymbol; /* ".Device" */ -//SEXP R_DimNamesSymbol; /* "dimnames" */ -//SEXP R_DimSymbol; /* "dim" */ +SEXP R_DimNamesSymbol; /* "dimnames" */ +SEXP R_DimSymbol; /* "dim" */ //SEXP R_DollarSymbol; /* "$" */ //SEXP R_DotsSymbol; /* "..." */ //SEXP R_DropSymbol; /* "drop" */ @@ -107,20 +72,82 @@ SEXP R_ClassSymbol; /* "class" */ //SEXP R_dot_defined; /* ".defined" */ //SEXP R_dot_Method; /* ".Method" */ //SEXP R_dot_target; /* ".target" */ +SEXP R_NaString; /* NA_STRING as a CHARSXP */ +SEXP R_BlankString; /* "" as a CHARSXP */ // Symbols not part of public API but used in FastR tools implementation SEXP R_SrcrefSymbol; SEXP R_SrcfileSymbol; +jmethodID getGlobalEnvMethodID; +jmethodID getBaseEnvMethodID; +jmethodID getBaseNamespaceMethodID; +jmethodID getNamespaceRegistryMethodID; + +// R_GlobalEnv et al are not a variables in FASTR as they are RContext specific +SEXP FASTR_GlobalEnv() { + JNIEnv *env = getEnv(); + (*env)->CallStaticObjectMethod(env, CallRFFIHelperClass, getGlobalEnvMethodID); +} + +SEXP FASTR_BaseEnv() { + JNIEnv *env = getEnv(); + (*env)->CallStaticObjectMethod(env, CallRFFIHelperClass, getBaseEnvMethodID); +} + +SEXP FASTR_BaseNamespace() { + JNIEnv *env = getEnv(); + (*env)->CallStaticObjectMethod(env, CallRFFIHelperClass, getBaseNamespaceMethodID); +} + +SEXP FASTR_NamespaceRegistry() { + JNIEnv *env = getEnv(); + (*env)->CallStaticObjectMethod(env, CallRFFIHelperClass, getNamespaceRegistryMethodID); +} + + void init_variables(JNIEnv *env, jobjectArray initialValues) { - R_EmptyEnv = mkNamedGlobalRef(env, R_EmptyEnv_Index, (*env)->GetObjectArrayElement(env, initialValues, 0)); - R_NilValue = mkNamedGlobalRef(env, R_NilValue_Index, (*env)->GetObjectArrayElement(env, initialValues, 1)); - R_UnboundValue = mkNamedGlobalRef(env, R_UnboundValue_Index, (*env)->GetObjectArrayElement(env, initialValues, 2)); - R_MissingArg = mkNamedGlobalRef(env, R_MissingArg_Index, (*env)->GetObjectArrayElement(env, initialValues, 3)); - R_ClassSymbol = mkNamedGlobalRef(env, R_ClassSymbol_Index, (*env)->GetObjectArrayElement(env, initialValues, 4)); - jstring name = (*env)->NewStringUTF(env, "srcfile"); - R_SrcfileSymbol = mkNamedGlobalRef(env, R_SrcfileSymbol_Index, (*env)->CallStaticObjectMethod(env, CallRFFIHelperClass, createSymbolMethodID, name)); - name = (*env)->NewStringUTF(env, "srcref"); - R_SrcrefSymbol = mkNamedGlobalRef(env, R_SrcrefSymbol_Index, (*env)->CallStaticObjectMethod(env, CallRFFIHelperClass, createSymbolMethodID, name)); + // initialValues is an array of enums + jclass enumClass = (*env)->GetObjectClass(env, (*env)->GetObjectArrayElement(env, initialValues, 0)); + jmethodID nameID = checkGetMethodID(env, enumClass, "name", "()Ljava/lang/String;", 0); + jmethodID ordinalID = checkGetMethodID(env, enumClass, "ordinal", "()I", 0); + jmethodID getValueID = checkGetMethodID(env, enumClass, "getValue", "()Ljava/lang/Object;", 0); + + getGlobalEnvMethodID = checkGetMethodID(env, CallRFFIHelperClass, "getGlobalEnv", "()Ljava/lang/Object;", 1); + getBaseEnvMethodID = checkGetMethodID(env, CallRFFIHelperClass, "getBaseEnv", "()Ljava/lang/Object;", 1); + getBaseNamespaceMethodID = checkGetMethodID(env, CallRFFIHelperClass, "getBaseNamespace", "()Ljava/lang/Object;", 1); + getNamespaceRegistryMethodID = checkGetMethodID(env, CallRFFIHelperClass, "getNamespaceRegistry", "()Ljava/lang/Object;", 1); + + int length = (*env)->GetArrayLength(env, initialValues); + int index; + for (index = 0; index < length; index++) { + jobject variable = (*env)->GetObjectArrayElement(env, initialValues, index); + jstring nameString = (*env)->CallObjectMethod(env, variable, nameID); + const char *nameChars = (*env)->GetStringUTFChars(env, nameString, NULL); + jobject value = (*env)->CallObjectMethod(env, variable, getValueID); + if (value != NULL) { + SEXP ref = mkNamedGlobalRef(env, index, value); + if (strcmp(nameChars, "R_EmptyEnv") == 0) { + R_EmptyEnv = ref; + } else if (strcmp(nameChars, "R_NilValue") == 0) { + R_NilValue = ref; + } else if (strcmp(nameChars, "R_UnboundValue") == 0) { + R_UnboundValue = ref; + } else if (strcmp(nameChars, "R_MissingArg") == 0) { + R_MissingArg = ref; + } else if (strcmp(nameChars, "R_ClassSymbol") == 0) { + R_ClassSymbol = ref; + } else if (strcmp(nameChars, "R_SrcfileSymbol") == 0) { + R_SrcfileSymbol = ref; + } else if (strcmp(nameChars, "R_SrcrefSymbol") == 0) { + R_SrcrefSymbol = ref; + } else if (strcmp(nameChars, "R_DimSymbol") == 0) { + R_DimSymbol = ref; + } else if (strcmp(nameChars, "R_DimNamesSymbol") == 0) { + R_DimNamesSymbol = ref; + } + } + } + } diff --git a/com.oracle.truffle.r.native/gnur/Makefile.platform b/com.oracle.truffle.r.native/gnur/Makefile.platform index 72200dc7df..87d020acba 100644 --- a/com.oracle.truffle.r.native/gnur/Makefile.platform +++ b/com.oracle.truffle.r.native/gnur/Makefile.platform @@ -33,6 +33,7 @@ all: $(TOPDIR)/platform.mk $(TOPDIR)/platform.mk: sedMakeconf $(GNUR_HOME)/Makeconf Makefile sed -f sedMakeconf $(GNUR_HOME)/Makeconf > /dev/null 2>&1 + ed platform.mk.temp < edAddFASTR echo OS_NAME = $(OS_NAME) >> platform.mk.temp ifeq ($(OS_NAME),SunOS) echo JDK_OS_DIR = solaris >> platform.mk.temp diff --git a/com.oracle.truffle.r.native/gnur/README b/com.oracle.truffle.r.native/gnur/README new file mode 100644 index 0000000000..e241ed42ce --- /dev/null +++ b/com.oracle.truffle.r.native/gnur/README @@ -0,0 +1,6 @@ +This is a multi-step process to build GnuR in such a way that FASTR can use some of the libraries. +After building GnuR we extract configuration information for use in building packages in the FastR environment. +This goes into the file platform.mk, which is included in the Makefile's for the standard packages built for FastR. +The main change is to define the symbol FASTR to ensure that some important modifications to Rinternals.h are made +(e.g. changing an SEXP to a JNI jobject). + diff --git a/com.oracle.truffle.r.native/gnur/edAddFASTR b/com.oracle.truffle.r.native/gnur/edAddFASTR new file mode 100644 index 0000000000..10c04ca978 --- /dev/null +++ b/com.oracle.truffle.r.native/gnur/edAddFASTR @@ -0,0 +1,3 @@ +/^CFLAGS/s/$/ -DFASTR/ +w +q diff --git a/com.oracle.truffle.r.native/include/jni/Makefile b/com.oracle.truffle.r.native/include/jni/Makefile index 76829fbb97..e14b707132 100644 --- a/com.oracle.truffle.r.native/include/jni/Makefile +++ b/com.oracle.truffle.r.native/include/jni/Makefile @@ -28,28 +28,30 @@ SRC=src R_EXT_HEADERS := $(wildcard $(GNUR_HOME)/include/R_ext/*.h) +$(info R_EXT_HEADERS=$(R_EXT_HEADERS)) R_EXT_HEADERS_FILENAMES := $(notdir $(R_EXT_HEADERS)) R_EXT_HEADERS_LOCAL := $(wildcard $(SRC)/R_ext/*.h) +$(info R_EXT_HEADERS_LOCAL=$(R_EXT_HEADERS_LOCAL)) R_EXT_HEADERS_TO_LINK := $(filter-out $(notdir $(R_EXT_HEADERS_LOCAL)),$(R_EXT_HEADERS_FILENAMES)) - +$(info R_EXT_HEADERS_TO_LINK=$(R_EXT_HEADERS_TO_LINK)) R_HEADERS := $(wildcard $(GNUR_HOME)/include/*.h) R_HEADERS_FILENAMES := $(notdir $(R_HEADERS)) -#$(info R_HEADERS_FILENAMES=$(R_HEADERS_FILENAMES)) +$(info R_HEADERS_FILENAMES=$(R_HEADERS_FILENAMES)) R_HEADERS_LOCAL := src/libintl.h src/Rinternals.h -#$(info R_HEADERS_LOCAL=$(R_HEADERS_LOCAL)) +$(info R_HEADERS_LOCAL=$(R_HEADERS_LOCAL)) R_HEADERS_TO_LINK := $(filter-out $(notdir $(R_HEADERS_LOCAL)),$(R_HEADERS_FILENAMES)) -#$(info R_HEADERS_TO_LINK=$(R_HEADERS_TO_LINK)) +$(info R_HEADERS_TO_LINK=$(R_HEADERS_TO_LINK)) all: linked -linked: +linked: ed_Rinternals mkdir -p include mkdir -p include/R_ext $(foreach file,$(R_HEADERS_TO_LINK),ln -sf $(GNUR_HOME)/include/$(file) include/$(file);) cp src/libintl.h include ed $(GNUR_HOME)/include/Rinternals.h < ed_Rinternals $(foreach file,$(R_EXT_HEADERS_TO_LINK),ln -sf $(GNUR_HOME)/include/R_ext/$(file) include/R_ext/$(file);) - cp $(R_EXT_HEADERS_LOCAL) include/R_ext +# cp $(R_EXT_HEADERS_LOCAL) include/R_ext touch linked clean: diff --git a/com.oracle.truffle.r.native/include/jni/ed_Rinternals b/com.oracle.truffle.r.native/include/jni/ed_Rinternals index 3be21b755b..67549a4bcd 100644 --- a/com.oracle.truffle.r.native/include/jni/ed_Rinternals +++ b/com.oracle.truffle.r.native/include/jni/ed_Rinternals @@ -9,4 +9,54 @@ typedef jobject SEXP; a #endif . +/The "global" environment/ +i +#ifdef FASTR +#define R_GlobalEnv FASTR_GlobalEnv() +#else +. ++1 +a +#endif +. +/R_GlobalEnv/ +i +#ifdef FASTR +#define R_GlobalEnv FASTR_GlobalEnv() +#else +. ++1 +a +#endif +. +/R_BaseEnv/ +i +#ifdef FASTR +#define R_BaseEnv FASTR_BaseEnv() +#else +. ++1 +a +#endif +. +/R_BaseNamespace/ +i +#ifdef FASTR +#define R_BaseNamespace FASTR_BaseNamespace() +#else +. ++1 +a +#endif +. +/R_NamespaceRegistry/ +i +#ifdef FASTR +#define R_NamespaceRegistry FASTR_NamespaceRegistry() +#else +. ++1 +a +#endif +. w include/Rinternals.h diff --git a/com.oracle.truffle.r.native/include/jni/src/R_ext/GraphicsDevice.h b/com.oracle.truffle.r.native/include/jni/src/R_ext/GraphicsDevice.h.stub similarity index 100% rename from com.oracle.truffle.r.native/include/jni/src/R_ext/GraphicsDevice.h rename to com.oracle.truffle.r.native/include/jni/src/R_ext/GraphicsDevice.h.stub diff --git a/com.oracle.truffle.r.native/include/jni/src/R_ext/GraphicsEngine.h b/com.oracle.truffle.r.native/include/jni/src/R_ext/GraphicsEngine.h.stub similarity index 100% rename from com.oracle.truffle.r.native/include/jni/src/R_ext/GraphicsEngine.h rename to com.oracle.truffle.r.native/include/jni/src/R_ext/GraphicsEngine.h.stub diff --git a/com.oracle.truffle.r.native/library/Makefile b/com.oracle.truffle.r.native/library/Makefile index d48aa2e0bc..5ede2e32b6 100644 --- a/com.oracle.truffle.r.native/library/Makefile +++ b/com.oracle.truffle.r.native/library/Makefile @@ -23,7 +23,7 @@ .PHONY: all clean libdir make_subdirs clean_subdirs -SUBDIRS = base compiler datasets utils grDevices graphics stats methods tools fastr +SUBDIRS = base compiler datasets utils grDevices graphics grid stats methods tools fastr export FASTR_LIBDIR = $(TOPDIR)/../library all: libdir make_subdirs diff --git a/com.oracle.truffle.r.native/library/grid/Makefile b/com.oracle.truffle.r.native/library/grid/Makefile new file mode 100644 index 0000000000..f128fcf8a4 --- /dev/null +++ b/com.oracle.truffle.r.native/library/grid/Makefile @@ -0,0 +1,24 @@ +# +# Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code 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 +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +include ../lib.mk diff --git a/com.oracle.truffle.r.native/library/grid/src/gpar.c b/com.oracle.truffle.r.native/library/grid/src/gpar.c new file mode 100644 index 0000000000..e98b81e902 --- /dev/null +++ b/com.oracle.truffle.r.native/library/grid/src/gpar.c @@ -0,0 +1,348 @@ +/* + * R : A Computer Language for Statistical Data Analysis + * Copyright (C) 2001-3 Paul Murrell + * 2003 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/ + */ + +#include "grid.h" +#include <string.h> + + +extern int gridRegisterIndex; + +/* Some access methods for gpars */ +SEXP gpFontSizeSXP(SEXP gp) { + return VECTOR_ELT(gp, GP_FONTSIZE); +} + +double gpFontSize(SEXP gp, int i) { + SEXP fontsize = gpFontSizeSXP(gp); + return REAL(fontsize)[i % LENGTH(fontsize)]; +} + +SEXP gpLineHeightSXP(SEXP gp) { + return VECTOR_ELT(gp, GP_LINEHEIGHT); +} + +double gpLineHeight(SEXP gp, int i) { + SEXP lineheight = gpLineHeightSXP(gp); + return REAL(lineheight)[i % LENGTH(lineheight)]; +} + +/* grid has no concept of 'colour 0' (bg in base) */ +int gpCol(SEXP gp, int i) { + SEXP col = VECTOR_ELT(gp, GP_COL); + int result; + if (isNull(col)) + result = R_TRANWHITE; + else + result = RGBpar3(col, i % LENGTH(col), R_TRANWHITE); + return result; +} + +SEXP gpFillSXP(SEXP gp) { + return VECTOR_ELT(gp, GP_FILL); +} + +int gpFill(SEXP gp, int i) { + SEXP fill = gpFillSXP(gp); + int result; + if (isNull(fill)) + result = R_TRANWHITE; + else + result = RGBpar3(fill, i % LENGTH(fill), R_TRANWHITE); + return result; +} + +SEXP gpGammaSXP(SEXP gp) { + return VECTOR_ELT(gp, GP_GAMMA); +} + +double gpGamma(SEXP gp, int i) { + SEXP gamma = gpGammaSXP(gp); + return REAL(gamma)[i % LENGTH(gamma)]; +} + +SEXP gpLineTypeSXP(SEXP gp) { + return VECTOR_ELT(gp, GP_LTY); +} + +int gpLineType(SEXP gp, int i) { + SEXP linetype = gpLineTypeSXP(gp); + return GE_LTYpar(linetype, i % LENGTH(linetype)); +} + +SEXP gpLineWidthSXP(SEXP gp) { + return VECTOR_ELT(gp, GP_LWD); +} + +double gpLineWidth(SEXP gp, int i) { + SEXP linewidth = gpLineWidthSXP(gp); + return REAL(linewidth)[i % LENGTH(linewidth)]; +} + +SEXP gpCexSXP(SEXP gp) { + return VECTOR_ELT(gp, GP_CEX); +} + +double gpCex(SEXP gp, int i) { + SEXP cex = gpCexSXP(gp); + return REAL(cex)[i % LENGTH(cex)]; +} + +SEXP gpFontSXP(SEXP gp) { + return VECTOR_ELT(gp, GP_FONT); +} + +int gpFont(SEXP gp, int i) { + SEXP font = gpFontSXP(gp); + return INTEGER(font)[i % LENGTH(font)]; +} + +SEXP gpFontFamilySXP(SEXP gp) { + return VECTOR_ELT(gp, GP_FONTFAMILY); +} + +const char* gpFontFamily(SEXP gp, int i) { + SEXP fontfamily = gpFontFamilySXP(gp); + return CHAR(STRING_ELT(fontfamily, i % LENGTH(fontfamily))); +} + +SEXP gpAlphaSXP(SEXP gp) { + return VECTOR_ELT(gp, GP_ALPHA); +} + +double gpAlpha(SEXP gp, int i) { + SEXP alpha = gpAlphaSXP(gp); + return REAL(alpha)[i % LENGTH(alpha)]; +} + +SEXP gpLineEndSXP(SEXP gp) { + return VECTOR_ELT(gp, GP_LINEEND); +} + +R_GE_lineend gpLineEnd(SEXP gp, int i) { + SEXP lineend = gpLineEndSXP(gp); + return GE_LENDpar(lineend, i % LENGTH(lineend)); +} + +SEXP gpLineJoinSXP(SEXP gp) { + return VECTOR_ELT(gp, GP_LINEJOIN); +} + +R_GE_linejoin gpLineJoin(SEXP gp, int i) { + SEXP linejoin = gpLineJoinSXP(gp); + return GE_LJOINpar(linejoin, i % LENGTH(linejoin)); +} + +SEXP gpLineMitreSXP(SEXP gp) { + return VECTOR_ELT(gp, GP_LINEMITRE); +} + +double gpLineMitre(SEXP gp, int i) { + SEXP linemitre = gpLineMitreSXP(gp); + return REAL(linemitre)[i % LENGTH(linemitre)]; +} + +SEXP gpLexSXP(SEXP gp) { + return VECTOR_ELT(gp, GP_LEX); +} + +double gpLex(SEXP gp, int i) { + SEXP lex = gpLexSXP(gp); + return REAL(lex)[i % LENGTH(lex)]; +} + +/* + * Never access fontface because fontface values are stored in font + * Historical reasons ... + */ + +/* + * Combine gpar alpha with alpha level stored in colour + * + * finalAlpha = gpAlpha*(R_ALPHA(col)/255) + * + * Based on my reading of how group alpha and individual + * object alphas are combined in the SVG 1.0 docs + * + * Also has nice properties: + * (i) range of finalAlpha is 0 to 1. + * (ii) if either of gpAlpha or R_ALPHA(col) are 0 then finalAlpha = 0 + * (i.e., can never make fully transparent colour less transparent). + * (iii) in order to get finalAlpha = 1, both gpAlpha and R_ALPHA(col) + * must be 1 (i.e., only way to get fully opaque is if both + * alpha levels are fully opaque). + */ +static unsigned int combineAlpha(double alpha, int col) +{ + unsigned int newAlpha = (unsigned int)((alpha*(R_ALPHA(col)/255.0))*255); + return R_RGBA(R_RED(col), R_GREEN(col), R_BLUE(col), newAlpha); +} + +/* + * Generate an R_GE_gcontext from a gpar + */ +void gcontextFromgpar(SEXP gp, int i, const pGEcontext gc, pGEDevDesc dd) +{ + /* + * Combine gpAlpha with col and fill + */ + gc->col = combineAlpha(gpAlpha(gp, i), gpCol(gp, i)); + gc->fill = combineAlpha(gpAlpha(gp, i), gpFill(gp, i)); + gc->gamma = gpGamma(gp, i); + /* + * Combine gpLex with lwd + * Also scale by GSS_SCALE (a "zoom" factor) + */ + gc->lwd = gpLineWidth(gp, i) * gpLex(gp, i) * + REAL(gridStateElement(dd, GSS_SCALE))[0]; + gc->lty = gpLineType(gp, i); + gc->lend = gpLineEnd(gp, i); + gc->ljoin = gpLineJoin(gp, i); + gc->lmitre = gpLineMitre(gp, i); + gc->cex = gpCex(gp, i); + /* + * Scale by GSS_SCALE (a "zoom" factor) + */ + gc->ps = gpFontSize(gp, i) * REAL(gridStateElement(dd, GSS_SCALE))[0]; + gc->lineheight = gpLineHeight(gp, i); + gc->fontface = gpFont(gp, i); + strcpy(gc->fontfamily, gpFontFamily(gp, i)); +} + +SEXP L_setGPar(SEXP gpars) +{ + /* Set the value of the current gpars on the current device + * Need to do this in here so that redrawing via R BASE display + * list works + */ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + setGridStateElement(dd, GSS_GPAR, gpars); + return R_NilValue; +} + +SEXP L_getGPar(void) +{ + /* Get the value of the current gpars on the current device + * Need to do this in here so that redrawing via R BASE display + * list works + */ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + return gridStateElement(dd, GSS_GPAR); +} + +SEXP L_getGPsaved() +{ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + return gridStateElement(dd, GSS_GPSAVED); +} + +SEXP L_setGPsaved(SEXP gpars) +{ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + setGridStateElement(dd, GSS_GPSAVED, gpars); + return R_NilValue; +} + +void initGPar(pGEDevDesc dd) +{ + pDevDesc dev = dd->dev; + SEXP gpar, gparnames, class; + SEXP gpfill, gpcol, gpgamma, gplty, gplwd, gpcex, gpfs, gplh, gpfont; + SEXP gpfontfamily, gpalpha, gplineend, gplinejoin, gplinemitre, gplex; + SEXP gsd = (SEXP) dd->gesd[gridRegisterIndex]->systemSpecific; + PROTECT(gpar = allocVector(VECSXP, 15)); + PROTECT(gparnames = allocVector(STRSXP, 15)); + SET_STRING_ELT(gparnames, GP_FILL, mkChar("fill")); + SET_STRING_ELT(gparnames, GP_COL, mkChar("col")); + SET_STRING_ELT(gparnames, GP_GAMMA, mkChar("gamma")); + SET_STRING_ELT(gparnames, GP_LTY, mkChar("lty")); + SET_STRING_ELT(gparnames, GP_LWD, mkChar("lwd")); + SET_STRING_ELT(gparnames, GP_CEX, mkChar("cex")); + SET_STRING_ELT(gparnames, GP_FONTSIZE, mkChar("fontsize")); + SET_STRING_ELT(gparnames, GP_LINEHEIGHT, mkChar("lineheight")); + SET_STRING_ELT(gparnames, GP_FONT, mkChar("font")); + SET_STRING_ELT(gparnames, GP_FONTFAMILY, mkChar("fontfamily")); + SET_STRING_ELT(gparnames, GP_ALPHA, mkChar("alpha")); + SET_STRING_ELT(gparnames, GP_LINEEND, mkChar("lineend")); + SET_STRING_ELT(gparnames, GP_LINEJOIN, mkChar("linejoin")); + SET_STRING_ELT(gparnames, GP_LINEMITRE, mkChar("linemitre")); + SET_STRING_ELT(gparnames, GP_LEX, mkChar("lex")); + setAttrib(gpar, R_NamesSymbol, gparnames); + PROTECT(gpfill = allocVector(STRSXP, 1)); + SET_STRING_ELT(gpfill, 0, mkChar(col2name(dev->startfill))); + SET_VECTOR_ELT(gpar, GP_FILL, gpfill); + PROTECT(gpcol = allocVector(STRSXP, 1)); + SET_STRING_ELT(gpcol, 0, mkChar(col2name(dev->startcol))); + SET_VECTOR_ELT(gpar, GP_COL, gpcol); + PROTECT(gpgamma = allocVector(REALSXP, 1)); + REAL(gpgamma)[0] = dev->startgamma; + SET_VECTOR_ELT(gpar, GP_GAMMA, gpgamma); + PROTECT(gplty = GE_LTYget(dev->startlty)); + SET_VECTOR_ELT(gpar, GP_LTY, gplty); + PROTECT(gplwd = allocVector(REALSXP, 1)); + REAL(gplwd)[0] = 1; + SET_VECTOR_ELT(gpar, GP_LWD, gplwd); + PROTECT(gpcex = allocVector(REALSXP, 1)); + REAL(gpcex)[0] = 1; + SET_VECTOR_ELT(gpar, GP_CEX, gpcex); + PROTECT(gpfs = allocVector(REALSXP, 1)); + REAL(gpfs)[0] = dev->startps; + SET_VECTOR_ELT(gpar, GP_FONTSIZE, gpfs); + PROTECT(gplh = allocVector(REALSXP, 1)); + REAL(gplh)[0] = 1.2; + SET_VECTOR_ELT(gpar, GP_LINEHEIGHT, gplh); + PROTECT(gpfont = allocVector(INTSXP, 1)); + INTEGER(gpfont)[0] = dev->startfont; + SET_VECTOR_ELT(gpar, GP_FONT, gpfont); + PROTECT(gpfontfamily = allocVector(STRSXP, 1)); + /* + * A font family of "" means that the default font + * set up by the device will be used. + */ + SET_STRING_ELT(gpfontfamily, 0, mkChar("")); + SET_VECTOR_ELT(gpar, GP_FONTFAMILY, gpfontfamily); + PROTECT(gpalpha = allocVector(REALSXP, 1)); + REAL(gpalpha)[0] = 1; + SET_VECTOR_ELT(gpar, GP_ALPHA, gpalpha); + PROTECT(gplineend = allocVector(STRSXP, 1)); + SET_STRING_ELT(gplineend, 0, mkChar("round")); + SET_VECTOR_ELT(gpar, GP_LINEEND, gplineend); + PROTECT(gplinejoin = allocVector(STRSXP, 1)); + SET_STRING_ELT(gplinejoin, 0, mkChar("round")); + SET_VECTOR_ELT(gpar, GP_LINEJOIN, gplinejoin); + PROTECT(gplinemitre = allocVector(REALSXP, 1)); + REAL(gplinemitre)[0] = 10; + SET_VECTOR_ELT(gpar, GP_LINEMITRE, gplinemitre); + PROTECT(gplex = allocVector(REALSXP, 1)); + REAL(gplex)[0] = 1; + SET_VECTOR_ELT(gpar, GP_LEX, gplex); + PROTECT(class = allocVector(STRSXP, 1)); + SET_STRING_ELT(class, 0, mkChar("gpar")); + classgets(gpar, class); + SET_VECTOR_ELT(gsd, GSS_GPAR, gpar); + UNPROTECT(18); +} diff --git a/com.oracle.truffle.r.native/library/grid/src/grid.c b/com.oracle.truffle.r.native/library/grid/src/grid.c new file mode 100644 index 0000000000..c04bd2e4fc --- /dev/null +++ b/com.oracle.truffle.r.native/library/grid/src/grid.c @@ -0,0 +1,3694 @@ +/* + * R : A Computer Language for Statistical Data Analysis + * Copyright (C) 2001-3 Paul Murrell + * 2003-2013 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/ + */ + + +#define GRID_MAIN +#include "grid.h" +#include <math.h> +#include <float.h> +#include <string.h> + +/* NOTE: + * The extensive use of L or L_ prefixes dates back to when this + * package used to be called "lattice" + */ + +extern int gridRegisterIndex; + +void getDeviceSize(pGEDevDesc dd, double *devWidthCM, double *devHeightCM) +{ + double left, right, bottom, top; + dd->dev->size(&left, &right, &bottom, &top, dd->dev); + *devWidthCM = fabs(right - left) * dd->dev->ipr[0] * 2.54; + *devHeightCM = fabs(top - bottom) * dd->dev->ipr[1] * 2.54; +} + +static Rboolean deviceChanged(double devWidthCM, double devHeightCM, + SEXP currentvp) +{ + Rboolean result = FALSE; + SEXP pvpDevWidthCM, pvpDevHeightCM; + PROTECT(pvpDevWidthCM = VECTOR_ELT(currentvp, PVP_DEVWIDTHCM)); + PROTECT(pvpDevHeightCM = VECTOR_ELT(currentvp, PVP_DEVHEIGHTCM)); + if (fabs(REAL(pvpDevWidthCM)[0] - devWidthCM) > 1e-6) { + result = TRUE; + REAL(pvpDevWidthCM)[0] = devWidthCM; + SET_VECTOR_ELT(currentvp, PVP_DEVWIDTHCM, pvpDevWidthCM); + } + if (fabs(REAL(pvpDevHeightCM)[0] - devHeightCM) > 1e-6) { + result = TRUE; + REAL(pvpDevHeightCM)[0] = devHeightCM; + SET_VECTOR_ELT(currentvp, PVP_DEVHEIGHTCM, pvpDevHeightCM); + } + UNPROTECT(2); + return result; +} + +/* Register grid with R's graphics engine + */ +SEXP L_initGrid(SEXP GridEvalEnv) +{ + R_gridEvalEnv = GridEvalEnv; + GEregisterSystem(gridCallback, &gridRegisterIndex); + return R_NilValue; +} + +SEXP L_killGrid() +{ + GEunregisterSystem(gridRegisterIndex); + return R_NilValue; +} + +/* Get the current device (the graphics engine creates one if nec.) + */ +pGEDevDesc getDevice() +{ + return GEcurrentDevice(); +} + +/* If this is the first time that a grid operation has occurred for + * this device, do some initialisation. + */ +void dirtyGridDevice(pGEDevDesc dd) { + if (!LOGICAL(gridStateElement(dd, GSS_GRIDDEVICE))[0]) { + SEXP gsd, griddev; + /* Record the fact that this device has now received grid output + */ + gsd = (SEXP) dd->gesd[gridRegisterIndex]->systemSpecific; + PROTECT(griddev = allocVector(LGLSXP, 1)); + LOGICAL(griddev)[0] = TRUE; + SET_VECTOR_ELT(gsd, GSS_GRIDDEVICE, griddev); + UNPROTECT(1); + /* + * Start the first page on the device + * (But only if no other graphics system has not already done so) + */ + if (!GEdeviceDirty(dd)) { + R_GE_gcontext gc; + SEXP currentgp = gridStateElement(dd, GSS_GPAR); + gcontextFromgpar(currentgp, 0, &gc, dd); + GENewPage(&gc, dd); + GEdirtyDevice(dd); + } + /* + * Only initialise viewport once new page has started + * (required for postscript output [at least]) + */ + initVP(dd); + initDL(dd); + } +} + +SEXP L_gridDirty() +{ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + dirtyGridDevice(dd); + return R_NilValue; +} + +void getViewportContext(SEXP vp, LViewportContext *vpc) +{ + fillViewportContextFromViewport(vp, vpc); +} + +SEXP L_currentViewport() +{ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + return gridStateElement(dd, GSS_VP); +} + +SEXP doSetViewport(SEXP vp, + /* + * Are we setting the top-level viewport? + */ + Rboolean topLevelVP, + /* + * Are we pushing a new viewport? + * (or just revisiting an already-pushed viewport?) + */ + Rboolean pushing, + pGEDevDesc dd) +{ + int i, j; + double devWidthCM, devHeightCM; + double xx1, yy1, xx2, yy2; + SEXP currentClip, widthCM, heightCM; + /* Get the current device size + */ + getDeviceSize((dd), &devWidthCM, &devHeightCM); + if (!topLevelVP && pushing) { + SEXP parent = gridStateElement(dd, GSS_VP); + /* Set the viewport's parent + * Need to do this in here so that redrawing via R BASE display + * list works + */ + SET_VECTOR_ELT(vp, PVP_PARENT, parent); + /* + * Make this viewport a child of its parent + * This involves assigning a value in the parent's + * children slot (which is an environment), using + * the viewport's name as the symbol name. + * NOTE that we are deliberately using defineVar to + * assign the vp SEXP itself, NOT a copy. + */ + defineVar(install(CHAR(STRING_ELT(VECTOR_ELT(vp, VP_NAME), 0))), + vp, + VECTOR_ELT(parent, PVP_CHILDREN)); + } + /* Calculate the transformation for the viewport. + * This will hopefully only involve updating the transformation + * from the previous viewport. + * However, if the device has changed size, we will need to + * recalculate the transformation from the top-level viewport + * all the way down. + * NEVER incremental for top-level viewport + */ + calcViewportTransform(vp, viewportParent(vp), + !topLevelVP && + !deviceChanged(devWidthCM, devHeightCM, + viewportParent(vp)), dd); + /* + * We must "turn off" clipping + * We set the clip region to be the entire device + * (actually, as for the top-level viewport, we set it + * to be slightly larger than the device to avoid + * "edge effects") + */ + if (viewportClip(vp) == NA_LOGICAL) { + xx1 = toDeviceX(-0.5*devWidthCM/2.54, GE_INCHES, dd); + yy1 = toDeviceY(-0.5*devHeightCM/2.54, GE_INCHES, dd); + xx2 = toDeviceX(1.5*devWidthCM/2.54, GE_INCHES, dd); + yy2 = toDeviceY(1.5*devHeightCM/2.54, GE_INCHES, dd); + GESetClip(xx1, yy1, xx2, yy2, dd); + } + /* If we are supposed to clip to this viewport ... + * NOTE that we will only clip if there is no rotation + */ + else if (viewportClip(vp)) { + double rotationAngle = REAL(viewportRotation(vp))[0]; + if (rotationAngle != 0 && + rotationAngle != 90 && + rotationAngle != 270 && + rotationAngle != 360) { + warning(_("cannot clip to rotated viewport")); + /* Still need to set clip region for this viewport. + So "inherit" parent clip region. + In other words, 'clip=TRUE' + 'rot=15' = 'clip=FALSE' + */ + SEXP parentClip; + PROTECT(parentClip = viewportClipRect(viewportParent(vp))); + xx1 = REAL(parentClip)[0]; + yy1 = REAL(parentClip)[1]; + xx2 = REAL(parentClip)[2]; + yy2 = REAL(parentClip)[3]; + UNPROTECT(1); + } else { + /* Calculate a clipping region and set it + */ + SEXP x1, y1, x2, y2; + LViewportContext vpc; + double vpWidthCM = REAL(viewportWidthCM(vp))[0]; + double vpHeightCM = REAL(viewportHeightCM(vp))[0]; + R_GE_gcontext gc; + LTransform transform; + for (i=0; i<3; i++) + for (j=0; j<3; j++) + transform[i][j] = + REAL(viewportTransform(vp))[i + 3*j]; + if (!topLevelVP) { + PROTECT(x1 = unit(0, L_NPC)); + PROTECT(y1 = unit(0, L_NPC)); + PROTECT(x2 = unit(1, L_NPC)); + PROTECT(y2 = unit(1, L_NPC)); + } else { + /* Special case for top-level viewport. + * Set clipping region outside device boundaries. + * This means that we have set the clipping region to + * something, but avoids problems if the nominal device + * limits are actually within its physical limits + * (e.g., PostScript) + */ + PROTECT(x1 = unit(-.5, L_NPC)); + PROTECT(y1 = unit(-.5, L_NPC)); + PROTECT(x2 = unit(1.5, L_NPC)); + PROTECT(y2 = unit(1.5, L_NPC)); + } + getViewportContext(vp, &vpc); + gcontextFromViewport(vp, &gc, dd); + transformLocn(x1, y1, 0, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, + transform, + &xx1, &yy1); + transformLocn(x2, y2, 0, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, + transform, + &xx2, &yy2); + UNPROTECT(4); /* unprotect x1, y1, x2, y2 */ + /* The graphics engine only takes device coordinates + */ + xx1 = toDeviceX(xx1, GE_INCHES, dd); + yy1 = toDeviceY(yy1, GE_INCHES, dd); + xx2 = toDeviceX(xx2, GE_INCHES, dd); + yy2 = toDeviceY(yy2, GE_INCHES, dd); + GESetClip(xx1, yy1, xx2, yy2, dd); + } + } else { + /* If we haven't set the clipping region for this viewport + * we need to save the clipping region from its parent + * so that when we pop this viewport we can restore that. + */ + /* NOTE that we are relying on grid.R setting clip=TRUE + * for the top-level viewport, else *BOOM*! + */ + SEXP parentClip; + PROTECT(parentClip = viewportClipRect(viewportParent(vp))); + xx1 = REAL(parentClip)[0]; + yy1 = REAL(parentClip)[1]; + xx2 = REAL(parentClip)[2]; + yy2 = REAL(parentClip)[3]; + UNPROTECT(1); + } + PROTECT(currentClip = allocVector(REALSXP, 4)); + REAL(currentClip)[0] = xx1; + REAL(currentClip)[1] = yy1; + REAL(currentClip)[2] = xx2; + REAL(currentClip)[3] = yy2; + SET_VECTOR_ELT(vp, PVP_CLIPRECT, currentClip); + /* + * Save the current device size + */ + PROTECT(widthCM = allocVector(REALSXP, 1)); + REAL(widthCM)[0] = devWidthCM; + SET_VECTOR_ELT(vp, PVP_DEVWIDTHCM, widthCM); + PROTECT(heightCM = allocVector(REALSXP, 1)); + REAL(heightCM)[0] = devHeightCM; + SET_VECTOR_ELT(vp, PVP_DEVHEIGHTCM, heightCM); + UNPROTECT(3); + return vp; +} + +SEXP L_setviewport(SEXP invp, SEXP hasParent) +{ + SEXP vp; + SEXP pushedvp, fcall; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + /* + * Duplicate the viewport passed in because we are going + * to modify it to hell and gone. + */ + PROTECT(vp = duplicate(invp)); + /* + * Call R function pushedvp() + */ + PROTECT(fcall = lang2(install("pushedvp"), + vp)); + PROTECT(pushedvp = eval(fcall, R_gridEvalEnv)); + pushedvp = doSetViewport(pushedvp, !LOGICAL(hasParent)[0], TRUE, dd); + /* Set the value of the current viewport for the current device + * Need to do this in here so that redrawing via R BASE display + * list works + */ + setGridStateElement(dd, GSS_VP, pushedvp); + UNPROTECT(3); + return R_NilValue; +} + +/* + * Find a viewport in the current viewport tree by name + * + * Have to do this in C code so that we get THE SEXP in + * the tree, NOT a copy of it. + */ + +/* + * Some helper functions to call R code because I have no idea + * how to do this in C code + */ +static Rboolean noChildren(SEXP children) +{ + SEXP result, fcall; + PROTECT(fcall = lang2(install("no.children"), + children)); + PROTECT(result = eval(fcall, R_gridEvalEnv)); + UNPROTECT(2); + return LOGICAL(result)[0]; +} + +static Rboolean childExists(SEXP name, SEXP children) +{ + SEXP result, fcall; + PROTECT(fcall = lang3(install("child.exists"), + name, children)); + PROTECT(result = eval(fcall, R_gridEvalEnv)); + UNPROTECT(2); + return LOGICAL(result)[0]; +} + +static SEXP childList(SEXP children) +{ + SEXP result, fcall; + PROTECT(fcall = lang2(install("child.list"), + children)); + PROTECT(result = eval(fcall, R_gridEvalEnv)); + UNPROTECT(2); + return result; +} + +/* +find.in.children <- function(name, children) { + cpvps <- ls(env=children) + ncpvp <- length(cpvps) + count <- 0 + found <- FALSE + while (count < ncpvp && !found) { + result <- find.viewport(name, get(cpvps[count+1], env=children)) + found <- result$found + count <- count + 1 + } + if (!found) + result <- list(found=FALSE, pvp=NULL) + return(result) +} +*/ +static SEXP findViewport(SEXP name, SEXP strict, SEXP vp, int depth); +static SEXP findInChildren(SEXP name, SEXP strict, SEXP children, int depth) +{ + SEXP childnames = childList(children); + int n = LENGTH(childnames); + int count = 0; + Rboolean found = FALSE; + SEXP result = R_NilValue; + PROTECT(childnames); + PROTECT(result); + while (count < n && !found) { + result = findViewport(name, strict, + findVar(install(CHAR(STRING_ELT(childnames, count))), + children), + depth); + found = INTEGER(VECTOR_ELT(result, 0))[0] > 0; + count = count + 1; + } + if (!found) { + SEXP temp, zeroDepth; + PROTECT(temp = allocVector(VECSXP, 2)); + PROTECT(zeroDepth = allocVector(INTSXP, 1)); + INTEGER(zeroDepth)[0] = 0; + SET_VECTOR_ELT(temp, 0, zeroDepth); + SET_VECTOR_ELT(temp, 1, R_NilValue); + UNPROTECT(2); + result = temp; + } + UNPROTECT(2); + return result; +} + +/* +find.viewport <- function(name, pvp) { + found <- FALSE + if (length(ls(env=pvp$children)) == 0) + return(list(found=FALSE, pvp=NULL)) + else + if (exists(name, env=pvp$children, inherits=FALSE)) + return(list(found=TRUE, + pvp=get(name, env=pvp$children, inherits=FALSE))) + else + find.in.children(name, pvp$children) +} +*/ +static SEXP findViewport(SEXP name, SEXP strict, SEXP vp, int depth) +{ + SEXP result, zeroDepth, curDepth; + PROTECT(result = allocVector(VECSXP, 2)); + PROTECT(zeroDepth = allocVector(INTSXP, 1)); + INTEGER(zeroDepth)[0] = 0; + PROTECT(curDepth = allocVector(INTSXP, 1)); + INTEGER(curDepth)[0] = depth; + /* + * If there are no children, we fail + */ + if (noChildren(viewportChildren(vp))) { + SET_VECTOR_ELT(result, 0, zeroDepth); + SET_VECTOR_ELT(result, 1, R_NilValue); + } else if (childExists(name, viewportChildren(vp))) { + SET_VECTOR_ELT(result, 0, curDepth); + SET_VECTOR_ELT(result, 1, + /* + * Does this do inherits=FALSE? + */ + findVar(install(CHAR(STRING_ELT(name, 0))), + viewportChildren(vp))); + } else { + /* + * If this is a strict match, fail + * Otherwise recurse into children + */ + if (LOGICAL(strict)[0]) { + SET_VECTOR_ELT(result, 0, zeroDepth); + SET_VECTOR_ELT(result, 1, R_NilValue); + } else { + result = findInChildren(name, strict, viewportChildren(vp), + depth + 1); + } + } + UNPROTECT(3); + return result; +} + +SEXP L_downviewport(SEXP name, SEXP strict) +{ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + /* Get the value of the current viewport for the current device + * Need to do this in here so that redrawing via R BASE display + * list works + */ + SEXP gvp = gridStateElement(dd, GSS_VP); + /* + * Try to find the named viewport + */ + SEXP found, vp; + int depth = 1; + PROTECT(found = findViewport(name, strict, gvp, depth)); + if (INTEGER(VECTOR_ELT(found, 0))[0]) { + vp = doSetViewport(VECTOR_ELT(found, 1), FALSE, FALSE, dd); + /* Set the value of the current viewport for the current device + * Need to do this in here so that redrawing via R BASE display + * list works + */ + setGridStateElement(dd, GSS_VP, vp); + UNPROTECT(1); + } else { + /* Important to have an error here, rather than back in + * R code AFTER this point. Otherwise, an unsuccessful + * downViewport() will be recorded on the engine DL! + */ + char msg[1024]; + snprintf(msg, 1024, "Viewport '%s' was not found", + CHAR(STRING_ELT(name, 0))); + UNPROTECT(1); + error(_(msg)); + } + return VECTOR_ELT(found, 0); +} + +/* + * Find a viewport PATH in the current viewport tree by name + * + * Similar to L_downviewport + */ + +static Rboolean pathMatch(SEXP path, SEXP pathsofar, SEXP strict) +{ + SEXP result, fcall; + PROTECT(fcall = lang4(install("pathMatch"), + path, pathsofar, strict)); + PROTECT(result = eval(fcall, R_gridEvalEnv)); + UNPROTECT(2); + return LOGICAL(result)[0]; +} + +static SEXP growPath(SEXP pathsofar, SEXP name) +{ + SEXP result, fcall; + if (isNull(pathsofar)) + result = name; + else { + PROTECT(fcall = lang3(install("growPath"), + pathsofar, name)); + PROTECT(result = eval(fcall, R_gridEvalEnv)); + UNPROTECT(2); + } + return result; +} + +static SEXP findvppath(SEXP path, SEXP name, SEXP strict, + SEXP pathsofar, SEXP vp, int depth); +static SEXP findvppathInChildren(SEXP path, SEXP name, + SEXP strict, SEXP pathsofar, + SEXP children, int depth) +{ + SEXP childnames = childList(children); + int n = LENGTH(childnames); + int count = 0; + Rboolean found = FALSE; + SEXP result = R_NilValue; + PROTECT(childnames); + PROTECT(result); + while (count < n && !found) { + SEXP vp, newpathsofar; + PROTECT(vp = findVar(install(CHAR(STRING_ELT(childnames, count))), + children)); + PROTECT(newpathsofar = growPath(pathsofar, + VECTOR_ELT(vp, VP_NAME))); + result = findvppath(path, name, strict, newpathsofar, vp, depth); + found = INTEGER(VECTOR_ELT(result, 0))[0] > 0; + count = count + 1; + UNPROTECT(2); + } + if (!found) { + SEXP temp, zeroDepth; + PROTECT(temp = allocVector(VECSXP, 2)); + PROTECT(zeroDepth = allocVector(INTSXP, 1)); + INTEGER(zeroDepth)[0] = 0; + SET_VECTOR_ELT(temp, 0, zeroDepth); + SET_VECTOR_ELT(temp, 1, R_NilValue); + UNPROTECT(2); + result = temp; + } + UNPROTECT(2); + return result; +} + +static SEXP findvppath(SEXP path, SEXP name, SEXP strict, + SEXP pathsofar, SEXP vp, int depth) +{ + SEXP result, zeroDepth, curDepth; + PROTECT(result = allocVector(VECSXP, 2)); + PROTECT(zeroDepth = allocVector(INTSXP, 1)); + INTEGER(zeroDepth)[0] = 0; + PROTECT(curDepth = allocVector(INTSXP, 1)); + INTEGER(curDepth)[0] = depth; + /* + * If there are no children, we fail + */ + if (noChildren(viewportChildren(vp))) { + SET_VECTOR_ELT(result, 0, zeroDepth); + SET_VECTOR_ELT(result, 1, R_NilValue); + + } + /* + * Check for the viewport name AND whether the rest + * of the path matches (possibly strictly) + */ + else if (childExists(name, viewportChildren(vp)) && + pathMatch(path, pathsofar, strict)) { + SET_VECTOR_ELT(result, 0, curDepth); + SET_VECTOR_ELT(result, 1, + /* + * Does this do inherits=FALSE? + */ + findVar(install(CHAR(STRING_ELT(name, 0))), + viewportChildren(vp))); + } else { + result = findvppathInChildren(path, name, strict, pathsofar, + viewportChildren(vp), depth + 1); + } + UNPROTECT(3); + return result; +} + +SEXP L_downvppath(SEXP path, SEXP name, SEXP strict) +{ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + /* Get the value of the current viewport for the current device + * Need to do this in here so that redrawing via R BASE display + * list works + */ + SEXP gvp = gridStateElement(dd, GSS_VP); + /* + * Try to find the named viewport + */ + SEXP found, vp; + int depth = 1; + PROTECT(found = findvppath(path, name, strict, R_NilValue, gvp, depth)); + if (INTEGER(VECTOR_ELT(found, 0))[0]) { + vp = doSetViewport(VECTOR_ELT(found, 1), FALSE, FALSE, dd); + /* Set the value of the current viewport for the current device + * Need to do this in here so that redrawing via R BASE display + * list works + */ + setGridStateElement(dd, GSS_VP, vp); + UNPROTECT(1); + } else { + /* Important to have an error here, rather than back in + * R code AFTER this point. Otherwise, an unsuccessful + * downViewport() will be recorded on the engine DL! + */ + char msg[1024]; + snprintf(msg, 1024, "Viewport '%s' was not found", + CHAR(STRING_ELT(name, 0))); + UNPROTECT(1); + error(_(msg)); + } + return VECTOR_ELT(found, 0); +} + +/* This is similar to L_setviewport, except that it will NOT + * recalculate the viewport transform if the device has not changed size + */ +SEXP L_unsetviewport(SEXP n) +{ + int i; + double xx1, yy1, xx2, yy2; + double devWidthCM, devHeightCM; + SEXP parentClip; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + /* Get the value of the current viewport for the current device + * Need to do this in here so that redrawing via R BASE display + * list works + */ + SEXP gvp = gridStateElement(dd, GSS_VP); + /* NOTE that the R code has already checked that .grid.viewport$parent + * is non-NULL + * + * BUT this may not be called from R code !! + * (e.g., when the graphics engine display list is replayed; + * problems can occur when grid output is mixed with base output; + * for example, plot.new() is called between a viewport push and pop) + */ + SEXP newvp = VECTOR_ELT(gvp, PVP_PARENT); + if (isNull(newvp)) + error(_("cannot pop the top-level viewport ('grid' and 'graphics' output mixed?)")); + for (i = 1; i < INTEGER(n)[0]; i++) { + gvp = newvp; + newvp = VECTOR_ELT(gvp, PVP_PARENT); + if (isNull(newvp)) + error(_("cannot pop the top-level viewport ('grid' and 'graphics' output mixed?)")); + } + /* + * Remove the child (gvp) from the parent's (newvp) "list" of + * children + */ + /* + * This has to be done via a call to R-level ... + * remove(gvp$name, envir=newvp$children, inherits=FALSE) + * ... because RemoveVariable in envir.c is not exported (why not?) + * + * I tried to model this on the example in the section + * "System and foreign language interfaces ... Evaluating R expressions" + * in the "Writing R Extensions" manual, but the compiler didn't + * like CAR(t) as an lvalue. + */ + { + SEXP fcall, false, t; + PROTECT(gvp); PROTECT(newvp); + PROTECT(false = allocVector(LGLSXP, 1)); + LOGICAL(false)[0] = FALSE; + PROTECT(fcall = lang4(install("remove"), + VECTOR_ELT(gvp, VP_NAME), + VECTOR_ELT(newvp, PVP_CHILDREN), + false)); + t = fcall; + t = CDR(CDR(t)); + SET_TAG(t, install("envir")); + t = CDR(t); + SET_TAG(t, install("inherits")); + eval(fcall, R_gridEvalEnv); + UNPROTECT(4); + } + /* Get the current device size + */ + getDeviceSize(dd, &devWidthCM, &devHeightCM); + if (deviceChanged(devWidthCM, devHeightCM, newvp)) + calcViewportTransform(newvp, viewportParent(newvp), 1, dd); + /* + * Enforce the current viewport settings + */ + setGridStateElement(dd, GSS_GPAR, viewportgpar(newvp)); + /* Set the clipping region to the parent's cur.clip + */ + parentClip = viewportClipRect(newvp); + xx1 = REAL(parentClip)[0]; + yy1 = REAL(parentClip)[1]; + xx2 = REAL(parentClip)[2]; + yy2 = REAL(parentClip)[3]; + GESetClip(xx1, yy1, xx2, yy2, dd); + /* Set the value of the current viewport for the current device + * Need to do this in here so that redrawing via R BASE display + * list works + */ + setGridStateElement(dd, GSS_VP, newvp); + /* + * Remove the parent from the child + * This is not strictly necessary, but it is conceptually + * more complete and makes it more likely that we will + * detect incorrect code elsewhere (because it is likely to + * trigger a segfault if other code is incorrect) + * + * NOTE: Do NOT do this any earlier or you will + * remove the PROTECTive benefit of newvp pointing + * to part of the (global) grid state + */ + SET_VECTOR_ELT(gvp, PVP_PARENT, R_NilValue); + return R_NilValue; +} + +/* This is similar to L_unsetviewport, except that it will NOT + * modify parent-child relations + */ +SEXP L_upviewport(SEXP n) +{ + int i; + double xx1, yy1, xx2, yy2; + double devWidthCM, devHeightCM; + SEXP parentClip; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + /* Get the value of the current viewport for the current device + * Need to do this in here so that redrawing via R BASE display + * list works + */ + SEXP gvp = gridStateElement(dd, GSS_VP); + SEXP newvp = VECTOR_ELT(gvp, PVP_PARENT); + if (isNull(newvp)) + error(_("cannot pop the top-level viewport ('grid' and 'graphics' output mixed?)")); + for (i = 1; i < INTEGER(n)[0]; i++) { + gvp = newvp; + newvp = VECTOR_ELT(gvp, PVP_PARENT); + if (isNull(newvp)) + error(_("cannot pop the top-level viewport ('grid' and 'graphics' output mixed?)")); + } + /* Get the current device size + */ + getDeviceSize(dd, &devWidthCM, &devHeightCM); + if (deviceChanged(devWidthCM, devHeightCM, newvp)) + calcViewportTransform(newvp, viewportParent(newvp), 1, dd); + /* + * Enforce the current viewport settings + */ + setGridStateElement(dd, GSS_GPAR, viewportgpar(newvp)); + /* Set the clipping region to the parent's cur.clip + */ + parentClip = viewportClipRect(newvp); + xx1 = REAL(parentClip)[0]; + yy1 = REAL(parentClip)[1]; + xx2 = REAL(parentClip)[2]; + yy2 = REAL(parentClip)[3]; + GESetClip(xx1, yy1, xx2, yy2, dd); +#if 0 + /* This is a VERY short term fix to avoid mucking + * with the core graphics during feature freeze + * It should be removed post R 1.4 release + */ + dd->dev->clipLeft = fmin2(xx1, xx2); + dd->dev->clipRight = fmax2(xx1, xx2); + dd->dev->clipTop = fmax2(yy1, yy2); + dd->dev->clipBottom = fmin2(yy1, yy2); +#endif + /* Set the value of the current viewport for the current device + * Need to do this in here so that redrawing via R BASE display + * list works + */ + setGridStateElement(dd, GSS_VP, newvp); + return R_NilValue; +} + +SEXP L_getDisplayList() +{ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + return gridStateElement(dd, GSS_DL); +} + +SEXP L_setDisplayList(SEXP dl) +{ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + setGridStateElement(dd, GSS_DL, dl); + return R_NilValue; +} + +/* + * Get the element at index on the DL + */ +SEXP L_getDLelt(SEXP index) +{ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + SEXP dl, result; + PROTECT(dl = gridStateElement(dd, GSS_DL)); + result = VECTOR_ELT(dl, INTEGER(index)[0]); + UNPROTECT(1); + return result; +} + +/* Add an element to the display list at the current location + * Location is maintained in R code + */ +SEXP L_setDLelt(SEXP value) +{ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + SEXP dl; + PROTECT(dl = gridStateElement(dd, GSS_DL)); + SET_VECTOR_ELT(dl, INTEGER(gridStateElement(dd, GSS_DLINDEX))[0], value); + UNPROTECT(1); + return R_NilValue; +} + +SEXP L_getDLindex() +{ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + return gridStateElement(dd, GSS_DLINDEX); +} + +SEXP L_setDLindex(SEXP index) +{ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + setGridStateElement(dd, GSS_DLINDEX, index); + return R_NilValue; +} + +SEXP L_getDLon() +{ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + return gridStateElement(dd, GSS_DLON); +} + +SEXP L_setDLon(SEXP value) +{ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + SEXP prev; + prev = gridStateElement(dd, GSS_DLON); + setGridStateElement(dd, GSS_DLON, value); + return prev; +} + +SEXP L_getEngineDLon() +{ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + return gridStateElement(dd, GSS_ENGINEDLON); +} + +SEXP L_setEngineDLon(SEXP value) +{ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + setGridStateElement(dd, GSS_ENGINEDLON, value); + return R_NilValue; +} + +SEXP L_getCurrentGrob() +{ + pGEDevDesc dd = getDevice(); + return gridStateElement(dd, GSS_CURRGROB); +} + +SEXP L_setCurrentGrob(SEXP value) +{ + pGEDevDesc dd = getDevice(); + setGridStateElement(dd, GSS_CURRGROB, value); + return R_NilValue; +} + +SEXP L_getEngineRecording() +{ + pGEDevDesc dd = getDevice(); + return gridStateElement(dd, GSS_ENGINERECORDING); +} + +SEXP L_setEngineRecording(SEXP value) +{ + pGEDevDesc dd = getDevice(); + setGridStateElement(dd, GSS_ENGINERECORDING, value); + return R_NilValue; +} + +SEXP L_currentGPar() +{ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + return gridStateElement(dd, GSS_GPAR); +} + +SEXP L_newpagerecording() +{ + pGEDevDesc dd = getDevice(); + if (dd->ask) { + NewFrameConfirm(dd->dev); + /* + * User may have killed device during pause for prompt + */ + if (NoDevices()) + error(_("attempt to plot on null device")); + else + /* + * Should throw an error if dd != GECurrentDevice ? + */ + dd = GEcurrentDevice(); + } + GEinitDisplayList(dd); + return R_NilValue; +} + +SEXP L_newpage() +{ + pGEDevDesc dd = getDevice(); + R_GE_gcontext gc; + /* + * Has the device been drawn on yet? + */ + Rboolean deviceDirty = GEdeviceDirty(dd); + /* + * Has the device been drawn on BY GRID yet? + */ + Rboolean deviceGridDirty = LOGICAL(gridStateElement(dd, + GSS_GRIDDEVICE))[0]; + /* + * Initialise grid on device + * If no drawing on device yet, does a new page + */ + if (!deviceGridDirty) { + dirtyGridDevice(dd); + } + /* + * If device has previously been drawn on (by grid or other system) + * do a new page + */ + if (deviceGridDirty || deviceDirty) { + SEXP currentgp = gridStateElement(dd, GSS_GPAR); + gcontextFromgpar(currentgp, 0, &gc, dd); + GENewPage(&gc, dd); + } + return R_NilValue; +} + +SEXP L_initGPar() +{ + pGEDevDesc dd = getDevice(); + initGPar(dd); + return R_NilValue; +} + +SEXP L_initViewportStack() +{ + pGEDevDesc dd = getDevice(); + initVP(dd); + return R_NilValue; +} + +SEXP L_initDisplayList() +{ + pGEDevDesc dd = getDevice(); + initDL(dd); + return R_NilValue; +} + +void getViewportTransform(SEXP currentvp, + pGEDevDesc dd, + double *vpWidthCM, double *vpHeightCM, + LTransform transform, double *rotationAngle) +{ + int i, j; + double devWidthCM, devHeightCM; + getDeviceSize((dd), &devWidthCM, &devHeightCM) ; + if (deviceChanged(devWidthCM, devHeightCM, currentvp)) { + /* IF the device has changed, recalculate the viewport transform + */ + calcViewportTransform(currentvp, viewportParent(currentvp), 1, dd); + } + for (i=0; i<3; i++) + for (j=0; j<3; j++) + transform[i][j] = + REAL(viewportTransform(currentvp))[i + 3*j]; + *rotationAngle = REAL(viewportRotation(currentvp))[0]; + *vpWidthCM = REAL(viewportWidthCM(currentvp))[0]; + *vpHeightCM = REAL(viewportHeightCM(currentvp))[0]; +} + + +/*************************** + * CONVERSION FUNCTIONS + *************************** + */ + +/* + * WITHIN THE CURRENT VIEWPORT ... + * + * Given a unit object and whether it is a location/dimension, + * convert to location/dimension in unit B + * + * NOTE: When this is used to convert a mouse click on a device to + * a location/dimension, the conversion of the mouse click to + * a unit within the current viewport has to be done elsewhere. + * e.g., in interactive.R, this is done by applying the inverse + * of the current viewport transformation to get a location in + * inches within the current viewport. + * + * This should ideally create a unit object to ensure that the + * values it returns are treated as values in the correct + * coordinate system. For now, this is MUCH easier to do in + * R code, so it is the responsibility of the R code calling this + * to create the unit object correctly/honestly. + * + * NOTE also that the unitto supplied should be a "valid" integer + * (the best way to get that is to use the valid.units() + * function in unit.R) + * + * what = 0 means x, 1 means y, 2 means width, 3 means height + */ +SEXP L_convert(SEXP x, SEXP whatfrom, + SEXP whatto, SEXP unitto) { + int i, nx; + SEXP answer; + double vpWidthCM, vpHeightCM; + double rotationAngle; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform; + SEXP currentvp, currentgp; + int TOunit, FROMaxis, TOaxis; + Rboolean relConvert; + /* + * Get the current device + */ + pGEDevDesc dd = getDevice(); + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + /* + * We do not need the current transformation, but + * we need the side effects of calculating it in + * case the device has been resized (or only just created) + */ + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + getViewportContext(currentvp, &vpc); + nx = unitLength(x); + PROTECT(answer = allocVector(REALSXP, nx)); + for (i=0; i<nx; i++) { + gcontextFromgpar(currentgp, i, &gc, dd); + TOunit = INTEGER(unitto)[i % LENGTH(unitto)]; + FROMaxis = INTEGER(whatfrom)[0]; + TOaxis = INTEGER(whatto)[0]; + /* + * Special case: FROM unit is just a plain, relative unit AND + * TO unit is relative AND + * NOT converting from 'x' to 'y' (or vice versa) ... + * + * ... AND relevant widthCM or heightCM is zero + * + * In these cases do NOT transform thru INCHES + * (to avoid divide-by-zero, but still do something useful) + */ + relConvert = (!isUnitArithmetic(x) && !isUnitList(x) && + (unitUnit(x, i) == L_NATIVE || unitUnit(x, i) == L_NPC) && + (TOunit == L_NATIVE || TOunit == L_NPC) && + ((FROMaxis == TOaxis) || + (FROMaxis == 0 && TOaxis == 2) || + (FROMaxis == 2 && TOaxis == 0) || + (FROMaxis == 1 && TOaxis == 3) || + (FROMaxis == 3 && TOaxis == 1))); + /* + * First, convert the unit object passed in to a value in INCHES + * (within the current viewport) + */ + switch (FROMaxis) { + case 0: + if (relConvert && vpWidthCM < 1e-6) { + REAL(answer)[i] = + transformXYtoNPC(unitValue(x, i), unitUnit(x, i), + vpc.xscalemin, vpc.xscalemax); + } else { + relConvert = FALSE; + REAL(answer)[i] = + transformXtoINCHES(x, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + } + break; + case 1: + if (relConvert && vpHeightCM < 1e-6) { + REAL(answer)[i] = + transformXYtoNPC(unitValue(x, i), unitUnit(x, i), + vpc.yscalemin, vpc.yscalemax); + } else { + relConvert = FALSE; + REAL(answer)[i] = + transformYtoINCHES(x, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + } + break; + case 2: + if (relConvert && vpWidthCM < 1e-6) { + REAL(answer)[i] = + transformWHtoNPC(unitValue(x, i), unitUnit(x, i), + vpc.xscalemin, vpc.xscalemax); + } else { + relConvert = FALSE; + REAL(answer)[i] = + transformWidthtoINCHES(x, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + } + break; + case 3: + if (relConvert && vpHeightCM < 1e-6) { + REAL(answer)[i] = + transformWHtoNPC(unitValue(x, i), unitUnit(x, i), + vpc.yscalemin, vpc.yscalemax); + } else { + relConvert = FALSE; + REAL(answer)[i] = + transformHeighttoINCHES(x, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + } + break; + } + /* + * Now, convert the values in INCHES to a value in the specified + * coordinate system + * (within the current viewport) + * + * BUT do NOT do this step for the special "relConvert" case + */ + switch (TOaxis) { + case 0: + if (relConvert) { + REAL(answer)[i] = transformXYfromNPC(REAL(answer)[i], TOunit, + vpc.xscalemin, + vpc.xscalemax); + } else { + REAL(answer)[i] = + transformXYFromINCHES(REAL(answer)[i], TOunit, + vpc.xscalemin, + vpc.xscalemax, + &gc, + vpWidthCM, vpHeightCM, + dd); + } + break; + case 1: + if (relConvert) { + REAL(answer)[i] = transformXYfromNPC(REAL(answer)[i], TOunit, + vpc.yscalemin, + vpc.yscalemax); + } else { + REAL(answer)[i] = + transformXYFromINCHES(REAL(answer)[i], TOunit, + vpc.yscalemin, + vpc.yscalemax, + &gc, + vpHeightCM, vpWidthCM, + dd); + } + break; + case 2: + if (relConvert) { + REAL(answer)[i] = transformWHfromNPC(REAL(answer)[i], TOunit, + vpc.xscalemin, + vpc.xscalemax); + } else { + REAL(answer)[i] = + transformWidthHeightFromINCHES(REAL(answer)[i], TOunit, + vpc.xscalemin, + vpc.xscalemax, + &gc, + vpWidthCM, vpHeightCM, + dd); + } + break; + case 3: + if (relConvert) { + REAL(answer)[i] = transformWHfromNPC(REAL(answer)[i], TOunit, + vpc.yscalemin, + vpc.yscalemax); + } else { + REAL(answer)[i] = + transformWidthHeightFromINCHES(REAL(answer)[i], TOunit, + vpc.yscalemin, + vpc.yscalemax, + &gc, + vpHeightCM, vpWidthCM, + dd); + break; + } + } + } + UNPROTECT(1); + return answer; +} + +/* + * Given a layout.pos.row and a layout.pos.col, calculate + * the region allocated by the layout of the current viewport + * + * Not a conversion as such, but similarly vulnerable to device resizing + */ +SEXP L_layoutRegion(SEXP layoutPosRow, SEXP layoutPosCol) { + LViewportLocation vpl; + SEXP answer; + double vpWidthCM, vpHeightCM; + double rotationAngle; + LTransform transform; + SEXP currentvp; + /* + * Get the current device + */ + pGEDevDesc dd = getDevice(); + currentvp = gridStateElement(dd, GSS_VP); + //currentgp = gridStateElement(dd, GSS_GPAR); + /* + * We do not need the current transformation, but + * we need the side effects of calculating it in + * case the device has been resized (or only just created) + */ + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + /* + * Only proceed if there is a layout currently defined + */ + if (isNull(viewportLayout(currentvp))) + error(_("there is no layout defined")); + /* + * The result is a numeric containing left, bottom, width, and height + */ + PROTECT(answer = allocVector(REALSXP, 4)); + /* + * NOTE: We are assuming here that calcViewportLocationFromLayout + * returns the allocated region with a ("left", "bottom") + * justification. This is CURRENTLY true, but ... + */ + calcViewportLocationFromLayout(layoutPosRow, + layoutPosCol, + currentvp, + &vpl); + /* + * I am not returning the units created in C code + * because they do not have the units attribute set + * so they do not behave nicely back in R code. + * Instead, I take the values and my knowledge that they + * are NPC units and construct real unit objects back in + * R code. + */ + REAL(answer)[0] = unitValue(vpl.x, 0); + REAL(answer)[1] = unitValue(vpl.y, 0); + REAL(answer)[2] = unitValue(vpl.width, 0); + REAL(answer)[3] = unitValue(vpl.height, 0); + UNPROTECT(1); + return answer; +} + +/*************************** + * EDGE DETECTION + *************************** + */ + +/* + * Calculate the point on the edge of a rectangle at angle theta + * 0 = East, 180 = West, etc ... + * Assumes that x- and y-values are in INCHES + * Assumes that theta is within [0, 360) + */ +static void rectEdge(double xmin, double ymin, double xmax, double ymax, + double theta, + double *edgex, double *edgey) +{ + double xm = (xmin + xmax)/2; + double ym = (ymin + ymax)/2; + double dx = (xmax - xmin)/2; + double dy = (ymax - ymin)/2; + /* + * FIXME: Special case 0 width or 0 height + */ + /* + * Special case angles + */ + if (theta == 0) { + *edgex = xmax; + *edgey = ym; + } else if (theta == 270) { + *edgex = xm; + *edgey = ymin; + } else if (theta == 180) { + *edgex = xmin; + *edgey = ym; + } else if (theta == 90) { + *edgex = xm; + *edgey = ymax; + } else { + double cutoff = dy/dx; + double angle = theta/180*M_PI; + double tanTheta = tan(angle); + double cosTheta = cos(angle); + double sinTheta = sin(angle); + if (fabs(tanTheta) < cutoff) { /* Intersect with side */ + if (cosTheta > 0) { /* Right side */ + *edgex = xmax; + *edgey = ym + tanTheta*dx; + } else { /* Left side */ + *edgex = xmin; + *edgey = ym - tanTheta*dx; + } + } else { /* Intersect with top/bottom */ + if (sinTheta > 0) { /* Top */ + *edgey = ymax; + *edgex = xm + dy/tanTheta; + } else { /* Bottom */ + *edgey = ymin; + *edgex = xm - dy/tanTheta; + } + } + } +} + +/* + * Calculate the point on the edge of a rectangle at angle theta + * 0 = East, 180 = West, etc ... + * Assumes that x- and y-values are in INCHES + * Assumes that theta is within [0, 360) + */ +static void circleEdge(double x, double y, double r, + double theta, + double *edgex, double *edgey) +{ + double angle = theta/180*M_PI; + *edgex = x + r*cos(angle); + *edgey = y + r*sin(angle); +} + +/* + * Calculate the point on the edge of a *convex* polygon at angle theta + * 0 = East, 180 = West, etc ... + * Assumes that x- and y-values are in INCHES + * Assumes that vertices are in clock-wise order + * Assumes that theta is within [0, 360) + */ +static void polygonEdge(double *x, double *y, int n, + double theta, + double *edgex, double *edgey) { + int i, v1, v2; + double xm, ym; + double xmin = DOUBLE_XMAX; + double xmax = -DOUBLE_XMAX; + double ymin = DOUBLE_XMAX; + double ymax = -DOUBLE_XMAX; + int found = 0; + double angle = theta/180*M_PI; + double vangle1, vangle2; + /* + * Find "centre" of polygon + */ + for (i=0; i<n; i++) { + if (x[i] < xmin) + xmin = x[i]; + if (x[i] > xmax) + xmax = x[i]; + if (y[i] < ymin) + ymin = y[i]; + if (y[i] > ymax) + ymax = y[i]; + } + xm = (xmin + xmax)/2; + ym = (ymin + ymax)/2; + /* + * Special case zero-width or zero-height + */ + if (fabs(xmin - xmax) < 1e-6) { + *edgex = xmin; + if (theta == 90) + *edgey = ymax; + else if (theta == 270) + *edgey = ymin; + else + *edgey = ym; + return; + } + if (fabs(ymin - ymax) < 1e-6) { + *edgey = ymin; + if (theta == 0) + *edgex = xmax; + else if (theta == 180) + *edgex = xmin; + else + *edgex = xm; + return; + } + /* + * Find edge that intersects line from centre at angle theta + */ + for (i=0; i<n; i++) { + v1 = i; + v2 = v1 + 1; + if (v2 == n) + v2 = 0; + /* + * Result of atan2 is in range -PI, PI so convert to + * 0, 360 to correspond to angle + */ + vangle1 = atan2(y[v1] - ym, x[v1] - xm); + if (vangle1 < 0) + vangle1 = vangle1 + 2*M_PI; + vangle2 = atan2(y[v2] - ym, x[v2] - xm); + if (vangle2 < 0) + vangle2 = vangle2 + 2*M_PI; + /* + * If vangle1 < vangle2 then angles are either side of 0 + * so check is more complicated + */ + if ((vangle1 >= vangle2 && + vangle1 >= angle && vangle2 < angle) || + (vangle1 < vangle2 && + ((vangle1 >= angle && 0 <= angle) || + (vangle2 < angle && 2*M_PI >= angle)))) { + found = 1; + break; + } + } + /* + * Find intersection point of "line from centre to bounding rect" + * and edge + */ + if (found) { + double x1 = xm; + double y1 = ym; + double x2, y2; + double x3 = x[v1]; + double y3 = y[v1]; + double x4 = x[v2]; + double y4 = y[v2]; + double numa, denom, ua; + rectEdge(xmin, ymin, xmax, ymax, theta, + &x2, &y2); + numa = ((x4 - x3)*(y1 - y3) - (y4 - y3)*(x1 - x3)); + denom = ((y4 - y3)*(x2 - x1) - (x4 - x3)*(y2 - y1)); + ua = numa/denom; + if (!R_FINITE(ua)) { + /* + * Should only happen if lines are parallel, which + * shouldn't happen! Unless, perhaps the polygon has + * zero extent vertically or horizontally ... ? + */ + error(_("polygon edge not found (zero-width or zero-height?)")); + } + /* + * numb = ((x2 - x1)*(y1 - y3) - (y2 - y1)*(x1 - x3)); + * ub = numb/denom; + */ + *edgex = x1 + ua*(x2 - x1); + *edgey = y1 + ua*(y2 - y1); + } else { + error(_("polygon edge not found")); + } +} + +/* + * Given a set of points, calculate the convex hull then + * find the edge of that hull + * + * NOTE: assumes that 'grDevices' package has been loaded + * so that chull() is available (grid depends on grDevices) + */ +static void hullEdge(double *x, double *y, int n, + double theta, + double *edgex, double *edgey) +{ + const void *vmax; + int i, nh; + double *hx, *hy; + SEXP xin, yin, chullFn, R_fcall, hull; + int adjust = 0; + double *xkeep, *ykeep; + vmax = vmaxget(); + /* Remove any NA's because chull() can't cope with them */ + xkeep = (double *) R_alloc(n, sizeof(double)); + ykeep = (double *) R_alloc(n, sizeof(double)); + for (i=0; i<n; i++) { + if (!R_FINITE(x[i]) || !R_FINITE(y[i])) { + adjust--; + } else { + xkeep[i + adjust] = x[i]; + ykeep[i + adjust] = y[i]; + } + } + n = n + adjust; + PROTECT(xin = allocVector(REALSXP, n)); + PROTECT(yin = allocVector(REALSXP, n)); + for (i=0; i<n; i++) { + REAL(xin)[i] = xkeep[i]; + REAL(yin)[i] = ykeep[i]; + } + /* + * Determine convex hull + */ + PROTECT(chullFn = findFun(install("chull"), R_gridEvalEnv)); + PROTECT(R_fcall = lang3(chullFn, xin, yin)); + PROTECT(hull = eval(R_fcall, R_gridEvalEnv)); + nh = LENGTH(hull); + hx = (double *) R_alloc(nh, sizeof(double)); + hy = (double *) R_alloc(nh, sizeof(double)); + for (i=0; i<nh; i++) { + hx[i] = x[INTEGER(hull)[i] - 1]; + hy[i] = y[INTEGER(hull)[i] - 1]; + } + /* + * Find edge of that hull + */ + polygonEdge(hx, hy, nh, theta, + edgex, edgey); + vmaxset(vmax); + UNPROTECT(5); +} + +/*************************** + * DRAWING PRIMITIVES + *************************** + */ + +/* + * Draw an arrow head, given the vertices of the arrow head. + * Assume vertices are in DEVICE coordinates. + */ +static void drawArrow(double *x, double *y, SEXP type, int i, + const pGEcontext gc, pGEDevDesc dd) +{ + int nt = LENGTH(type); + switch (INTEGER(type)[i % nt]) { + case 1: + GEPolyline(3, x, y, gc, dd); + break; + case 2: + GEPolygon(3, x, y, gc, dd); + break; + } +} + +/* + * Calculate vertices for drawing an arrow head. + * Assumes that x and y locations are in INCHES. + * Returns vertices in DEVICE coordinates. + */ +static void calcArrow(double x1, double y1, + double x2, double y2, + SEXP angle, SEXP length, int i, + LViewportContext vpc, + double vpWidthCM, double vpHeightCM, + double *vertx, double *verty, + const pGEcontext gc, pGEDevDesc dd) +{ + int na = LENGTH(angle); + int nl = LENGTH(length); + double xc, yc, rot; + double l1, l2, l, a; + l1 = transformWidthtoINCHES(length, i % nl, vpc, gc, + vpWidthCM, vpHeightCM, + dd); + l2 = transformHeighttoINCHES(length, i % nl, vpc, gc, + vpWidthCM, vpHeightCM, + dd); + l = fmin2(l1, l2); + a = DEG2RAD * REAL(angle)[i % na]; + xc = x2 - x1; + yc = y2 - y1; + rot= atan2(yc, xc); + vertx[0] = toDeviceX(x1 + l * cos(rot+a), + GE_INCHES, dd); + verty[0] = toDeviceY(y1 + l * sin(rot+a), + GE_INCHES, dd); + vertx[1] = toDeviceX(x1, + GE_INCHES, dd); + verty[1] = toDeviceY(y1, + GE_INCHES, dd); + vertx[2] = toDeviceX(x1 + l * cos(rot-a), + GE_INCHES, dd); + verty[2] = toDeviceY(y1 + l * sin(rot-a), + GE_INCHES, dd); +} + +/* + * Assumes x and y are at least length 2 + * Also assumes x and y are in DEVICE coordinates + */ +static void arrows(double *x, double *y, int n, + SEXP arrow, int i, + /* + * Which ends we are allowed to draw arrow heads on + * (we may be drawing a line segment that has been + * broken by NAs) + */ + Rboolean start, Rboolean end, + LViewportContext vpc, + double vpWidthCM, double vpHeightCM, + const pGEcontext gc, pGEDevDesc dd) +{ + /* + * Write a checkArrow() function to make + * sure 'a' is a valid arrow description ? + * If someone manages to sneak in a + * corrupt arrow description ... BOOM! + */ + SEXP ends = VECTOR_ELT(arrow, GRID_ARROWENDS); + int ne = LENGTH(ends); + double vertx[3], verty[3]; + Rboolean first, last; + if (n < 2) + error(_("require at least two points to draw arrow")); + first = TRUE; + last = TRUE; + switch (INTEGER(ends)[i % ne]) { + case 2: + first = FALSE; + break; + case 1: + last = FALSE; + break; + } + if (first && start) { + calcArrow(fromDeviceX(x[0], GE_INCHES, dd), + fromDeviceY(y[0], GE_INCHES, dd), + fromDeviceX(x[1], GE_INCHES, dd), + fromDeviceY(y[1], GE_INCHES, dd), + VECTOR_ELT(arrow, GRID_ARROWANGLE), + VECTOR_ELT(arrow, GRID_ARROWLENGTH), + i, vpc, vpWidthCM, vpHeightCM, vertx, verty, gc, dd); + drawArrow(vertx, verty, + VECTOR_ELT(arrow, GRID_ARROWTYPE), i, + gc, dd); + } + if (last && end) { + calcArrow(fromDeviceX(x[n - 1], GE_INCHES, dd), + fromDeviceY(y[n - 1], GE_INCHES, dd), + fromDeviceX(x[n - 2], GE_INCHES, dd), + fromDeviceY(y[n - 2], GE_INCHES, dd), + VECTOR_ELT(arrow, GRID_ARROWANGLE), + VECTOR_ELT(arrow, GRID_ARROWLENGTH), + i, vpc, vpWidthCM, vpHeightCM, vertx, verty, gc, dd); + drawArrow(vertx, verty, + VECTOR_ELT(arrow, GRID_ARROWTYPE), i, + gc, dd); + } +} + +SEXP L_moveTo(SEXP x, SEXP y) +{ + double xx, yy; + double vpWidthCM, vpHeightCM; + double rotationAngle; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform; + SEXP devloc, prevloc; + SEXP currentvp, currentgp; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + PROTECT(prevloc = gridStateElement(dd, GSS_PREVLOC)); + PROTECT(devloc = gridStateElement(dd, GSS_CURRLOC)); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + getViewportContext(currentvp, &vpc); + gcontextFromgpar(currentgp, 0, &gc, dd); + /* Convert the x and y values to CM locations */ + transformLocn(x, y, 0, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, + transform, + &xx, &yy); + /* + * Non-finite values are ok here + * L_lineTo figures out what to draw + * when values are non-finite + */ + REAL(prevloc)[0] = REAL(devloc)[0]; + REAL(prevloc)[1] = REAL(devloc)[1]; + REAL(devloc)[0] = xx; + REAL(devloc)[1] = yy; + UNPROTECT(2); + return R_NilValue; +} + +SEXP L_lineTo(SEXP x, SEXP y, SEXP arrow) +{ + double xx0, yy0, xx1, yy1; + double xx, yy; + double vpWidthCM, vpHeightCM; + double rotationAngle; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform; + SEXP devloc, prevloc; + SEXP currentvp, currentgp; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + PROTECT(prevloc = gridStateElement(dd, GSS_PREVLOC)); + PROTECT(devloc = gridStateElement(dd, GSS_CURRLOC)); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + getViewportContext(currentvp, &vpc); + gcontextFromgpar(currentgp, 0, &gc, dd); + /* Convert the x and y values to CM locations */ + transformLocn(x, y, 0, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, + transform, + &xx, &yy); + REAL(prevloc)[0] = REAL(devloc)[0]; + REAL(prevloc)[1] = REAL(devloc)[1]; + REAL(devloc)[0] = xx; + REAL(devloc)[1] = yy; + /* The graphics engine only takes device coordinates + */ + xx0 = toDeviceX(REAL(prevloc)[0], GE_INCHES, dd); + yy0 = toDeviceY(REAL(prevloc)[1], GE_INCHES, dd), + xx1 = toDeviceX(xx, GE_INCHES, dd); + yy1 = toDeviceY(yy, GE_INCHES, dd); + if (R_FINITE(xx0) && R_FINITE(yy0) && + R_FINITE(xx1) && R_FINITE(yy1)) { + GEMode(1, dd); + GELine(xx0, yy0, xx1, yy1, &gc, dd); + if (!isNull(arrow)) { + double ax[2], ay[2]; + ax[0] = xx0; + ax[1] = xx1; + ay[0] = yy0; + ay[1] = yy1; + arrows(ax, ay, 2, + arrow, 0, TRUE, TRUE, + vpc, vpWidthCM, vpHeightCM, &gc, dd); + } + GEMode(0, dd); + } + UNPROTECT(2); + return R_NilValue; +} + +/* We are assuming here that the R code has checked that x and y + * are unit objects and that vp is a viewport + */ +SEXP L_lines(SEXP x, SEXP y, SEXP index, SEXP arrow) +{ + int i, j, nx, nl, start=0; + double *xx, *yy; + double xold, yold; + double vpWidthCM, vpHeightCM; + double rotationAngle; + const void *vmax; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform; + SEXP currentvp, currentgp; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + getViewportContext(currentvp, &vpc); + GEMode(1, dd); + /* + * Number of lines + */ + nl = LENGTH(index); + for (j=0; j<nl; j++) { + SEXP indices = VECTOR_ELT(index, j); + gcontextFromgpar(currentgp, j, &gc, dd); + /* + * Number of vertices + * + * x and y same length forced in R code + */ + nx = LENGTH(indices); + /* Convert the x and y values to CM locations */ + vmax = vmaxget(); + xx = (double *) R_alloc(nx, sizeof(double)); + yy = (double *) R_alloc(nx, sizeof(double)); + xold = NA_REAL; + yold = NA_REAL; + for (i=0; i<nx; i++) { + transformLocn(x, y, INTEGER(indices)[i] - 1, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, + transform, + &(xx[i]), &(yy[i])); + /* The graphics engine only takes device coordinates + */ + xx[i] = toDeviceX(xx[i], GE_INCHES, dd); + yy[i] = toDeviceY(yy[i], GE_INCHES, dd); + if ((R_FINITE(xx[i]) && R_FINITE(yy[i])) && + !(R_FINITE(xold) && R_FINITE(yold))) + start = i; + else if ((R_FINITE(xold) && R_FINITE(yold)) && + !(R_FINITE(xx[i]) && R_FINITE(yy[i]))) { + if (i-start > 1) { + GEPolyline(i-start, xx+start, yy+start, &gc, dd); + if (!isNull(arrow)) { + /* + * Can draw an arrow at the start if the points + * include the first point. + * CANNOT draw an arrow at the end point + * because we have just broken the line for an NA. + */ + arrows(xx+start, yy+start, i-start, + arrow, j, start == 0, FALSE, + vpc, vpWidthCM, vpHeightCM, &gc, dd); + } + } + } + else if ((R_FINITE(xold) && R_FINITE(yold)) && + (i == nx-1)) { + GEPolyline(nx-start, xx+start, yy+start, &gc, dd); + if (!isNull(arrow)) { + /* + * Can draw an arrow at the start if the points + * include the first point. + * Can draw an arrow at the end point. + */ + arrows(xx+start, yy+start, nx-start, + arrow, j, start == 0, TRUE, + vpc, vpWidthCM, vpHeightCM, &gc, dd); + } + } + xold = xx[i]; + yold = yy[i]; + } + vmaxset(vmax); + } + GEMode(0, dd); + return R_NilValue; +} + +/* We are assuming here that the R code has checked that x and y + * are unit objects + */ +SEXP gridXspline(SEXP x, SEXP y, SEXP s, SEXP o, SEXP a, SEXP rep, SEXP index, + double theta, Rboolean draw, Rboolean trace) +{ + int i, j, nx, np, nloc; + double *xx, *yy, *ss; + double vpWidthCM, vpHeightCM; + double rotationAngle; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform; + SEXP currentvp, currentgp; + SEXP tracePts = R_NilValue; + SEXP result = R_NilValue; + double edgex, edgey; + double xmin = DOUBLE_XMAX; + double xmax = -DOUBLE_XMAX; + double ymin = DOUBLE_XMAX; + double ymax = -DOUBLE_XMAX; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + getViewportContext(currentvp, &vpc); + gcontextFromgpar(currentgp, 0, &gc, dd); + /* + * Number of xsplines + */ + np = LENGTH(index); + PROTECT(tracePts = allocVector(VECSXP, np)); + nloc = 0; + for (i=0; i<np; i++) { + const void *vmax; + SEXP indices = VECTOR_ELT(index, i); + SEXP points; + gcontextFromgpar(currentgp, i, &gc, dd); + /* + * Number of vertices + * + * Check in R code that x and y same length + */ + nx = LENGTH(indices); + /* Convert the x and y values to CM locations */ + vmax = vmaxget(); + if (draw) + GEMode(1, dd); + xx = (double *) R_alloc(nx, sizeof(double)); + yy = (double *) R_alloc(nx, sizeof(double)); + ss = (double *) R_alloc(nx, sizeof(double)); + for (j=0; j<nx; j++) { + ss[j] = REAL(s)[(INTEGER(indices)[j] - 1) % LENGTH(s)]; + /* + * If drawing, convert to INCHES on device + * If just calculating bounds, convert to INCHES within current vp + */ + if (draw) { + transformLocn(x, y, INTEGER(indices)[j] - 1, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, + transform, + &(xx[j]), &(yy[j])); + } else { + xx[j] = transformXtoINCHES(x, INTEGER(indices)[j] - 1, + vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + yy[j] = transformYtoINCHES(y, INTEGER(indices)[j] - 1, + vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + } + /* The graphics engine only takes device coordinates + */ + xx[j] = toDeviceX(xx[j], GE_INCHES, dd); + yy[j] = toDeviceY(yy[j], GE_INCHES, dd); + if (!(R_FINITE(xx[j]) && R_FINITE(yy[j]))) { + error(_("non-finite control point in Xspline")); + } + } + PROTECT(points = GEXspline(nx, xx, yy, ss, + LOGICAL(o)[0], LOGICAL(rep)[0], + draw, &gc, dd)); + { + /* + * In some cases, GEXspline seems to produce identical points + * (at least observed at end of spline) + * so trim identical points from the ends + * (so arrow heads are drawn at correct angle) + */ + int np = LENGTH(VECTOR_ELT(points, 0)); + double *px = REAL(VECTOR_ELT(points, 0)); + double *py = REAL(VECTOR_ELT(points, 1)); + int start = 0; + int end = np - 1; + /* + * DEBUGGING ... + int k; + for (k=0; k<np; k++) { + GESymbol(px[k], py[k], 16, 3, &gc, dd); + } + * ... DEBUGGING + */ + while (np > 1 && + (px[start] == px[start + 1]) && + (py[start] == py[start + 1])) { + start++; + np--; + } + while (np > 1 && + (px[end] == px[end - 1]) && + (py[end] == py[end - 1])) { + end--; + np--; + } + if (trace) { + int k; + int count = end - start + 1; + double *keepXptr, *keepYptr; + SEXP keepPoints, keepX, keepY; + PROTECT(keepPoints = allocVector(VECSXP, 2)); + PROTECT(keepX = allocVector(REALSXP, count)); + PROTECT(keepY = allocVector(REALSXP, count)); + keepXptr = REAL(keepX); + keepYptr = REAL(keepY); + for (k=start; k<(end + 1); k++) { + keepXptr[k - start] = fromDeviceX(px[k], GE_INCHES, dd); + keepYptr[k - start] = fromDeviceY(py[k], GE_INCHES, dd); + } + SET_VECTOR_ELT(keepPoints, 0, keepX); + SET_VECTOR_ELT(keepPoints, 1, keepY); + SET_VECTOR_ELT(tracePts, i, keepPoints); + UNPROTECT(3); /* keepPoints & keepX & keepY */ + } + if (draw && !isNull(a) && !isNull(points)) { + /* + * Can draw an arrow at the either end. + */ + arrows(&(px[start]), &(py[start]), np, + a, i, TRUE, TRUE, + vpc, vpWidthCM, vpHeightCM, &gc, dd); + } + if (!draw && !trace && !isNull(points)) { + /* + * Update bounds + */ + int j, n = LENGTH(VECTOR_ELT(points, 0)); + double *pxx = (double *) R_alloc(n, sizeof(double)); + double *pyy = (double *) R_alloc(n, sizeof(double)); + for (j=0; j<n; j++) { + pxx[j] = fromDeviceX(px[j], GE_INCHES, dd); + pyy[j] = fromDeviceY(py[j], GE_INCHES, dd); + if (R_FINITE(pxx[j]) && R_FINITE(pyy[j])) { + if (pxx[j] < xmin) + xmin = pxx[j]; + if (pxx[j] > xmax) + xmax = pxx[j]; + if (pyy[j] < ymin) + ymin = pyy[j]; + if (pyy[j] > ymax) + ymax = pyy[j]; + nloc++; + } + } + /* + * Calculate edgex and edgey for case where this is + * the only xspline + */ + hullEdge(pxx, pyy, n, theta, &edgex, &edgey); + } + } /* End of trimming-redundant-points code */ + UNPROTECT(1); /* points */ + if (draw) + GEMode(0, dd); + vmaxset(vmax); + } + if (!draw && !trace && nloc > 0) { + PROTECT(result = allocVector(REALSXP, 4)); + /* + * If there is more than one xspline, just produce edge + * based on bounding rect of all xsplines + */ + if (np > 1) { + rectEdge(xmin, ymin, xmax, ymax, theta, + &edgex, &edgey); + } + /* + * Reverse the scale adjustment (zoom factor) + * when calculating physical value to return to user-level + */ + REAL(result)[0] = edgex / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + REAL(result)[1] = edgey / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + REAL(result)[2] = (xmax - xmin) / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + REAL(result)[3] = (ymax - ymin) / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + UNPROTECT(1); /* result */ + } else if (trace) { + result = tracePts; + } + UNPROTECT(1); /* tracePts */ + return result; +} + +SEXP L_xspline(SEXP x, SEXP y, SEXP s, SEXP o, SEXP a, SEXP rep, SEXP index) +{ + gridXspline(x, y, s, o, a, rep, index, 0, TRUE, FALSE); + return R_NilValue; +} + +SEXP L_xsplineBounds(SEXP x, SEXP y, SEXP s, SEXP o, SEXP a, SEXP rep, + SEXP index, SEXP theta) +{ + return gridXspline(x, y, s, o, a, rep, index, REAL(theta)[0], + FALSE, FALSE); +} + +SEXP L_xsplinePoints(SEXP x, SEXP y, SEXP s, SEXP o, SEXP a, SEXP rep, + SEXP index, SEXP theta) +{ + return gridXspline(x, y, s, o, a, rep, index, REAL(theta)[0], + FALSE, TRUE); +} + +SEXP L_segments(SEXP x0, SEXP y0, SEXP x1, SEXP y1, SEXP arrow) +{ + int i, nx0, ny0, nx1, ny1, maxn; + double vpWidthCM, vpHeightCM; + double rotationAngle; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform; + SEXP currentvp, currentgp; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + getViewportContext(currentvp, &vpc); + maxn = nx0 = unitLength(x0); + ny0 = unitLength(y0); + nx1 = unitLength(x1); + ny1 = unitLength(y1); + if (ny0 > maxn) + maxn = ny0; + if (nx1 > maxn) + maxn = nx1; + if (ny1 > maxn) + maxn = ny1; + /* Convert the x and y values to INCHES locations */ + /* FIXME: Need to check for NaN's and NA's + */ + GEMode(1, dd); + for (i=0; i<maxn; i++) { + double xx0, yy0, xx1, yy1; + gcontextFromgpar(currentgp, i, &gc, dd); + transformLocn(x0, y0, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, transform, &xx0, &yy0); + transformLocn(x1, y1, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, transform, &xx1, &yy1); + /* The graphics engine only takes device coordinates + */ + xx0 = toDeviceX(xx0, GE_INCHES, dd); + yy0 = toDeviceY(yy0, GE_INCHES, dd); + xx1 = toDeviceX(xx1, GE_INCHES, dd); + yy1 = toDeviceY(yy1, GE_INCHES, dd); + if (R_FINITE(xx0) && R_FINITE(yy0) && + R_FINITE(xx1) && R_FINITE(yy1)) { + GELine(xx0, yy0, xx1, yy1, &gc, dd); + if (!isNull(arrow)) { + double ax[2], ay[2]; + ax[0] = xx0; + ax[1] = xx1; + ay[0] = yy0; + ay[1] = yy1; + arrows(ax, ay, 2, + arrow, i, TRUE, TRUE, + vpc, vpWidthCM, vpHeightCM, &gc, dd); + } + } + } + GEMode(0, dd); + return R_NilValue; +} + +static int getArrowN(SEXP x1, SEXP x2, SEXP xnm1, SEXP xn, + SEXP y1, SEXP y2, SEXP ynm1, SEXP yn) +{ + int nx2, nxnm1, nxn, ny1, ny2, nynm1, nyn, maxn; + maxn = 0; + /* + * x1, y1, xnm1, and ynm1 could be NULL if this is adding + * arrows to a line.to + */ + if (isNull(y1)) + ny1 = 0; + else + ny1 = unitLength(y1); + nx2 = unitLength(x2); + ny2 = unitLength(y2); + if (isNull(xnm1)) + nxnm1 = 0; + else + nxnm1 = unitLength(xnm1); + if (isNull(ynm1)) + nynm1 = 0; + else + nynm1 = unitLength(ynm1); + nxn = unitLength(xn); + nyn = unitLength(yn); + if (ny1 > maxn) + maxn = ny1; + if (nx2 > maxn) + maxn = nx2; + if (ny2 > maxn) + maxn = ny2; + if (nxnm1 > maxn) + maxn = nxnm1; + if (nynm1 > maxn) + maxn = nynm1; + if (nxn > maxn) + maxn = nxn; + if (nyn > maxn) + maxn = nyn; + return maxn; +} + +SEXP L_arrows(SEXP x1, SEXP x2, SEXP xnm1, SEXP xn, + SEXP y1, SEXP y2, SEXP ynm1, SEXP yn, + SEXP angle, SEXP length, SEXP ends, SEXP type) +{ + int i, maxn; + int ne; + double vpWidthCM, vpHeightCM; + double rotationAngle; + Rboolean first, last; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform; + SEXP currentvp, currentgp; + SEXP devloc = R_NilValue; /* -Wall */ + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + getViewportContext(currentvp, &vpc); + maxn = getArrowN(x1, x2, xnm1, xn, + y1, y2, ynm1, yn); + ne = LENGTH(ends); + /* Convert the x and y values to INCHES locations */ + /* FIXME: Need to check for NaN's and NA's + */ + GEMode(1, dd); + for (i=0; i<maxn; i++) { + double xx1, xx2, xxnm1, xxn, yy1, yy2, yynm1, yyn; + double vertx[3]; + double verty[3]; + first = TRUE; + last = TRUE; + switch (INTEGER(ends)[i % ne]) { + case 2: + first = FALSE; + break; + case 1: + last = FALSE; + break; + } + gcontextFromgpar(currentgp, i, &gc, dd); + /* + * If we're adding arrows to a line.to + * x1 will be NULL + */ + if (isNull(x1)) + PROTECT(devloc = gridStateElement(dd, GSS_CURRLOC)); + if (first) { + if (isNull(x1)) { + xx1 = REAL(devloc)[0]; + yy1 = REAL(devloc)[1]; + } else + transformLocn(x1, y1, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, transform, &xx1, &yy1); + transformLocn(x2, y2, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, transform, &xx2, &yy2); + calcArrow(xx1, yy1, xx2, yy2, angle, length, i, + vpc, vpWidthCM, vpHeightCM, + vertx, verty, &gc, dd); + /* + * Only draw arrow if both ends of first segment + * are not non-finite + */ + if (R_FINITE(toDeviceX(xx2, GE_INCHES, dd)) && + R_FINITE(toDeviceY(yy2, GE_INCHES, dd)) && + R_FINITE(vertx[1]) && R_FINITE(verty[1])) + drawArrow(vertx, verty, type, i, &gc, dd); + } + if (last) { + if (isNull(xnm1)) { + xxnm1 = REAL(devloc)[0]; + yynm1 = REAL(devloc)[1]; + } else + transformLocn(xnm1, ynm1, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, transform, &xxnm1, &yynm1); + transformLocn(xn, yn, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, transform, &xxn, &yyn); + calcArrow(xxn, yyn, xxnm1, yynm1, angle, length, i, + vpc, vpWidthCM, vpHeightCM, + vertx, verty, &gc, dd); + /* + * Only draw arrow if both ends of laste segment are + * not non-finite + */ + if (R_FINITE(toDeviceX(xxnm1, GE_INCHES, dd)) && + R_FINITE(toDeviceY(yynm1, GE_INCHES, dd)) && + R_FINITE(vertx[1]) && R_FINITE(verty[1])) + drawArrow(vertx, verty, type, i, &gc, dd); + } + if (isNull(x1)) + UNPROTECT(1); + } + GEMode(0, dd); + return R_NilValue; +} + +SEXP L_polygon(SEXP x, SEXP y, SEXP index) +{ + int i, j, nx, np, start=0; + double *xx, *yy; + double xold, yold; + double vpWidthCM, vpHeightCM; + double rotationAngle; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform; + SEXP currentvp, currentgp; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + getViewportContext(currentvp, &vpc); + GEMode(1, dd); + /* + * Number of polygons + */ + np = LENGTH(index); + for (i=0; i<np; i++) { + const void *vmax; + SEXP indices = VECTOR_ELT(index, i); + gcontextFromgpar(currentgp, i, &gc, dd); + /* + * Number of vertices + * + * Check in R code that x and y same length + */ + nx = LENGTH(indices); + /* Convert the x and y values to CM locations */ + vmax = vmaxget(); + xx = (double *) R_alloc(nx + 1, sizeof(double)); + yy = (double *) R_alloc(nx + 1, sizeof(double)); + xold = NA_REAL; + yold = NA_REAL; + for (j=0; j<nx; j++) { + transformLocn(x, y, INTEGER(indices)[j] - 1, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, + transform, + &(xx[j]), &(yy[j])); + /* The graphics engine only takes device coordinates + */ + xx[j] = toDeviceX(xx[j], GE_INCHES, dd); + yy[j] = toDeviceY(yy[j], GE_INCHES, dd); + if ((R_FINITE(xx[j]) && R_FINITE(yy[j])) && + !(R_FINITE(xold) && R_FINITE(yold))) + start = j; /* first point of current segment */ + else if ((R_FINITE(xold) && R_FINITE(yold)) && + !(R_FINITE(xx[j]) && R_FINITE(yy[j]))) { + if (j-start > 1) { + GEPolygon(j-start, xx+start, yy+start, &gc, dd); + } + } + else if ((R_FINITE(xold) && R_FINITE(yold)) && (j == nx-1)) { + /* last */ + GEPolygon(nx-start, xx+start, yy+start, &gc, dd); + } + xold = xx[j]; + yold = yy[j]; + } + vmaxset(vmax); + } + GEMode(0, dd); + return R_NilValue; +} + +static SEXP gridCircle(SEXP x, SEXP y, SEXP r, + double theta, Rboolean draw) +{ + int i, nx, ny, nr, ncirc; + double xx, yy, rr1, rr2, rr = 0.0 /* -Wall */; + double vpWidthCM, vpHeightCM; + double rotationAngle; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform; + SEXP currentvp, currentgp; + SEXP result = R_NilValue; + double xmin = DOUBLE_XMAX; + double xmax = -DOUBLE_XMAX; + double ymin = DOUBLE_XMAX; + double ymax = -DOUBLE_XMAX; + double edgex, edgey; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + getViewportContext(currentvp, &vpc); + nx = unitLength(x); + ny = unitLength(y); + nr = unitLength(r); + if (ny > nx) + nx = ny; + if (nr > nx) + nx = nr; + if (draw) { + GEMode(1, dd); + } + ncirc = 0; + for (i=0; i<nx; i++) { + gcontextFromgpar(currentgp, i, &gc, dd); + /* + * If drawing, convert to INCHES on device + * If just calculating bounds, convert to INCHES within current vp + */ + if (draw) { + transformLocn(x, y, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, + transform, + &xx, &yy); + } else { + xx = transformXtoINCHES(x, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + yy = transformYtoINCHES(y, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + } + /* These two will give the same answer unless r is in "native", + * "npc", or some other relative units; in those cases, just + * take the smaller of the two values. + */ + rr1 = transformWidthtoINCHES(r, i % nr, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + rr2 = transformHeighttoINCHES(r, i % nr, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + /* + * A negative radius is silently converted to absolute value + */ + rr = fmin2(fabs(rr1), fabs(rr2)); + if (R_FINITE(xx) && R_FINITE(yy) && R_FINITE(rr)) { + if (draw) { + /* The graphics engine only takes device coordinates + */ + xx = toDeviceX(xx, GE_INCHES, dd); + yy = toDeviceY(yy, GE_INCHES, dd); + rr = toDeviceWidth(rr, GE_INCHES, dd); + GECircle(xx, yy, rr, &gc, dd); + } else { + if (xx + rr < xmin) + xmin = xx + rr; + if (xx + rr > xmax) + xmax = xx + rr; + if (xx - rr < xmin) + xmin = xx - rr; + if (xx - rr > xmax) + xmax = xx - rr; + if (yy + rr < ymin) + ymin = yy + rr; + if (yy + rr > ymax) + ymax = yy + rr; + if (yy - rr < ymin) + ymin = yy - rr; + if (yy - rr > ymax) + ymax = yy - rr; + ncirc++; + } + } + } + if (draw) { + GEMode(0, dd); + } else if (ncirc > 0) { + result = allocVector(REALSXP, 4); + if (ncirc == 1) { + /* + * Produce edge of actual circle + */ + circleEdge(xx, yy, rr, theta, &edgex, &edgey); + } else { + /* + * Produce edge of rect bounding all circles + */ + rectEdge(xmin, ymin, xmax, ymax, theta, + &edgex, &edgey); + } + /* + * Reverse the scale adjustment (zoom factor) + * when calculating physical value to return to user-level + */ + REAL(result)[0] = edgex / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + REAL(result)[1] = edgey / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + REAL(result)[2] = (xmax - xmin) / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + REAL(result)[3] = (ymax - ymin) / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + } + return result; +} + +SEXP L_circle(SEXP x, SEXP y, SEXP r) +{ + gridCircle(x, y, r, 0, TRUE); + return R_NilValue; +} + +SEXP L_circleBounds(SEXP x, SEXP y, SEXP r, SEXP theta) +{ + return gridCircle(x, y, r, REAL(theta)[0], FALSE); +} + +/* We are assuming here that the R code has checked that + * x, y, w, and h are all unit objects and that vp is a viewport + */ +static SEXP gridRect(SEXP x, SEXP y, SEXP w, SEXP h, + SEXP hjust, SEXP vjust, double theta, Rboolean draw) +{ + double xx, yy, ww, hh; + double vpWidthCM, vpHeightCM; + double rotationAngle; + int i, ny, nw, nh, maxn, nrect; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform; + SEXP currentvp, currentgp; + SEXP result = R_NilValue; + double edgex, edgey; + double xmin = DOUBLE_XMAX; + double xmax = -DOUBLE_XMAX; + double ymin = DOUBLE_XMAX; + double ymax = -DOUBLE_XMAX; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + getViewportContext(currentvp, &vpc); + maxn = unitLength(x); + ny = unitLength(y); + nw = unitLength(w); + nh = unitLength(h); + if (ny > maxn) + maxn = ny; + if (nw > maxn) + maxn = nw; + if (nh > maxn) + maxn = nh; + if (draw) { + GEMode(1, dd); + } + nrect = 0; + for (i=0; i<maxn; i++) { + gcontextFromgpar(currentgp, i, &gc, dd); + /* + * If drawing, convert to INCHES on device + * If just calculating bounds, convert to INCHES within current vp + */ + if (draw) { + transformLocn(x, y, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, + transform, + &xx, &yy); + } else { + xx = transformXtoINCHES(x, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + yy = transformYtoINCHES(y, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + } + ww = transformWidthtoINCHES(w, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + hh = transformHeighttoINCHES(h, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + /* If the total rotation angle is zero then we can draw a + * rectangle as the devices understand rectangles + * Otherwise we have to draw a polygon equivalent. + */ + if (draw) { + if (rotationAngle == 0) { + xx = justifyX(xx, ww, REAL(hjust)[i % LENGTH(hjust)]); + yy = justifyY(yy, hh, REAL(vjust)[i % LENGTH(vjust)]); + /* The graphics engine only takes device coordinates + */ + xx = toDeviceX(xx, GE_INCHES, dd); + yy = toDeviceY(yy, GE_INCHES, dd); + ww = toDeviceWidth(ww, GE_INCHES, dd); + hh = toDeviceHeight(hh, GE_INCHES, dd); + if (R_FINITE(xx) && R_FINITE(yy) && + R_FINITE(ww) && R_FINITE(hh)) + GERect(xx, yy, xx + ww, yy + hh, &gc, dd); + } else { + /* We have to do a little bit of work to figure out where the + * corners of the rectangle are. + */ + double xxx[5], yyy[5], xadj, yadj; + double dw, dh; + SEXP zeroInches, xadjInches, yadjInches, wwInches, hhInches; + int tmpcol; + PROTECT(zeroInches = unit(0, L_INCHES)); + /* Find bottom-left location */ + justification(ww, hh, + REAL(hjust)[i % LENGTH(hjust)], + REAL(vjust)[i % LENGTH(vjust)], + &xadj, &yadj); + PROTECT(xadjInches = unit(xadj, L_INCHES)); + PROTECT(yadjInches = unit(yadj, L_INCHES)); + transformDimn(xadjInches, yadjInches, 0, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, rotationAngle, + &dw, &dh); + xxx[0] = xx + dw; + yyy[0] = yy + dh; + /* Find top-left location */ + PROTECT(hhInches = unit(hh, L_INCHES)); + transformDimn(zeroInches, hhInches, 0, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, rotationAngle, + &dw, &dh); + xxx[1] = xxx[0] + dw; + yyy[1] = yyy[0] + dh; + /* Find top-right location */ + PROTECT(wwInches = unit(ww, L_INCHES)); + transformDimn(wwInches, hhInches, 0, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, rotationAngle, + &dw, &dh); + xxx[2] = xxx[0] + dw; + yyy[2] = yyy[0] + dh; + /* Find bottom-right location */ + transformDimn(wwInches, zeroInches, 0, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, rotationAngle, + &dw, &dh); + xxx[3] = xxx[0] + dw; + yyy[3] = yyy[0] + dh; + if (R_FINITE(xxx[0]) && R_FINITE(yyy[0]) && + R_FINITE(xxx[1]) && R_FINITE(yyy[1]) && + R_FINITE(xxx[2]) && R_FINITE(yyy[2]) && + R_FINITE(xxx[3]) && R_FINITE(yyy[3])) { + /* The graphics engine only takes device coordinates + */ + xxx[0] = toDeviceX(xxx[0], GE_INCHES, dd); + yyy[0] = toDeviceY(yyy[0], GE_INCHES, dd); + xxx[1] = toDeviceX(xxx[1], GE_INCHES, dd); + yyy[1] = toDeviceY(yyy[1], GE_INCHES, dd); + xxx[2] = toDeviceX(xxx[2], GE_INCHES, dd); + yyy[2] = toDeviceY(yyy[2], GE_INCHES, dd); + xxx[3] = toDeviceX(xxx[3], GE_INCHES, dd); + yyy[3] = toDeviceY(yyy[3], GE_INCHES, dd); + /* Close the polygon */ + xxx[4] = xxx[0]; + yyy[4] = yyy[0]; + /* Do separate fill and border to avoid border being + * drawn on clipping boundary when there is a fill + */ + tmpcol = gc.col; + gc.col = R_TRANWHITE; + GEPolygon(5, xxx, yyy, &gc, dd); + gc.col = tmpcol; + gc.fill = R_TRANWHITE; + GEPolygon(5, xxx, yyy, &gc, dd); + } + UNPROTECT(5); + } + } else { /* Just calculating boundary */ + xx = justifyX(xx, ww, REAL(hjust)[i % LENGTH(hjust)]); + yy = justifyY(yy, hh, REAL(vjust)[i % LENGTH(vjust)]); + if (R_FINITE(xx) && R_FINITE(yy) && + R_FINITE(ww) && R_FINITE(hh)) { + if (xx < xmin) + xmin = xx; + if (xx > xmax) + xmax = xx; + if (xx + ww < xmin) + xmin = xx + ww; + if (xx + ww > xmax) + xmax = xx + ww; + if (yy < ymin) + ymin = yy; + if (yy > ymax) + ymax = yy; + if (yy + hh < ymin) + ymin = yy + hh; + if (yy + hh > ymax) + ymax = yy + hh; + /* + * Calculate edgex and edgey for case where this is + * the only rect + */ + rectEdge(xx, yy, xx + ww, yy + hh, theta, + &edgex, &edgey); + nrect++; + } + } + } + if (draw) { + GEMode(0, dd); + } + if (nrect > 0) { + result = allocVector(REALSXP, 4); + /* + * If there is more than one rect, just produce edge + * based on bounding rect of all rects + */ + if (nrect > 1) { + rectEdge(xmin, ymin, xmax, ymax, theta, + &edgex, &edgey); + } + /* + * Reverse the scale adjustment (zoom factor) + * when calculating physical value to return to user-level + */ + REAL(result)[0] = edgex / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + REAL(result)[1] = edgey / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + REAL(result)[2] = (xmax - xmin) / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + REAL(result)[3] = (ymax - ymin) / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + } + return result; +} + +SEXP L_rect(SEXP x, SEXP y, SEXP w, SEXP h, SEXP hjust, SEXP vjust) +{ + gridRect(x, y, w, h, hjust, vjust, 0, TRUE); + return R_NilValue; +} + +SEXP L_rectBounds(SEXP x, SEXP y, SEXP w, SEXP h, SEXP hjust, SEXP vjust, + SEXP theta) +{ + return gridRect(x, y, w, h, hjust, vjust, REAL(theta)[0], FALSE); +} + +/* FIXME: need to add L_pathBounds ? */ + +SEXP L_path(SEXP x, SEXP y, SEXP index, SEXP rule) +{ + int i, j, k, npoly, *nper, ntot; + double *xx, *yy; + const void *vmax; + double vpWidthCM, vpHeightCM; + double rotationAngle; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform; + SEXP currentvp, currentgp; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + getViewportContext(currentvp, &vpc); + GEMode(1, dd); + vmax = vmaxget(); + /* + * Number of polygons + */ + npoly = LENGTH(index); + /* + * Total number of points and + * Number of points per polygon + */ + ntot = 0; + nper = (int *) R_alloc(npoly, sizeof(int)); + for (i=0; i < npoly; i++) { + nper[i] = LENGTH(VECTOR_ELT(index, i)); + ntot = ntot + nper[i]; + } + xx = (double *) R_alloc(ntot, sizeof(double)); + yy = (double *) R_alloc(ntot, sizeof(double)); + k = 0; + for (i=0; i < npoly; i++) { + SEXP indices = VECTOR_ELT(index, i); + for (j=0; j < nper[i]; j++) { + transformLocn(x, y, INTEGER(indices)[j] - 1, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, + transform, + &(xx[k]), &(yy[k])); + /* The graphics engine only takes device coordinates + */ + xx[k] = toDeviceX(xx[k], GE_INCHES, dd); + yy[k] = toDeviceY(yy[k], GE_INCHES, dd); + /* NO NA values allowed in 'x' or 'y' + */ + if (!R_FINITE(xx[k]) || !R_FINITE(yy[k])) + error(_("non-finite x or y in graphics path")); + k++; + } + } + gcontextFromgpar(currentgp, 0, &gc, dd); + GEPath(xx, yy, npoly, nper, INTEGER(rule)[0], &gc, dd); + vmaxset(vmax); + GEMode(0, dd); + return R_NilValue; +} + +/* FIXME: need to add L_rasterBounds */ + +/* FIXME: Add more checks on correct inputs, + e.g., Raster should be a matrix of R colors */ +SEXP L_raster(SEXP raster, SEXP x, SEXP y, SEXP w, SEXP h, + SEXP hjust, SEXP vjust, SEXP interpolate) +{ + const void *vmax; + int i, n, ny, nw, nh, maxn; + double xx, yy, ww, hh; + double vpWidthCM, vpHeightCM; + double rotationAngle; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform; + SEXP currentvp, currentgp; + SEXP dim; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + unsigned int *image; + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + getViewportContext(currentvp, &vpc); + /* Convert the raster matrix to R internal colours */ + n = LENGTH(raster); + if (n <= 0) { + error(_("Empty raster")); + } + vmax = vmaxget(); + /* raster is rather inefficient so allow a native representation as + an integer array which requires no conversion */ + if (inherits(raster, "nativeRaster") && isInteger(raster)) { + image = (unsigned int*) INTEGER(raster); + } else { + image = (unsigned int*) R_alloc(n, sizeof(unsigned int)); + for (i=0; i<n; i++) { + image[i] = RGBpar3(raster, i, R_TRANWHITE); + } + } + dim = getAttrib(raster, R_DimSymbol); + maxn = unitLength(x); + ny = unitLength(y); + nw = unitLength(w); + nh = unitLength(h); + if (ny > maxn) + maxn = ny; + if (nw > maxn) + maxn = nw; + if (nh > maxn) + maxn = nh; + GEMode(1, dd); + for (i=0; i<maxn; i++) { + gcontextFromgpar(currentgp, i, &gc, dd); + transformLocn(x, y, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, + transform, + &xx, &yy); + ww = transformWidthtoINCHES(w, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + hh = transformHeighttoINCHES(h, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + if (rotationAngle == 0) { + xx = justifyX(xx, ww, REAL(hjust)[i % LENGTH(hjust)]); + yy = justifyY(yy, hh, REAL(vjust)[i % LENGTH(vjust)]); + /* The graphics engine only takes device coordinates + */ + xx = toDeviceX(xx, GE_INCHES, dd); + yy = toDeviceY(yy, GE_INCHES, dd); + ww = toDeviceWidth(ww, GE_INCHES, dd); + hh = toDeviceHeight(hh, GE_INCHES, dd); + if (R_FINITE(xx) && R_FINITE(yy) && + R_FINITE(ww) && R_FINITE(hh)) + GERaster(image, INTEGER(dim)[1], INTEGER(dim)[0], + xx, yy, ww, hh, rotationAngle, + LOGICAL(interpolate)[i % LENGTH(interpolate)], + &gc, dd); + } else { + /* We have to do a little bit of work to figure out where the + * bottom-left corner of the image is. + */ + double xbl, ybl, xadj, yadj; + double dw, dh; + SEXP xadjInches, yadjInches; + /* Find bottom-left location */ + justification(ww, hh, + REAL(hjust)[i % LENGTH(hjust)], + REAL(vjust)[i % LENGTH(vjust)], + &xadj, &yadj); + PROTECT(xadjInches = unit(xadj, L_INCHES)); + PROTECT(yadjInches = unit(yadj, L_INCHES)); + transformDimn(xadjInches, yadjInches, 0, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, rotationAngle, + &dw, &dh); + xbl = xx + dw; + ybl = yy + dh; + xbl = toDeviceX(xbl, GE_INCHES, dd); + ybl = toDeviceY(ybl, GE_INCHES, dd); + ww = toDeviceWidth(ww, GE_INCHES, dd); + hh = toDeviceHeight(hh, GE_INCHES, dd); + if (R_FINITE(xbl) && R_FINITE(ybl) && + R_FINITE(ww) && R_FINITE(hh)) { + /* The graphics engine only takes device coordinates + */ + GERaster(image, INTEGER(dim)[1], INTEGER(dim)[0], + xbl, ybl, ww, hh, rotationAngle, + LOGICAL(interpolate)[i % LENGTH(interpolate)], + &gc, dd); + } + UNPROTECT(2); + } + } + GEMode(0, dd); + vmaxset(vmax); + return R_NilValue; +} + +SEXP L_cap() +{ + int i, col, row, nrow, ncol, size; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + int *rint; + SEXP raster; + /* The raster is R internal colours, so convert to + * R external colours (strings) + * AND the raster is BY ROW so need to rearrange it + * to be BY COLUMN (though the dimensions are correct) */ + SEXP image, idim; + + PROTECT(raster = GECap(dd)); + /* Non-complying devices will return NULL */ + if (isNull(raster)) { + image = raster; + } else { + size = LENGTH(raster); + nrow = INTEGER(getAttrib(raster, R_DimSymbol))[0]; + ncol = INTEGER(getAttrib(raster, R_DimSymbol))[1]; + + PROTECT(image = allocVector(STRSXP, size)); + rint = INTEGER(raster); + for (i=0; i<size; i++) { + col = i % ncol + 1; + row = i / ncol + 1; + SET_STRING_ELT(image, (col - 1)*nrow + row - 1, + mkChar(col2name(rint[i]))); + } + + PROTECT(idim = allocVector(INTSXP, 2)); + INTEGER(idim)[0] = nrow; + INTEGER(idim)[1] = ncol; + setAttrib(image, R_DimSymbol, idim); + + UNPROTECT(2); + } + UNPROTECT(1); + return image; +} + +/* + * Code to draw OR size text + * Combined to avoid code replication + */ +static SEXP gridText(SEXP label, SEXP x, SEXP y, SEXP hjust, SEXP vjust, + SEXP rot, SEXP checkOverlap, double theta, Rboolean draw) +{ + int i, nx, ny; + double *xx, *yy; + double vpWidthCM, vpHeightCM; + double rotationAngle; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform; + SEXP txt, result = R_NilValue; + double edgex, edgey; + double xmin = DOUBLE_XMAX; + double xmax = -DOUBLE_XMAX; + double ymin = DOUBLE_XMAX; + double ymax = -DOUBLE_XMAX; + /* + * Bounding rectangles for checking overlapping + * Initialised to shut up compiler + */ + LRect *bounds = NULL; + LRect trect; + int numBounds = 0; + int overlapChecking = LOGICAL(checkOverlap)[0]; + const void *vmax; + SEXP currentvp, currentgp; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + getViewportContext(currentvp, &vpc); + nx = unitLength(x); + ny = unitLength(y); + if (ny > nx) + nx = ny; + vmax = vmaxget(); + xx = (double *) R_alloc(nx, sizeof(double)); + yy = (double *) R_alloc(nx, sizeof(double)); + for (i=0; i<nx; i++) { + gcontextFromgpar(currentgp, i, &gc, dd); + /* + * If drawing, convert to INCHES on device + * If just calculating bounds, convert to INCHES within current vp + */ + if (draw) { + transformLocn(x, y, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, + transform, + &(xx[i]), &(yy[i])); + } else { + xx[i] = transformXtoINCHES(x, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + yy[i] = transformYtoINCHES(y, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + } + } + /* The label can be a string or an expression + */ + PROTECT(txt = label); + if (isSymbol(txt) || isLanguage(txt)) + txt = coerceVector(txt, EXPRSXP); + else if (!isExpression(txt)) + txt = coerceVector(txt, STRSXP); + if (overlapChecking || !draw) { + bounds = (LRect *) R_alloc(nx, sizeof(LRect)); + } + /* + * Check we have any text to draw + */ + if (LENGTH(txt) > 0) { + int ntxt = 0; + if (draw) { + /* + * Drawing text + */ + GEMode(1, dd); + } + for (i=0; i<nx; i++) { + int doDrawing = 1; + gcontextFromgpar(currentgp, i, &gc, dd); + /* + * Generate bounding boxes when checking for overlap + * or sizing text + */ + if (overlapChecking || !draw) { + int j = 0; + textRect(xx[i], yy[i], txt, i, &gc, + REAL(hjust)[i % LENGTH(hjust)], + REAL(vjust)[i % LENGTH(vjust)], + /* + * When calculating bounding rect for text + * only consider rotation of text within + * local context, not relative to device + * (so don't add rotationAngle) + */ + numeric(rot, i % LENGTH(rot)), + dd, &trect); + while (doDrawing && (j < numBounds)) + if (intersect(trect, bounds[j++])) + doDrawing = 0; + if (doDrawing) { + copyRect(trect, &(bounds[numBounds])); + numBounds++; + } + } + if (draw && doDrawing) { + /* The graphics engine only takes device coordinates + */ + xx[i] = toDeviceX(xx[i], GE_INCHES, dd); + yy[i] = toDeviceY(yy[i], GE_INCHES, dd); + if (R_FINITE(xx[i]) && R_FINITE(yy[i])) { + gcontextFromgpar(currentgp, i, &gc, dd); + if (isExpression(txt)) + GEMathText(xx[i], yy[i], + VECTOR_ELT(txt, i % LENGTH(txt)), + REAL(hjust)[i % LENGTH(hjust)], + REAL(vjust)[i % LENGTH(vjust)], + numeric(rot, i % LENGTH(rot)) + + rotationAngle, + &gc, dd); + else + GEText(xx[i], yy[i], + CHAR(STRING_ELT(txt, i % LENGTH(txt))), + (gc.fontface == 5) ? CE_SYMBOL : + getCharCE(STRING_ELT(txt, i % LENGTH(txt))), + REAL(hjust)[i % LENGTH(hjust)], + REAL(vjust)[i % LENGTH(vjust)], + numeric(rot, i % LENGTH(rot)) + + rotationAngle, + &gc, dd); + } + } + if (!draw) { + double minx, maxx, miny, maxy; + /* + * Sizing text + */ + if (R_FINITE(xx[i]) && R_FINITE(yy[i])) { + minx = fmin2(trect.x1, + fmin2(trect.x2, + fmin2(trect.x3, trect.x4))); + if (minx < xmin) + xmin = minx; + maxx = fmax2(trect.x1, + fmax2(trect.x2, + fmax2(trect.x3, trect.x4))); + if (maxx > xmax) + xmax = maxx; + miny = fmin2(trect.y1, + fmin2(trect.y2, + fmin2(trect.y3, trect.y4))); + if (miny < ymin) + ymin = miny; + maxy = fmax2(trect.y1, + fmax2(trect.y2, + fmax2(trect.y3, trect.y4))); + if (maxy > ymax) + ymax = maxy; + /* + * Calculate edgex and edgey for case where this is + * the only rect + */ + { + double xxx[4], yyy[4]; + /* + * Must be in clock-wise order for polygonEdge + */ + xxx[0] = trect.x4; yyy[0] = trect.y4; + xxx[1] = trect.x3; yyy[1] = trect.y3; + xxx[2] = trect.x2; yyy[2] = trect.y2; + xxx[3] = trect.x1; yyy[3] = trect.y1; + polygonEdge(xxx, yyy, 4, theta, + &edgex, &edgey); + } + ntxt++; + } + } + } + if (draw) { + GEMode(0, dd); + } + if (ntxt > 0) { + result = allocVector(REALSXP, 4); + /* + * If there is more than one text, just produce edge + * based on bounding rect of all text + */ + if (ntxt > 1) { + /* + * Produce edge of rect bounding all text + */ + rectEdge(xmin, ymin, xmax, ymax, theta, + &edgex, &edgey); + } + /* + * Reverse the scale adjustment (zoom factor) + * when calculating physical value to return to user-level + */ + REAL(result)[0] = edgex / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + REAL(result)[1] = edgey / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + REAL(result)[2] = (xmax - xmin) / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + REAL(result)[3] = (ymax - ymin) / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + } + } + vmaxset(vmax); + UNPROTECT(1); + return result; +} + +SEXP L_text(SEXP label, SEXP x, SEXP y, SEXP hjust, SEXP vjust, + SEXP rot, SEXP checkOverlap) +{ + gridText(label, x, y, hjust, vjust, rot, checkOverlap, 0, TRUE); + return R_NilValue; +} + +/* + * Return four values representing boundary of text (which may consist + * of multiple pieces of text, unaligned, and/or rotated) + * in INCHES. + * + * Result is (xmin, xmax, ymin, ymax) + * + * Return NULL if no text to draw; R code will generate unit from that + */ +SEXP L_textBounds(SEXP label, SEXP x, SEXP y, + SEXP hjust, SEXP vjust, SEXP rot, SEXP theta) +{ + SEXP checkOverlap = allocVector(LGLSXP, 1); + LOGICAL(checkOverlap)[0] = FALSE; + return gridText(label, x, y, hjust, vjust, rot, checkOverlap, + REAL(theta)[0], FALSE); +} + +SEXP L_points(SEXP x, SEXP y, SEXP pch, SEXP size) +{ + int i, nx, npch; + /* double *xx, *yy;*/ + double *xx, *yy; + double vpWidthCM, vpHeightCM; + double rotationAngle; + double symbolSize; + const void *vmax; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform; + SEXP currentvp, currentgp; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + getViewportContext(currentvp, &vpc); + nx = unitLength(x); + npch = LENGTH(pch); + /* Convert the x and y values to CM locations */ + vmax = vmaxget(); + xx = (double *) R_alloc(nx, sizeof(double)); + yy = (double *) R_alloc(nx, sizeof(double)); + for (i=0; i<nx; i++) { + gcontextFromgpar(currentgp, i, &gc, dd); + transformLocn(x, y, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, + transform, + &(xx[i]), &(yy[i])); + /* The graphics engine only takes device coordinates + */ + xx[i] = toDeviceX(xx[i], GE_INCHES, dd); + yy[i] = toDeviceY(yy[i], GE_INCHES, dd); + } + GEMode(1, dd); + for (i=0; i<nx; i++) + if (R_FINITE(xx[i]) && R_FINITE(yy[i])) { + /* FIXME: The symbols will not respond to viewport + * rotations !!! + */ + int ipch = NA_INTEGER /* -Wall */; + gcontextFromgpar(currentgp, i, &gc, dd); + symbolSize = transformWidthtoINCHES(size, i, vpc, &gc, + vpWidthCM, vpHeightCM, dd); + /* The graphics engine only takes device coordinates + */ + symbolSize = toDeviceWidth(symbolSize, GE_INCHES, dd); + if (R_FINITE(symbolSize)) { + /* + * FIXME: + * Resolve any differences between this and FixupPch() + * in plot.c ? + */ + if (isString(pch)) { + ipch = GEstring_to_pch(STRING_ELT(pch, i % npch)); + } else if (isInteger(pch)) { + ipch = INTEGER(pch)[i % npch]; + } else if (isReal(pch)) { + ipch = R_FINITE(REAL(pch)[i % npch]) ? + (int) REAL(pch)[i % npch] : NA_INTEGER; + } else error(_("invalid plotting symbol")); + /* + * special case for pch = "." + */ + if (ipch == 46) symbolSize = gpCex(currentgp, i); + /* + * FIXME: + * For character-based symbols, we need to modify + * gc->cex so that the FONT size corresponds to + * the specified symbolSize. + */ + GESymbol(xx[i], yy[i], ipch, symbolSize, &gc, dd); + } + } + GEMode(0, dd); + vmaxset(vmax); + return R_NilValue; +} + +SEXP L_clip(SEXP x, SEXP y, SEXP w, SEXP h, SEXP hjust, SEXP vjust) +{ + double xx, yy, ww, hh; + double vpWidthCM, vpHeightCM; + double rotationAngle; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform; + SEXP currentvp, currentgp, currentClip; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + getViewportContext(currentvp, &vpc); + GEMode(1, dd); + /* + * Only set ONE clip rectangle (i.e., NOT vectorised) + */ + gcontextFromgpar(currentgp, 0, &gc, dd); + transformLocn(x, y, 0, vpc, &gc, + vpWidthCM, vpHeightCM, + dd, + transform, + &xx, &yy); + ww = transformWidthtoINCHES(w, 0, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + hh = transformHeighttoINCHES(h, 0, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + /* + * We can ONLY clip if the total rotation angle is zero. + */ + if (rotationAngle == 0) { + xx = justifyX(xx, ww, REAL(hjust)[0]); + yy = justifyY(yy, hh, REAL(vjust)[0]); + /* The graphics engine only takes device coordinates + */ + xx = toDeviceX(xx, GE_INCHES, dd); + yy = toDeviceY(yy, GE_INCHES, dd); + ww = toDeviceWidth(ww, GE_INCHES, dd); + hh = toDeviceHeight(hh, GE_INCHES, dd); + if (R_FINITE(xx) && R_FINITE(yy) && + R_FINITE(ww) && R_FINITE(hh)) { + GESetClip(xx, yy, xx + ww, yy + hh, dd); + /* + * ALSO set the current clip region for the + * current viewport so that, if a viewport + * is pushed within the current viewport, + * when that viewport gets popped again, + * the clip region returns to what was set + * by THIS clipGrob (NOT to the current + * viewport's previous setting) + */ + PROTECT(currentClip = allocVector(REALSXP, 4)); + REAL(currentClip)[0] = xx; + REAL(currentClip)[1] = yy; + REAL(currentClip)[2] = xx + ww; + REAL(currentClip)[3] = yy + hh; + SET_VECTOR_ELT(currentvp, PVP_CLIPRECT, currentClip); + UNPROTECT(1); + } + } else { + warning(_("unable to clip to rotated rectangle")); + } + GEMode(0, dd); + return R_NilValue; +} + +SEXP L_pretty(SEXP scale) { + double min = numeric(scale, 0); + double max = numeric(scale, 1); + double temp; + /* FIXME: This is just a dummy pointer because we do not have + * log scales. This will cause death and destruction if it is + * not addressed when log scales are added ! + */ + double *usr = NULL; + double axp[3]; + /* FIXME: Default preferred number of ticks hard coded ! */ + int n = 5; + Rboolean swap = min > max; + /* + * Feature: + * like R, something like xscale = c(100,0) just works + */ + if(swap) { + temp = min; min = max; max = temp; + } + + GEPretty(&min, &max, &n); + + if(swap) { + temp = min; min = max; max = temp; + } + + axp[0] = min; + axp[1] = max; + axp[2] = n; + /* FIXME: "log" flag hard-coded to FALSE because we do not + * have log scales yet + */ + return Rf_CreateAtVector(axp, usr, n, FALSE); +} + +/* + * NOTE: This does not go through the graphics engine, but + * skips straight to the device to obtain a mouse click. + * This is because I do not want to put a GELocator in the + * graphics engine; that would be a crappy long term solution. + * I will wait for a better event-loop/call-back solution before + * doing something with the graphics engine. + * This is a stop gap in the meantime. + * + * The answer is in INCHES + */ + +SEXP L_locator() { + double x = 0; + double y = 0; + SEXP answer; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + GEMode(2, dd); + PROTECT(answer = allocVector(REALSXP, 2)); + /* + * Get a mouse click + * Fails if user did not click mouse button 1 + */ + if(dd->dev->locator && dd->dev->locator(&x, &y, dd->dev)) { + REAL(answer)[0] = fromDeviceX(x, GE_INCHES, dd); + REAL(answer)[1] = fromDeviceY(y, GE_INCHES, dd); + } else { + REAL(answer)[0] = NA_REAL; + REAL(answer)[1] = NA_REAL; + } + UNPROTECT(1); + GEMode(0, dd); + return answer; +} + +/* + * **************************************** + * Calculating boundaries of primitives + * + * **************************************** + */ + +/* + * Return four values representing boundary of set of locations + * in INCHES. + * + * Result is (xmin, xmax, ymin, ymax) + * + * Used for lines, segments, polygons + */ +SEXP L_locnBounds(SEXP x, SEXP y, SEXP theta) +{ + int i, nx, ny, nloc; + double *xx, *yy; + double vpWidthCM, vpHeightCM; + double rotationAngle; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform; + SEXP currentvp, currentgp; + SEXP result = R_NilValue; + const void *vmax; + double xmin = DOUBLE_XMAX; + double xmax = -DOUBLE_XMAX; + double ymin = DOUBLE_XMAX; + double ymax = -DOUBLE_XMAX; + double edgex, edgey; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + getViewportContext(currentvp, &vpc); + nx = unitLength(x); + ny = unitLength(y); + if (ny > nx) + nx = ny; + nloc = 0; + vmax = vmaxget(); + if (nx > 0) { + xx = (double *) R_alloc(nx, sizeof(double)); + yy = (double *) R_alloc(nx, sizeof(double)); + for (i=0; i<nx; i++) { + gcontextFromgpar(currentgp, i, &gc, dd); + xx[i] = transformXtoINCHES(x, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + yy[i] = transformYtoINCHES(y, i, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + /* + * Determine min/max x/y values + */ + if (R_FINITE(xx[i]) && R_FINITE(yy[i])) { + if (xx[i] < xmin) + xmin = xx[i]; + if (xx[i] > xmax) + xmax = xx[i]; + if (yy[i] < ymin) + ymin = yy[i]; + if (yy[i] > ymax) + ymax = yy[i]; + nloc++; + } + } + } + if (nloc > 0) { + hullEdge(xx, yy, nx, REAL(theta)[0], &edgex, &edgey); + result = allocVector(REALSXP, 4); + /* + * Reverse the scale adjustment (zoom factor) + * when calculating physical value to return to user-level + */ + REAL(result)[0] = edgex / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + REAL(result)[1] = edgey / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + REAL(result)[2] = (xmax - xmin) / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + REAL(result)[3] = (ymax - ymin) / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + } + vmaxset(vmax); + return result; +} + +/* + * **************************************** + * Calculating text metrics + * + * **************************************** + */ +SEXP L_stringMetric(SEXP label) +{ + int i, n; + double vpWidthCM, vpHeightCM; + double rotationAngle; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform; + SEXP currentvp, currentgp; + SEXP txt; + SEXP result = R_NilValue; + SEXP ascent = R_NilValue; + SEXP descent = R_NilValue; + SEXP width = R_NilValue; + const void *vmax; + double asc, dsc, wid; + /* Get the current device + */ + pGEDevDesc dd = getDevice(); + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + getViewportContext(currentvp, &vpc); + /* The label can be a string or an expression: is protected. + */ + txt = label; + if (isSymbol(txt) || isLanguage(txt)) + txt = coerceVector(txt, EXPRSXP); + else if (!isExpression(txt)) + txt = coerceVector(txt, STRSXP); + PROTECT(txt); + n = LENGTH(txt); + vmax = vmaxget(); + PROTECT(ascent = allocVector(REALSXP, n)); + PROTECT(descent = allocVector(REALSXP, n)); + PROTECT(width = allocVector(REALSXP, n)); + if (n > 0) { + for (i=0; i<n; i++) { + gcontextFromgpar(currentgp, i, &gc, dd); + if (isExpression(txt)) + GEExpressionMetric(VECTOR_ELT(txt, i % LENGTH(txt)), &gc, + &asc, &dsc, &wid, + dd); + else + GEStrMetric(CHAR(STRING_ELT(txt, i)), + getCharCE(STRING_ELT(txt, i)), &gc, + &asc, &dsc, &wid, + dd); + /* + * Reverse the scale adjustment (zoom factor) + * when calculating physical value to return to user-level + */ + REAL(ascent)[i] = fromDeviceHeight(asc, GE_INCHES, dd) / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + REAL(descent)[i] = fromDeviceHeight(dsc, GE_INCHES, dd) / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + REAL(width)[i] = fromDeviceWidth(wid, GE_INCHES, dd) / + REAL(gridStateElement(dd, GSS_SCALE))[0]; + } + } + PROTECT(result = allocVector(VECSXP, 3)); + SET_VECTOR_ELT(result, 0, ascent); + SET_VECTOR_ELT(result, 1, descent); + SET_VECTOR_ELT(result, 2, width); + vmaxset(vmax); + UNPROTECT(5); + return result; +} + diff --git a/com.oracle.truffle.r.native/library/grid/src/grid.h b/com.oracle.truffle.r.native/library/grid/src/grid.h new file mode 100644 index 0000000000..81ac600478 --- /dev/null +++ b/com.oracle.truffle.r.native/library/grid/src/grid.h @@ -0,0 +1,633 @@ +/* + * R : A Computer Language for Statistical Data Analysis + * Copyright (C) 2001-3 Paul Murrell + * 2003-8 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/ + */ + +#include <Rconfig.h> +#include <Rinternals.h> +#include <Rmath.h> + +#include <R_ext/Constants.h> +#include <R_ext/GraphicsEngine.h> + +#include <Rinternals.h> +#ifdef ENABLE_NLS +#include <libintl.h> +#define _(String) dgettext ("grid", String) +#else +#define _(String) (String) +#endif + +/* All grid type names are prefixed with an "L" + * All grid global variable names are prefixed with an "L_" + */ + +/* This information is stored with R's graphics engine so that + * grid can have state information per device and grid output can + * be maintained on multiple devices. + */ + +#define GSS_DEVSIZE 0 +#define GSS_CURRLOC 1 +#define GSS_DL 2 +#define GSS_DLINDEX 3 +#define GSS_DLON 4 +#define GSS_GPAR 5 +#define GSS_GPSAVED 6 +#define GSS_VP 7 +#define GSS_GLOBALINDEX 8 +#define GSS_GRIDDEVICE 9 +#define GSS_PREVLOC 10 +#define GSS_ENGINEDLON 11 +#define GSS_CURRGROB 12 +#define GSS_ENGINERECORDING 13 +/* #define GSS_ASK 14 unused in R >= 2.7.0 */ +#define GSS_SCALE 15 + +/* + * Structure of a viewport + */ +#define VP_X 0 +#define VP_Y 1 +#define VP_WIDTH 2 +#define VP_HEIGHT 3 +#define VP_JUST 4 +#define VP_GP 5 +#define VP_CLIP 6 +#define VP_XSCALE 7 +#define VP_YSCALE 8 +#define VP_ANGLE 9 +#define VP_LAYOUT 10 +#define VP_LPOSROW 11 +#define VP_LPOSCOL 12 +#define VP_VALIDJUST 13 +#define VP_VALIDLPOSROW 14 +#define VP_VALIDLPOSCOL 15 +#define VP_NAME 16 +/* + * Additional structure of a pushedvp + */ +#define PVP_PARENTGPAR 17 +#define PVP_GPAR 18 +#define PVP_TRANS 19 +#define PVP_WIDTHS 20 +#define PVP_HEIGHTS 21 +#define PVP_WIDTHCM 22 +#define PVP_HEIGHTCM 23 +#define PVP_ROTATION 24 +#define PVP_CLIPRECT 25 +#define PVP_PARENT 26 +#define PVP_CHILDREN 27 +#define PVP_DEVWIDTHCM 28 +#define PVP_DEVHEIGHTCM 29 + +/* + * Structure of a layout + */ +#define LAYOUT_NROW 0 +#define LAYOUT_NCOL 1 +#define LAYOUT_WIDTHS 2 +#define LAYOUT_HEIGHTS 3 +#define LAYOUT_RESPECT 4 +#define LAYOUT_VRESPECT 5 +#define LAYOUT_MRESPECT 6 +#define LAYOUT_JUST 7 +#define LAYOUT_VJUST 8 + +#define GP_FILL 0 +#define GP_COL 1 +#define GP_GAMMA 2 +#define GP_LTY 3 +#define GP_LWD 4 +#define GP_CEX 5 +#define GP_FONTSIZE 6 +#define GP_LINEHEIGHT 7 +#define GP_FONT 8 +#define GP_FONTFAMILY 9 +#define GP_ALPHA 10 +#define GP_LINEEND 11 +#define GP_LINEJOIN 12 +#define GP_LINEMITRE 13 +#define GP_LEX 14 +/* + * Keep fontface at the end because it is never used in C code + */ +#define GP_FONTFACE 15 + +/* + * Structure of an arrow description + */ +#define GRID_ARROWANGLE 0 +#define GRID_ARROWLENGTH 1 +#define GRID_ARROWENDS 2 +#define GRID_ARROWTYPE 3 + +typedef double LTransform[3][3]; + +typedef double LLocation[3]; + +typedef enum { + L_adding = 1, + L_subtracting = 2, + L_summing = 3, + L_plain = 4, + L_maximising = 5, + L_minimising = 6, + L_multiplying = 7 +} LNullArithmeticMode; + +/* NOTE: The order of the enums here must match the order of the + * strings in unit.R + */ +typedef enum { + L_NPC = 0, + L_CM = 1, + L_INCHES = 2, + L_LINES = 3, + L_NATIVE = 4, + L_NULL = 5, /* only used in layout specifications (?) */ + L_SNPC = 6, + L_MM = 7, + /* Some units based on TeX's definition thereof + */ + L_POINTS = 8, /* 72.27 pt = 1 in */ + L_PICAS = 9, /* 1 pc = 12 pt */ + L_BIGPOINTS = 10, /* 72 bp = 1 in */ + L_DIDA = 11, /* 1157 dd = 1238 pt */ + L_CICERO = 12, /* 1 cc = 12 dd */ + L_SCALEDPOINTS = 13, /* 65536 sp = 1pt */ + /* Some units which require an object to query for a value. + */ + L_STRINGWIDTH = 14, + L_STRINGHEIGHT = 15, + L_STRINGASCENT = 16, + L_STRINGDESCENT = 17, + /* L_LINES now means multiples of the line height. + * This is multiples of the font size. + */ + L_CHAR = 18, + L_GROBX = 19, + L_GROBY = 20, + L_GROBWIDTH = 21, + L_GROBHEIGHT = 22, + L_GROBASCENT = 23, + L_GROBDESCENT = 24, + /* + * No longer used + */ + L_MYLINES = 103, + L_MYCHAR = 104, + L_MYSTRINGWIDTH = 105, + L_MYSTRINGHEIGHT = 106 +} LUnit; + +typedef enum { + L_LEFT = 0, + L_RIGHT = 1, + L_BOTTOM = 2, + L_TOP = 3, + L_CENTRE = 4, + L_CENTER = 5 +} LJustification; + +/* An arbitrarily-oriented rectangle. + * The vertices are assumed to be in order going anticlockwise + * around the rectangle. + */ +typedef struct { + double x1; + double x2; + double x3; + double x4; + double y1; + double y2; + double y3; + double y4; +} LRect; + +/* A description of the location of a viewport */ +typedef struct { + SEXP x; + SEXP y; + SEXP width; + SEXP height; + double hjust; + double vjust; +} LViewportLocation; + +/* Components of a viewport which provide coordinate information + * for children of the viewport + */ +typedef struct { + double xscalemin; + double xscalemax; + double yscalemin; + double yscalemax; +} LViewportContext; + +/* Evaluation environment */ +#ifndef GRID_MAIN +extern SEXP R_gridEvalEnv; +#else +SEXP R_gridEvalEnv; +#endif + + +/* Functions called by R code + * (from all over the place) + */ +SEXP L_initGrid(SEXP GridEvalEnv); +SEXP L_killGrid(); +SEXP L_gridDirty(); +SEXP L_currentViewport(); +SEXP L_setviewport(SEXP vp, SEXP hasParent); +SEXP L_downviewport(SEXP vp, SEXP strict); +SEXP L_downvppath(SEXP path, SEXP name, SEXP strict); +SEXP L_unsetviewport(SEXP last); +SEXP L_upviewport(SEXP last); +SEXP L_getDisplayList(); +SEXP L_setDisplayList(SEXP dl); +SEXP L_getDLelt(SEXP index); +SEXP L_setDLelt(SEXP value); +SEXP L_getDLindex(); +SEXP L_setDLindex(SEXP index); +SEXP L_getDLon(); +SEXP L_setDLon(SEXP value); +SEXP L_getEngineDLon(); +SEXP L_setEngineDLon(SEXP value); +SEXP L_getCurrentGrob(); +SEXP L_setCurrentGrob(SEXP value); +SEXP L_getEngineRecording(); +SEXP L_setEngineRecording(SEXP value); +SEXP L_currentGPar(); +SEXP L_newpagerecording(); +SEXP L_newpage(); +SEXP L_initGPar(); +SEXP L_initViewportStack(); +SEXP L_initDisplayList(); +SEXP L_convertToNative(SEXP x, SEXP what); +SEXP L_moveTo(SEXP x, SEXP y); +SEXP L_lineTo(SEXP x, SEXP y, SEXP arrow); +SEXP L_lines(SEXP x, SEXP y, SEXP index, SEXP arrow); +SEXP L_segments(SEXP x0, SEXP y0, SEXP x1, SEXP y1, SEXP arrow); +SEXP L_arrows(SEXP x1, SEXP x2, SEXP xnm1, SEXP xn, + SEXP y1, SEXP y2, SEXP ynm1, SEXP yn, + SEXP angle, SEXP length, SEXP ends, SEXP type); +SEXP L_path(SEXP x, SEXP y, SEXP index, SEXP rule); +SEXP L_polygon(SEXP x, SEXP y, SEXP index); +SEXP L_xspline(SEXP x, SEXP y, SEXP s, SEXP o, SEXP a, SEXP rep, SEXP index); +SEXP L_circle(SEXP x, SEXP y, SEXP r); +SEXP L_rect(SEXP x, SEXP y, SEXP w, SEXP h, SEXP hjust, SEXP vjust); +SEXP L_raster(SEXP raster, SEXP x, SEXP y, SEXP w, SEXP h, + SEXP hjust, SEXP vjust, SEXP interpolate); +SEXP L_cap(); +SEXP L_text(SEXP label, SEXP x, SEXP y, SEXP hjust, SEXP vjust, + SEXP rot, SEXP checkOverlap); +SEXP L_points(SEXP x, SEXP y, SEXP pch, SEXP size); +SEXP L_clip(SEXP x, SEXP y, SEXP w, SEXP h, SEXP hjust, SEXP vjust); +SEXP L_pretty(SEXP scale); +SEXP L_locator(); +SEXP L_convert(SEXP x, SEXP whatfrom, + SEXP whatto, SEXP unitto); +SEXP L_layoutRegion(SEXP layoutPosRow, SEXP layoutPosCol); + +SEXP L_stringMetric(SEXP label); + +/* From matrix.c */ +double locationX(LLocation l); + +double locationY(LLocation l); + +void copyTransform(LTransform t1, LTransform t2); + +void invTransform(LTransform t, LTransform invt); + +void identity(LTransform m); + +void translation(double tx, double ty, LTransform m); + +void scaling(double sx, double sy, LTransform m); + +void rotation(double theta, LTransform m); + +void multiply(LTransform m1, LTransform m2, LTransform m); + +void location(double x, double y, LLocation v); + +void trans(LLocation vin, LTransform m, LLocation vout); + +/* From unit.c */ +int isUnitArithmetic(SEXP ua); + +int isUnitList(SEXP ul); + +SEXP unit(double value, int unit); + +double unitValue(SEXP unit, int index); + +int unitUnit(SEXP unit, int index); + +SEXP unitData(SEXP unit, int index); + +int unitLength(SEXP u); + +extern int L_nullLayoutMode; + +double pureNullUnitValue(SEXP unit, int index); + +int pureNullUnit(SEXP unit, int index, pGEDevDesc dd); + +double transformX(SEXP x, int index, LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + int nullLMode, int nullAMode, + pGEDevDesc dd); + +double transformY(SEXP y, int index, LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + int nullLMode, int nullAMode, + pGEDevDesc dd); + +double transformWidth(SEXP width, int index, LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + int nullLMode, int nullAMode, + pGEDevDesc dd); + +double transformHeight(SEXP height, int index, LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + int nullLMode, int nullAMode, + pGEDevDesc dd); + +double transformXtoINCHES(SEXP x, int index, LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + pGEDevDesc dd); + +double transformYtoINCHES(SEXP y, int index, LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + pGEDevDesc dd); + +void transformLocn(SEXP x, SEXP y, int index, LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + pGEDevDesc dd, + LTransform t, + double *xx, double *yy); + +double transformWidthtoINCHES(SEXP w, int index, LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + pGEDevDesc dd); + +double transformHeighttoINCHES(SEXP h, int index, LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + pGEDevDesc dd); + +void transformDimn(SEXP w, SEXP h, int index, LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + pGEDevDesc dd, + double rotationAngle, + double *ww, double *hh); + +double transformXYFromINCHES(double location, int unit, + double scalemin, double scalemax, + const pGEcontext gc, + double thisCM, double otherCM, + pGEDevDesc dd); + +double transformWidthHeightFromINCHES(double value, int unit, + double scalemin, double scalemax, + const pGEcontext gc, + double thisCM, double otherCM, + pGEDevDesc dd); + +double transformXYtoNPC(double x, int from, double min, double max); + +double transformWHtoNPC(double x, int from, double min, double max); + +double transformXYfromNPC(double x, int to, double min, double max); + +double transformWHfromNPC(double x, int to, double min, double max); + +/* From just.c */ +double justifyX(double x, double width, double hjust); + +double justifyY(double y, double height, double vjust); + +double convertJust(int vjust); + +void justification(double width, double height, double hjust, double vjust, + double *hadj, double *vadj); + +/* From util.c */ +SEXP getListElement(SEXP list, char *str); + +void setListElement(SEXP list, char *str, SEXP value); + +SEXP getSymbolValue(char *symbolName); + +void setSymbolValue(char *symbolName, SEXP value); + +double numeric(SEXP x, int index); + +void rect(double x1, double x2, double x3, double x4, + double y1, double y2, double y3, double y4, + LRect *r); + +void copyRect(LRect r1, LRect *r); + +int intersect(LRect r1, LRect r2); + +void textRect(double x, double y, SEXP text, int i, + const pGEcontext gc, + double xadj, double yadj, + double rot, pGEDevDesc dd, LRect *r); + +/* From gpar.c */ +double gpFontSize(SEXP gp, int i); + +double gpLineHeight(SEXP gp, int i); + +int gpCol(SEXP gp, int i); + +SEXP gpFillSXP(SEXP gp); + +int gpFill(SEXP gp, int i); + +double gpGamma(SEXP gp, int i); + +int gpLineType(SEXP gp, int i); + +double gpLineWidth(SEXP gp, int i); + +double gpCex(SEXP gp, int i); + +int gpFont(SEXP gp, int i); + +const char* gpFontFamily(SEXP gp, int i); + +SEXP gpFontSXP(SEXP gp); + +SEXP gpFontFamilySXP(SEXP gp); + +SEXP gpFontSizeSXP(SEXP gp); + +SEXP gpLineHeightSXP(SEXP gp); + +void gcontextFromgpar(SEXP gp, int i, const pGEcontext gc, pGEDevDesc dd); + +void initGPar(pGEDevDesc dd); + +/* From viewport.c */ +SEXP viewportX(SEXP vp); + +SEXP viewportY(SEXP vp); + +SEXP viewportWidth(SEXP vp); + +SEXP viewportHeight(SEXP vp); + +SEXP viewportgpar(SEXP vp); + +const char* viewportFontFamily(SEXP vp); + +int viewportFont(SEXP vp); + +double viewportFontSize(SEXP vp); + +double viewportLineHeight(SEXP vp); + +Rboolean viewportClip(SEXP vp); + +SEXP viewportClipRect(SEXP vp); + +double viewportXScaleMin(SEXP vp); + +double viewportXScaleMax(SEXP vp); + +double viewportYScaleMin(SEXP vp); + +double viewportYScaleMax(SEXP vp); + +double viewportHJust(SEXP v); + +double viewportVJust(SEXP vp); + +SEXP viewportLayoutPosRow(SEXP vp); + +SEXP viewportLayoutPosCol(SEXP vp); + +SEXP viewportLayout(SEXP vp); + +SEXP viewportParent(SEXP vp); + +SEXP viewportTransform(SEXP vp); + +SEXP viewportLayoutWidths(SEXP vp); + +SEXP viewportLayoutHeights(SEXP vp); + +SEXP viewportWidthCM(SEXP vp); + +SEXP viewportHeightCM(SEXP vp); + +SEXP viewportRotation(SEXP vp); + +SEXP viewportParent(SEXP vp); + +SEXP viewportChildren(SEXP vp); + +SEXP viewportDevWidthCM(SEXP vp); + +SEXP viewportDevHeightCM(SEXP vp); + +void fillViewportContextFromViewport(SEXP vp, LViewportContext *vpc); + +void copyViewportContext(LViewportContext vpc1, LViewportContext *vpc2); + +void gcontextFromViewport(SEXP vp, const pGEcontext gc, pGEDevDesc dd); + +void calcViewportTransform(SEXP vp, SEXP parent, Rboolean incremental, + pGEDevDesc dd); + +void initVP(pGEDevDesc dd); + +/* From layout.c */ +Rboolean checkPosRowPosCol(SEXP viewport, SEXP parent); + +void calcViewportLayout(SEXP viewport, + double parentWidthCM, + double parentHeightCM, + LViewportContext parentContext, + const pGEcontext parentgc, + pGEDevDesc dd); + +void calcViewportLocationFromLayout(SEXP layoutPosRow, + SEXP layoutPosCol, + SEXP parent, + LViewportLocation *vpl); + +/* From state.c */ +void initDL(pGEDevDesc dd); + +SEXP gridStateElement(pGEDevDesc dd, int elementIndex); + +void setGridStateElement(pGEDevDesc dd, int elementIndex, SEXP value); + +SEXP gridCallback(GEevent task, pGEDevDesc dd, SEXP data); + +/* From grid.c */ +SEXP doSetViewport(SEXP vp, + Rboolean topLevelVP, + Rboolean pushing, + pGEDevDesc dd); + +void getDeviceSize(pGEDevDesc dd, double *devWidthCM, double *devHeightCM); + +/* This is, confusingly, a wrapper for GEcurrentDevice */ +pGEDevDesc getDevice(); + +void getViewportTransform(SEXP currentvp, + pGEDevDesc dd, + double *vpWidthCM, double *vpHeightCM, + LTransform transform, double *rotationAngle); + +SEXP L_circleBounds(SEXP x, SEXP y, SEXP r, SEXP theta); +SEXP L_locnBounds(SEXP x, SEXP y, SEXP theta); +SEXP L_rectBounds(SEXP x, SEXP y, SEXP w, SEXP h, SEXP hjust, SEXP vjust, + SEXP theta); +SEXP L_textBounds(SEXP label, SEXP x, SEXP y, + SEXP hjust, SEXP vjust, SEXP rot, SEXP theta); +SEXP L_xsplineBounds(SEXP x, SEXP y, SEXP s, SEXP o, SEXP a, SEXP rep, + SEXP index, SEXP theta); +SEXP L_xsplinePoints(SEXP x, SEXP y, SEXP s, SEXP o, SEXP a, SEXP rep, + SEXP index, SEXP theta); + +/* From unit.c */ +SEXP validUnits(SEXP units); + +/* From gpar.c */ +SEXP L_getGPar(void); +SEXP L_setGPar(SEXP gpars); + diff --git a/com.oracle.truffle.r.native/library/grid/src/just.c b/com.oracle.truffle.r.native/library/grid/src/just.c new file mode 100644 index 0000000000..c005c35956 --- /dev/null +++ b/com.oracle.truffle.r.native/library/grid/src/just.c @@ -0,0 +1,128 @@ +/* + * R : A Computer Language for Statistical Data Analysis + * Copyright (C) 2001-3 Paul Murrell + * 2003 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/ + */ + +#include "grid.h" + +/* Modify a location for the correct justification */ + +/* These tranformations assume that x and width are in the same units */ +/* FIXME: I don't think we check anywhere that a horizontal justification + * is not L_BOTTOM or L_TOP (i.e., meaningless). Ditto for checking + * vertical justification. + */ +double justifyX(double x, double width, double hjust) { + return x - width*hjust; + /* + * From when hjust and vjust were enums + * + double result = 0; + switch (hjust) { + case L_LEFT: + result = x; + break; + case L_RIGHT: + result = x - width; + break; + case L_CENTRE: + case L_CENTER: + result = x - width/2; + break; + } + return result; + */ +} + +double justifyY(double y, double height, double vjust) { + return y - height*vjust; + /* + * From when hjust and vjust were enums + * + double result = 0; + switch (vjust) { + case L_BOTTOM: + result = y; + break; + case L_TOP: + result = y - height; + break; + case L_CENTRE: + case L_CENTER: + result = y - height/2; + break; + } + return result; + */ +} + +/* Convert enum justification into 0..1 justification */ +double convertJust(int just) { + double result = 0; + switch (just) { + case L_BOTTOM: + case L_LEFT: + result = 0; + break; + case L_CENTRE: + case L_CENTER: + result = .5; + break; + case L_TOP: + case L_RIGHT: + result = 1; + break; + } + return result; +} + +/* Return the amount of justification required + */ +void justification(double width, double height, double hjust, double vjust, + double *hadj, double *vadj) +{ + *hadj = -width*hjust; + *vadj = -height*vjust; + /* + * From when hjust and vjust were enums + switch (hjust) { + case L_LEFT: + *hadj = 0; + break; + case L_RIGHT: + *hadj = -width; + break; + case L_CENTRE: + case L_CENTER: + *hadj = -width/2; + break; + } + switch (vjust) { + case L_BOTTOM: + *vadj = 0; + break; + case L_TOP: + *vadj = -height; + break; + case L_CENTRE: + case L_CENTER: + *vadj = -height/2; + break; + } + */ +} diff --git a/com.oracle.truffle.r.native/library/grid/src/layout.c b/com.oracle.truffle.r.native/library/grid/src/layout.c new file mode 100644 index 0000000000..f338862e7b --- /dev/null +++ b/com.oracle.truffle.r.native/library/grid/src/layout.c @@ -0,0 +1,648 @@ +/* + * R : A Computer Language for Statistical Data Analysis + * Copyright (C) 2001-3 Paul Murrell + * 2003-2013 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/ + */ + +#include "grid.h" + +/* This stuff always returns an LViewportLocation in "npc" units + */ + +int layoutNRow(SEXP l) { + return INTEGER(VECTOR_ELT(l, LAYOUT_NROW))[0]; +} + +int layoutNCol(SEXP l) { + return INTEGER(VECTOR_ELT(l, LAYOUT_NCOL))[0]; +} + +SEXP layoutWidths(SEXP l) { + return VECTOR_ELT(l, LAYOUT_WIDTHS); +} + +SEXP layoutHeights(SEXP l) { + return VECTOR_ELT(l, LAYOUT_HEIGHTS); +} + +int layoutRespect(SEXP l) { + return INTEGER(VECTOR_ELT(l, LAYOUT_VRESPECT))[0]; +} + +int* layoutRespectMat(SEXP l) { + return INTEGER(VECTOR_ELT(l, LAYOUT_MRESPECT)); +} + +double layoutHJust(SEXP l) { + return REAL(VECTOR_ELT(l, LAYOUT_VJUST))[0]; +} + +double layoutVJust(SEXP l) { + return REAL(VECTOR_ELT(l, LAYOUT_VJUST))[1]; +} + +Rboolean relativeUnit(SEXP unit, int index, + pGEDevDesc dd) { + return pureNullUnit(unit, index, dd); +} + +void findRelWidths(SEXP layout, int *relativeWidths, + pGEDevDesc dd) +{ + int i; + SEXP widths = layoutWidths(layout); + for (i=0; i<layoutNCol(layout); i++) + relativeWidths[i] = relativeUnit(widths, i, dd); +} + +void findRelHeights(SEXP layout, int *relativeHeights, + pGEDevDesc dd) +{ + int i; + SEXP heights = layoutHeights(layout); + for (i=0; i<layoutNRow(layout); i++) + relativeHeights[i] = relativeUnit(heights, i, dd); +} + +void allocateKnownWidths(SEXP layout, + int *relativeWidths, + double parentWidthCM, double parentHeightCM, + LViewportContext parentContext, + const pGEcontext parentgc, + pGEDevDesc dd, + double *npcWidths, double *widthLeftCM) +{ + int i; + SEXP widths = layoutWidths(layout); + for (i=0; i<layoutNCol(layout); i++) + if (!relativeWidths[i]) { + npcWidths[i] = transformWidth(widths, i, parentContext, + parentgc, + parentWidthCM, parentHeightCM, + 0, 0, dd)*2.54; + *widthLeftCM -= npcWidths[i]; + } +} + +void allocateKnownHeights(SEXP layout, + int *relativeHeights, + double parentWidthCM, double parentHeightCM, + LViewportContext parentContext, + const pGEcontext parentgc, + pGEDevDesc dd, + double *npcHeights, double *heightLeftCM) +{ + int i; + SEXP heights = layoutHeights(layout); + for (i=0; i<layoutNRow(layout); i++) + if (!relativeHeights[i]) { + npcHeights[i] = transformHeight(heights, i, parentContext, + parentgc, + parentWidthCM, parentHeightCM, + 0, 0, dd)*2.54; + *heightLeftCM -= npcHeights[i]; + } +} + +int colRespected(int col, SEXP layout) { + int i; + int result = 0; + int respect = layoutRespect(layout); + int *respectMat = layoutRespectMat(layout); + if (respect == 1) + result = 1; + else + for (i=0; i<layoutNRow(layout); i++) + if (respectMat[col*layoutNRow(layout) + i] != 0) + result = 1; + return result; +} + +int rowRespected(int row, SEXP layout) { + int i; + int result = 0; + int respect = layoutRespect(layout); + int *respectMat = layoutRespectMat(layout); + if (respect == 1) + result = 1; + else + for (i=0; i<layoutNCol(layout); i++) + if (respectMat[i*layoutNRow(layout) + row] != 0) + result = 1; + return result; +} + +/* + * These sum up ALL relative widths and heights (unit = "null") + * Some effort is made to find all truly null units + * (e.g., including a grobwidth unit where the grob's width is null) + */ +double totalWidth(SEXP layout, int *relativeWidths, + LViewportContext parentContext, + const pGEcontext parentgc, + pGEDevDesc dd) +{ + int i; + SEXP widths = layoutWidths(layout); + double totalWidth = 0; + for (i=0; i<layoutNCol(layout); i++) + if (relativeWidths[i]) + totalWidth += transformWidth(widths, i, parentContext, + parentgc, + /* + * NOTE: 0, 0, here is ok + * because we are only + * obtaining "null" units + */ + 0, 0, 1, 0, dd); + return totalWidth; +} + +double totalHeight(SEXP layout, int *relativeHeights, + LViewportContext parentContext, + const pGEcontext parentgc, + pGEDevDesc dd) +{ + int i; + SEXP heights = layoutHeights(layout); + double totalHeight = 0; + for (i=0; i<layoutNRow(layout); i++) + if (relativeHeights[i]) + totalHeight += transformHeight(heights, i, parentContext, + parentgc, + /* + * NOTE: 0, 0, here is ok + * because we are only + * obtaining "null" units + */ + 0, 0, 1, 0, dd); + return totalHeight; +} + +void allocateRespected(SEXP layout, + int *relativeWidths, int *relativeHeights, + double *reducedWidthCM, double *reducedHeightCM, + LViewportContext parentContext, + const pGEcontext parentgc, + pGEDevDesc dd, + double *npcWidths, double *npcHeights) +{ + int i; + SEXP widths = layoutWidths(layout); + SEXP heights = layoutHeights(layout); + int respect = layoutRespect(layout); + double sumWidth = totalWidth(layout, relativeWidths, parentContext, + parentgc, dd); + double sumHeight = totalHeight(layout, relativeHeights, parentContext, + parentgc, dd); + double denom, mult; + double tempWidthCM = *reducedWidthCM; + double tempHeightCM = *reducedHeightCM; + if (respect > 0) { + /* Determine whether aspect ratio of available space is + * bigger or smaller than aspect ratio of layout + */ + // NB: widths could be zero + // if ((tempHeightCM / tempWidthCM) > (sumHeight / sumWidth)) { + if ( tempHeightCM * sumWidth > sumHeight * tempWidthCM) { + denom = sumWidth; + mult = tempWidthCM; + } + else { + denom = sumHeight; + mult = tempHeightCM; + } + /* Allocate respected widths + */ + for (i=0; i<layoutNCol(layout); i++) + if (relativeWidths[i]) + if (colRespected(i, layout)) { + /* + * Special case of respect, but sumHeight = 0. + * Action is to allocate widths as if unrespected. + * Ok to test == 0 because will only be 0 if + * all relative heights are actually exactly 0. + */ + if (sumHeight == 0) { + denom = sumWidth; + mult = tempWidthCM; + } + /* Build a unit SEXP with a single value and no data + */ + npcWidths[i] = pureNullUnitValue(widths, i) / + denom*mult; + *reducedWidthCM -= npcWidths[i]; + } + /* Allocate respected heights + */ + for (i=0; i<layoutNRow(layout); i++) + if (relativeHeights[i]) + if (rowRespected(i, layout)) { + /* + * Special case of respect, but sumWidth = 0. + * Action is to allocate widths as if unrespected. + * Ok to test == 0 because will only be 0 if + * all relative heights are actually exactly 0. + */ + if (sumWidth == 0) { + denom = sumHeight; + mult = tempHeightCM; + } + npcHeights[i] = pureNullUnitValue(heights, i) / + denom*mult; + *reducedHeightCM -= npcHeights[i]; + } + } +} + +void setRespectedZero(SEXP layout, + int *relativeWidths, int *relativeHeights, + double *npcWidths, double *npcHeights) +{ + int i; + for (i=0; i<layoutNCol(layout); i++) + if (relativeWidths[i]) + if (colRespected(i, layout)) + npcWidths[i] = 0; + for (i=0; i<layoutNRow(layout); i++) + if (relativeHeights[i]) + if (rowRespected(i, layout)) + npcHeights[i] = 0; +} + +/* These sum up unrespected relative widths and heights (unit = "null") + */ +double totalUnrespectedWidth(SEXP layout, int *relativeWidths, + LViewportContext parentContext, + const pGEcontext parentgc, + pGEDevDesc dd) +{ + int i; + SEXP widths = layoutWidths(layout); + double totalWidth = 0; + for (i=0; i<layoutNCol(layout); i++) + if (relativeWidths[i]) + if (!colRespected(i, layout)) + totalWidth += transformWidth(widths, i, parentContext, + parentgc, + /* + * NOTE: 0, 0, here is ok + * because we are only + * obtaining "null" units + */ + 0, 0, 1, 0, dd); + return totalWidth; +} + +double totalUnrespectedHeight(SEXP layout, int *relativeHeights, + LViewportContext parentContext, + const pGEcontext parentgc, + pGEDevDesc dd) +{ + int i; + SEXP heights = layoutHeights(layout); + double totalHeight = 0; + for (i=0; i<layoutNRow(layout); i++) + if (relativeHeights[i]) + if (!rowRespected(i, layout)) + totalHeight += transformHeight(heights, i, parentContext, + parentgc, + /* + * NOTE: 0, 0, here is ok + * because we are only + * obtaining "null" units + */ + 0, 0, 1, 0, dd); + return totalHeight; +} + + +void setRemainingWidthZero(SEXP layout, + int *relativeWidths, + double *npcWidths) +{ + int i; + for (i=0; i<layoutNCol(layout); i++) + if (relativeWidths[i]) + if (!colRespected(i, layout)) + npcWidths[i] = 0; +} + +void allocateRemainingWidth(SEXP layout, int *relativeWidths, + double remainingWidthCM, + LViewportContext parentContext, + const pGEcontext parentgc, + pGEDevDesc dd, + double *npcWidths) +{ + int i; + SEXP widths = layoutWidths(layout); + double sumWidth; + sumWidth = totalUnrespectedWidth(layout, relativeWidths, + parentContext, parentgc, dd); + if (sumWidth > 0) { + for (i=0; i<layoutNCol(layout); i++) + if (relativeWidths[i]) + if (!colRespected(i, layout)) + npcWidths[i] = remainingWidthCM* + transformWidth(widths, i, parentContext, parentgc, + /* + * NOTE: 0, 0, here is ok + * because we are only + * obtaining "null" units + */ + 0, 0, 1, 0, dd)/ + sumWidth; + } else { + /* + * If ALL relative widths are zero then they all get + * allocated zero width + */ + setRemainingWidthZero(layout, relativeWidths, npcWidths); + } +} + +void setRemainingHeightZero(SEXP layout, + int *relativeHeights, + double *npcHeights) +{ + int i; + for (i=0; i<layoutNRow(layout); i++) + if (relativeHeights[i]) + if (!rowRespected(i, layout)) + npcHeights[i] = 0; +} + +void allocateRemainingHeight(SEXP layout, int *relativeHeights, + double remainingHeightCM, + LViewportContext parentContext, + const pGEcontext parentgc, + pGEDevDesc dd, + double *npcHeights) +{ + int i; + SEXP heights = layoutHeights(layout); + double sumHeight; + sumHeight = totalUnrespectedHeight(layout, relativeHeights, + parentContext, parentgc, dd); + if (sumHeight > 0) { + for (i=0; i<layoutNRow(layout); i++) + if (relativeHeights[i]) + if (!rowRespected(i, layout)) + npcHeights[i] = remainingHeightCM* + transformHeight(heights, i, parentContext, parentgc, + /* + * NOTE: 0, 0, here is ok + * because we are only + * obtaining "null" units + */ + 0, 0, 1, 0, dd)/ + sumHeight; + } else { + /* + * If ALL relative heights are zero then they all get + * allocated zero height + */ + setRemainingHeightZero(layout, relativeHeights, npcHeights); + } +} + +static double sumDims(double dims[], int from, int to) +{ + int i; + double s = 0; + for (i = from; i < to + 1; i++) + s = s + dims[i]; + return s; +} + +static void subRegion(SEXP layout, + int minrow, int maxrow, int mincol, int maxcol, + double widths[], double heights[], + double parentWidthCM, double parentHeightCM, + double *left, double *bottom, + double *width, double *height) +{ + double hjust = layoutHJust(layout); + double vjust = layoutVJust(layout); + double totalWidth = sumDims(widths, 0, layoutNCol(layout) - 1); + double totalHeight = sumDims(heights, 0, layoutNRow(layout) - 1); + *width = sumDims(widths, mincol, maxcol); + *height = sumDims(heights, minrow, maxrow); + /* widths and heights are in CM */ + *left = parentWidthCM*hjust - totalWidth*hjust + + sumDims(widths, 0, mincol - 1); + *bottom = parentHeightCM*vjust + (1 - vjust)*totalHeight - + sumDims(heights, 0, maxrow); + /* + * From when hjust and vjust were enums + * + switch (layoutHJust(layout)) { + case L_LEFT: + *left = sumDims(widths, 0, mincol - 1); + break; + case L_RIGHT: + *left = 1 - sumDims(widths, mincol, layoutNCol(layout) - 1); + break; + case L_CENTRE: + case L_CENTER: + *left = (0.5 - totalWidth/2) + sumDims(widths, 0, mincol - 1); + break; + } + switch (layoutVJust(layout)) { + case L_BOTTOM: + *bottom = totalHeight - sumDims(heights, 0, maxrow); + break; + case L_TOP: + *bottom = 1 - sumDims(heights, 0, maxrow); + break; + case L_CENTRE: + case L_CENTER: + *bottom = (0.5 - totalHeight/2) + totalHeight + - sumDims(heights, 0, maxrow); + } + */ +} + +void calcViewportLayout(SEXP viewport, + double parentWidthCM, + double parentHeightCM, + LViewportContext parentContext, + const pGEcontext parentgc, + pGEDevDesc dd) +{ + int i; + SEXP currentWidths, currentHeights; + SEXP layout = viewportLayout(viewport); + double *npcWidths = (double *) R_alloc(layoutNCol(layout), sizeof(double)); + double *npcHeights = (double *) R_alloc(layoutNRow(layout), + sizeof(double)); + int *relativeWidths = (int *) R_alloc(layoutNCol(layout), sizeof(int)); + int *relativeHeights = (int *) R_alloc(layoutNRow(layout), sizeof(int)); + double reducedWidthCM = parentWidthCM; + double reducedHeightCM = parentHeightCM; + /* Figure out which rows and cols have relative heights and widths + */ + findRelWidths(layout, relativeWidths, dd); + findRelHeights(layout, relativeHeights, dd); + /* For any width or height which has a unit other than "null" + * we can immediately figure out its physical size. + * We do this and return the widthCM and heightCM + * remaining after these widths and heights have been allocated + */ + allocateKnownWidths(layout, relativeWidths, + parentWidthCM, parentHeightCM, + parentContext, parentgc, + dd, npcWidths, + &reducedWidthCM); + allocateKnownHeights(layout, relativeHeights, + parentWidthCM, parentHeightCM, + parentContext, parentgc, + dd, npcHeights, + &reducedHeightCM); + + /* Now allocate respected widths and heights and return + * widthCM and heightCM remaining + */ + if (reducedWidthCM > 0 || + reducedHeightCM > 0) { + allocateRespected(layout, relativeWidths, relativeHeights, + &reducedWidthCM, &reducedHeightCM, + parentContext, parentgc, dd, + npcWidths, npcHeights); + } else { + /* + * IF EITHER we started with ZERO widthCM and heightCM + * OR we've used up all the widthCM and heightCM + * THEN all respected widths/heights get ZERO + */ + setRespectedZero(layout, relativeWidths, relativeHeights, + npcWidths, npcHeights); + } + /* Now allocate relative widths and heights (unit = "null") + * in the remaining space + */ + if (reducedWidthCM > 0) { + allocateRemainingWidth(layout, relativeWidths, + reducedWidthCM, + parentContext, parentgc, dd, npcWidths); + } else { + /* + * IF EITHER we started with ZERO width + * OR we've used up all the width + * THEN any relative widths get ZERO + */ + setRemainingWidthZero(layout, relativeWidths, npcWidths); + } + if (reducedHeightCM > 0) { + allocateRemainingHeight(layout, relativeHeights, + reducedHeightCM, + parentContext, parentgc, dd, npcHeights); + } else { + /* + * IF EITHER we started with ZERO height + * OR we've used up all the height + * THEN any relative heights get ZERO + */ + setRemainingHeightZero(layout, relativeHeights, npcHeights); + } + /* Record the widths and heights in the viewport + */ + PROTECT(currentWidths = allocVector(REALSXP, layoutNCol(layout))); + PROTECT(currentHeights = allocVector(REALSXP, layoutNRow(layout))); + for (i=0; i<layoutNCol(layout); i++) { + /* Layout widths are stored in CM + */ + REAL(currentWidths)[i] = npcWidths[i]; + } + for (i=0; i<layoutNRow(layout); i++) { + /* Layout heights are stored in CM + */ + REAL(currentHeights)[i] = npcHeights[i]; + } + SET_VECTOR_ELT(viewport, PVP_WIDTHS, currentWidths); + SET_VECTOR_ELT(viewport, PVP_HEIGHTS, currentHeights); + UNPROTECT(2); +} + +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; +} + +void calcViewportLocationFromLayout(SEXP layoutPosRow, + SEXP layoutPosCol, + SEXP parent, + LViewportLocation *vpl) +{ + int minrow, maxrow, mincol, maxcol; + double x, y, width, height; + SEXP vpx, vpy, vpwidth, vpheight; + SEXP layout = viewportLayout(parent); + /* It is possible for ONE of layoutPosRow and layoutPosCol to + * be NULL; this is interpreted as "occupy all rows/cols" + * NOTE: The " - 1" is there because R is 1-based and C is zero-based + */ + if (isNull(layoutPosRow)) { + minrow = 0; + maxrow = layoutNRow(layout) - 1; + } else { + minrow = INTEGER(layoutPosRow)[0] - 1; + maxrow = INTEGER(layoutPosRow)[1] - 1; + } + if (isNull(layoutPosCol)) { + mincol = 0; + maxcol = layoutNCol(layout) - 1; + } else { + mincol = INTEGER(layoutPosCol)[0] - 1; + maxcol = INTEGER(layoutPosCol)[1] - 1; + } + /* Put the relevant values into vpl */ + subRegion(viewportLayout(parent), minrow, maxrow, mincol, maxcol, + REAL(viewportLayoutWidths(parent)), + REAL(viewportLayoutHeights(parent)), + REAL(viewportWidthCM(parent))[0], + REAL(viewportHeightCM(parent))[0], + &x, &y, &width, &height); + /* Layout widths and heights are stored in CM + */ + PROTECT(vpx = unit(x, L_CM)); + vpl->x = vpx; + PROTECT(vpy = unit(y, L_CM)); + vpl->y = vpy; + PROTECT(vpwidth = unit(width, L_CM)); + vpl->width = vpwidth; + PROTECT(vpheight = unit(height, L_CM)); + vpl->height = vpheight; + vpl->hjust = 0; + vpl->vjust = 0; + /* Question: Is there any chance that these newly-allocated + * unit SEXPs will get corrupted after this unprotect ?? + */ + UNPROTECT(4); +} + diff --git a/com.oracle.truffle.r.native/library/grid/src/matrix.c b/com.oracle.truffle.r.native/library/grid/src/matrix.c new file mode 100644 index 0000000000..edae7088d0 --- /dev/null +++ b/com.oracle.truffle.r.native/library/grid/src/matrix.c @@ -0,0 +1,153 @@ +/* + * R : A Computer Language for Statistical Data Analysis + * Copyright (C) 2001-3 Paul Murrell + * 2003 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/ + */ + +#include "grid.h" + +/* Code for matrices, matrix multiplication, etc for performing + * 2D affine transformations: translations, scaling, and rotations. + */ + +double locationX(LLocation l) { + return l[0]; +} + +double locationY(LLocation l) { + return l[1]; +} + +void copyTransform(LTransform t1, LTransform t2) +{ + int i, j; + for (i=0; i<3; i++) + for (j=0; j<3; j++) + t2[i][j] = t1[i][j]; +} + +void invTransform(LTransform t, LTransform invt) +{ + double det = t[0][0]*(t[2][2]*t[1][1] - t[2][1]*t[1][2]) - + t[1][0]*(t[2][2]*t[0][1] - t[2][1]*t[0][2]) + + t[2][0]*(t[1][2]*t[0][1] - t[1][1]*t[0][2]); + if (det == 0) + error(_("singular transformation matrix")); + invt[0][0] = 1/det*(t[2][2]*t[1][1] - t[2][1]*t[1][2]); + invt[0][1] = -1/det*(t[2][2]*t[0][1] - t[2][1]*t[0][2]); + invt[0][2] = 1/det*(t[1][2]*t[0][1] - t[1][1]*t[0][2]); + invt[1][0] = -1/det*(t[2][2]*t[1][0] - t[2][0]*t[1][2]); + invt[1][1] = 1/det*(t[2][2]*t[0][0] - t[2][0]*t[0][2]); + invt[1][2] = -1/det*(t[1][2]*t[0][0] - t[1][0]*t[0][2]); + invt[2][0] = 1/det*(t[2][1]*t[1][0] - t[2][0]*t[1][1]); + invt[2][1] = -1/det*(t[2][1]*t[0][0] - t[2][0]*t[0][1]); + invt[2][2] = 1/det*(t[1][1]*t[0][0] - t[1][0]*t[0][1]); +} + +void identity(LTransform m) +{ + int i, j; + for (i=0; i<3; i++) + for (j=0; j<3; j++) + if (i == j) + m[i][j] = 1; + else + m[i][j] = 0; +} + +void translation(double tx, double ty, LTransform m) +{ + identity(m); + m[2][0] = tx; + m[2][1] = ty; +} + +void scaling(double sx, double sy, LTransform m) +{ + identity(m); + m[0][0] = sx; + m[1][1] = sy; +} + +void rotation(double theta, LTransform m) +{ + double thetarad = theta/180*M_PI; + double costheta = cos(thetarad); + double sintheta = sin(thetarad); + identity(m); + m[0][0] = costheta; + m[0][1] = sintheta; + m[1][0] = -sintheta; + m[1][1] = costheta; +} + +void multiply(LTransform m1, LTransform m2, LTransform m) +{ + m[0][0] = m1[0][0]*m2[0][0] + m1[0][1]*m2[1][0] + m1[0][2]*m2[2][0]; + m[0][1] = m1[0][0]*m2[0][1] + m1[0][1]*m2[1][1] + m1[0][2]*m2[2][1]; + m[0][2] = m1[0][0]*m2[0][2] + m1[0][1]*m2[1][2] + m1[0][2]*m2[2][2]; + m[1][0] = m1[1][0]*m2[0][0] + m1[1][1]*m2[1][0] + m1[1][2]*m2[2][0]; + m[1][1] = m1[1][0]*m2[0][1] + m1[1][1]*m2[1][1] + m1[1][2]*m2[2][1]; + m[1][2] = m1[1][0]*m2[0][2] + m1[1][1]*m2[1][2] + m1[1][2]*m2[2][2]; + m[2][0] = m1[2][0]*m2[0][0] + m1[2][1]*m2[1][0] + m1[2][2]*m2[2][0]; + m[2][1] = m1[2][0]*m2[0][1] + m1[2][1]*m2[1][1] + m1[2][2]*m2[2][1]; + m[2][2] = m1[2][0]*m2[0][2] + m1[2][1]*m2[1][2] + m1[2][2]*m2[2][2]; +} + +void location(double x, double y, LLocation v) +{ + v[0] = x; + v[1] = y; + v[2] = 1; +} + +void trans(LLocation vin, LTransform m, LLocation vout) +{ + vout[0] = vin[0]*m[0][0] + vin[1]*m[1][0] + vin[2]*m[2][0]; + vout[1] = vin[0]*m[0][1] + vin[1]*m[1][1] + vin[2]*m[2][1]; + vout[2] = vin[0]*m[0][2] + vin[1]*m[1][2] + vin[2]*m[2][2]; +} + +/* Testing code + * Need to undocument main() below and add #include <math.h> at top of file + * Correct answers are "2.67 2.00 1.00" for m4=identity + * and "0.00 2.00 1.00" for m4=rotation + */ + +/* + main() + { + LLocation v1, v2; + LTransform m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11; + location(9, 10, v1); + translation(-5, -6, m1); + scaling(1/7.0, 1/8.0, m2); + scaling(7, 8, m3); + identity(m4); + rotation(3.141592 / 2, m4); + translation(4, 4, m5); + scaling(1/3.0, 1/4.0, m6); + multiply(m1, m2, m7); + multiply(m7, m3, m8); + multiply(m8, m4, m9); + multiply(m9, m5, m10); + multiply(m10, m6, m11); + transform(v1, m11, v2); + printf("%1.2f %1.2f %1.2f\n", v2[0], v2[1], v2[2]); + } +*/ + diff --git a/com.oracle.truffle.r.native/library/grid/src/register.c b/com.oracle.truffle.r.native/library/grid/src/register.c new file mode 100644 index 0000000000..43c2075a34 --- /dev/null +++ b/com.oracle.truffle.r.native/library/grid/src/register.c @@ -0,0 +1,100 @@ +/* + * R : A Computer Language for Statistical Data Analysis + * Copyright (C) 2001-3 Paul Murrell + * 2003-12 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/ + */ + +/* Code to register grid functions with R + */ +#include <R_ext/Rdynload.h> +#include "grid.h" + +static const R_CallMethodDef callMethods[] = { + {"L_initGrid", (DL_FUNC) &L_initGrid, 1}, + {"L_killGrid", (DL_FUNC) &L_killGrid, 0}, + {"L_gridDirty", (DL_FUNC) &L_gridDirty, 0}, + {"L_currentViewport", (DL_FUNC) &L_currentViewport, 0}, + {"L_setviewport", (DL_FUNC) &L_setviewport, 2}, + {"L_downviewport", (DL_FUNC) &L_downviewport, 2}, + {"L_downvppath", (DL_FUNC) &L_downvppath, 3}, + {"L_unsetviewport", (DL_FUNC) &L_unsetviewport, 1}, + {"L_upviewport", (DL_FUNC) &L_upviewport, 1}, + {"L_getDisplayList", (DL_FUNC) &L_getDisplayList, 0}, + {"L_setDisplayList", (DL_FUNC) &L_setDisplayList, 1}, + {"L_getDLelt", (DL_FUNC) &L_getDLelt, 1}, + {"L_setDLelt", (DL_FUNC) &L_setDLelt, 1}, + {"L_getDLindex", (DL_FUNC) &L_getDLindex, 0}, + {"L_setDLindex", (DL_FUNC) &L_setDLindex, 1}, + {"L_getDLon", (DL_FUNC) &L_getDLon, 0}, + {"L_setDLon", (DL_FUNC) &L_setDLon, 1}, + {"L_getEngineDLon", (DL_FUNC) &L_getEngineDLon, 0}, + {"L_setEngineDLon", (DL_FUNC) &L_setEngineDLon, 1}, + {"L_getCurrentGrob", (DL_FUNC) &L_getCurrentGrob, 0}, + {"L_setCurrentGrob", (DL_FUNC) &L_setCurrentGrob, 1}, + {"L_getEngineRecording", (DL_FUNC) &L_getEngineRecording, 0}, + {"L_setEngineRecording", (DL_FUNC) &L_setEngineRecording, 1}, + {"L_currentGPar", (DL_FUNC) &L_currentGPar, 0}, + {"L_newpagerecording", (DL_FUNC) &L_newpagerecording, 0}, + {"L_newpage", (DL_FUNC) &L_newpage, 0}, + {"L_initGPar", (DL_FUNC) &L_initGPar, 0}, + {"L_initViewportStack", (DL_FUNC) &L_initViewportStack, 0}, + {"L_initDisplayList", (DL_FUNC) &L_initDisplayList, 0}, + {"L_moveTo", (DL_FUNC) &L_moveTo, 2}, + {"L_lineTo", (DL_FUNC) &L_lineTo, 3}, + {"L_lines", (DL_FUNC) &L_lines, 4}, + {"L_segments", (DL_FUNC) &L_segments, 5}, + {"L_arrows", (DL_FUNC) &L_arrows, 12}, + {"L_path", (DL_FUNC) &L_path, 4}, + {"L_polygon", (DL_FUNC) &L_polygon, 3}, + {"L_xspline", (DL_FUNC) &L_xspline, 7}, + {"L_circle", (DL_FUNC) &L_circle, 3}, + {"L_rect", (DL_FUNC) &L_rect, 6}, + {"L_raster", (DL_FUNC) &L_raster, 8}, + {"L_cap", (DL_FUNC) &L_cap, 0}, + {"L_text", (DL_FUNC) &L_text, 7}, + {"L_points", (DL_FUNC) &L_points, 4}, + {"L_clip", (DL_FUNC) &L_clip, 6}, + {"L_pretty", (DL_FUNC) &L_pretty, 1}, + {"L_locator", (DL_FUNC) &L_locator, 0}, + {"L_convert", (DL_FUNC) &L_convert, 4}, + {"L_layoutRegion", (DL_FUNC) &L_layoutRegion, 2}, + {"validUnits", (DL_FUNC) &validUnits, 1}, + {"L_getGPar", (DL_FUNC) &L_getGPar, 0}, + {"L_setGPar", (DL_FUNC) &L_setGPar, 1}, + {"L_circleBounds", (DL_FUNC) &L_circleBounds, 4}, + {"L_locnBounds", (DL_FUNC) &L_locnBounds, 3}, + {"L_rectBounds", (DL_FUNC) &L_rectBounds, 7}, + {"L_textBounds", (DL_FUNC) &L_textBounds, 7}, + {"L_xsplineBounds", (DL_FUNC) &L_xsplineBounds, 8}, + {"L_xsplinePoints", (DL_FUNC) &L_xsplinePoints, 8}, + {"L_stringMetric", (DL_FUNC) &L_stringMetric, 1}, + { NULL, NULL, 0 } +}; + + +void +#ifdef HAVE_VISIBILITY_ATTRIBUTE +__attribute__ ((visibility ("default"))) +#endif +R_init_grid(DllInfo *dll) +{ + /* No .C, .Fortran, or .External routines => NULL + */ + R_registerRoutines(dll, NULL, callMethods, NULL, NULL); + R_useDynamicSymbols(dll, FALSE); + R_forceSymbols(dll, FALSE); +} diff --git a/com.oracle.truffle.r.native/library/grid/src/state.c b/com.oracle.truffle.r.native/library/grid/src/state.c new file mode 100644 index 0000000000..a6e68d670e --- /dev/null +++ b/com.oracle.truffle.r.native/library/grid/src/state.c @@ -0,0 +1,302 @@ +/* + * R : A Computer Language for Statistical Data Analysis + * Copyright (C) 2001-3 Paul Murrell + * 2003-5 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/ + */ + +#include "grid.h" + +int gridRegisterIndex; + +/* The gridSystemState (per device) consists of + * GSS_DEVSIZE 0 = current size of device + * GSS_CURRLOC 1 = current location of grid "pen" + * GSS_DL 2 = grid display list + * GSS_DLINDEX 3 = display list index + * GSS_DLON 4 = is the display list on? + * GSS_GPAR 5 = gpar settings + * GSS_GPSAVED 6 = previous gpar settings + * GSS_VP 7 = viewport + * GSS_GLOBALINDEX 8 = index of this system state in the global list of states + * GSS_GRIDDEVICE 9 = does this device contain grid output? + * GSS_PREVLOC 10 = previous location of grid "pen" + * GSS_ENGINEDLON 11 = are we using the graphics engine's display list? + * GSS_CURRGROB 12 = current grob being drawn (for determining + * the list of grobs to search when evaluating a grobwidth/height + * unit via gPath) + * GSS_ENGINERECORDING 13 = are we already inside a .Call.graphics call? + * Used by grid.Call.graphics to avoid unnecessary recording on + * engine display list + * [GSS_ASK 14 = should we prompt the user before starting a new page? + * Replaced by per-device setting as from R 2.7.0.] + * GSS_SCALE 15 = a scale or "zoom" factor for all output + * (to support "fit to window" resizing on windows device) + * + * NOTE: if you add to this list you MUST change the size of the vector + * allocated in createGridSystemState() below. +*/ + +SEXP createGridSystemState() +{ + return allocVector(VECSXP, 16); +} + +void initDL(pGEDevDesc dd) +{ + SEXP dl, dlindex; + SEXP vp = gridStateElement(dd, GSS_VP); + SEXP gsd = (SEXP) dd->gesd[gridRegisterIndex]->systemSpecific; + /* The top-level viewport goes at the start of the display list + */ + PROTECT(dl = allocVector(VECSXP, 100)); + SET_VECTOR_ELT(dl, 0, vp); + SET_VECTOR_ELT(gsd, GSS_DL, dl); + PROTECT(dlindex = allocVector(INTSXP, 1)); + INTEGER(dlindex)[0] = 1; + SET_VECTOR_ELT(gsd, GSS_DLINDEX, dlindex); + UNPROTECT(2); +} + +/* + * This is used to init some bits of the system state + * Called when a grahpics engine redraw is about to occur + * NOTE that it does not init all of the state, in particular, + * the display list is not initialised here (see initDL), + * nor is the ROOT viewport (see initVP), + * nor is the current gpar (see initGP) + */ +void initOtherState(pGEDevDesc dd) +{ + SEXP currloc, prevloc, recording; + SEXP state = (SEXP) dd->gesd[gridRegisterIndex]->systemSpecific; + currloc = VECTOR_ELT(state, GSS_CURRLOC); + REAL(currloc)[0] = NA_REAL; + REAL(currloc)[1] = NA_REAL; + prevloc = VECTOR_ELT(state, GSS_PREVLOC); + REAL(prevloc)[0] = NA_REAL; + REAL(prevloc)[1] = NA_REAL; + SET_VECTOR_ELT(state, GSS_CURRGROB, R_NilValue); + recording = VECTOR_ELT(state, GSS_ENGINERECORDING); + LOGICAL(recording)[0] = FALSE; + SET_VECTOR_ELT(state, GSS_ENGINERECORDING, recording); +} + +void fillGridSystemState(SEXP state, pGEDevDesc dd) +{ + SEXP devsize, currloc, prevloc; + + PROTECT(state); + devsize = allocVector(REALSXP, 2); + REAL(devsize)[0] = 0; + REAL(devsize)[1] = 0; + SET_VECTOR_ELT(state, GSS_DEVSIZE, devsize); + /* "current location" + * Initial setting relies on the fact that all values sent to devices + * are in INCHES; so (0, 0) is the bottom-left corner of the device. + */ + currloc = allocVector(REALSXP, 2); + REAL(currloc)[0] = NA_REAL; + REAL(currloc)[1] = NA_REAL; + SET_VECTOR_ELT(state, GSS_CURRLOC, currloc); + prevloc = allocVector(REALSXP, 2); + REAL(prevloc)[0] = NA_REAL; + REAL(prevloc)[1] = NA_REAL; + SET_VECTOR_ELT(state, GSS_PREVLOC, prevloc); + SET_VECTOR_ELT(state, GSS_DLON, ScalarLogical(TRUE)); + SET_VECTOR_ELT(state, GSS_ENGINEDLON, ScalarLogical(TRUE)); + SET_VECTOR_ELT(state, GSS_CURRGROB, R_NilValue); + SET_VECTOR_ELT(state, GSS_ENGINERECORDING, ScalarLogical(FALSE)); + initGPar(dd); + SET_VECTOR_ELT(state, GSS_GPSAVED, R_NilValue); + /* Do NOT initialise top-level viewport or grid display list for + * this device until there is some grid output + */ + SET_VECTOR_ELT(state, GSS_GLOBALINDEX, R_NilValue); + /* Note that no grid output has occurred on the device yet. + */ + SET_VECTOR_ELT(state, GSS_GRIDDEVICE, ScalarLogical(FALSE)); +#if 0 + SET_VECTOR_ELT(state, GSS_ASK, ScalarLogical(dd->ask)); +#endif + SET_VECTOR_ELT(state, GSS_SCALE, ScalarReal(1.0)); + UNPROTECT(1); +} + +SEXP gridStateElement(pGEDevDesc dd, int elementIndex) +{ + return VECTOR_ELT((SEXP) dd->gesd[gridRegisterIndex]->systemSpecific, + elementIndex); +} + +void setGridStateElement(pGEDevDesc dd, int elementIndex, SEXP value) +{ + SET_VECTOR_ELT((SEXP) dd->gesd[gridRegisterIndex]->systemSpecific, + elementIndex, value); +} + +static void deglobaliseState(SEXP state) +{ + int index = INTEGER(VECTOR_ELT(state, GSS_GLOBALINDEX))[0]; + SET_VECTOR_ELT(findVar(install(".GRID.STATE"), R_gridEvalEnv), + index, R_NilValue); +} + +static int findStateSlot() +{ + int i; + int result = -1; + SEXP globalstate = findVar(install(".GRID.STATE"), R_gridEvalEnv); + for (i = 0; i < length(globalstate); i++) + if (VECTOR_ELT(globalstate, i) == R_NilValue) { + result = i; + break; + } + if (result < 0) + error(_("unable to store 'grid' state. Too many devices open?")); + return result; +} + +static void globaliseState(SEXP state) +{ + int index = findStateSlot(); + SEXP globalstate, indexsxp; + PROTECT(globalstate = findVar(install(".GRID.STATE"), R_gridEvalEnv)); + /* Record the index for deglobalisation + */ + PROTECT(indexsxp = allocVector(INTSXP, 1)); + INTEGER(indexsxp)[0] = index; + SET_VECTOR_ELT(state, GSS_GLOBALINDEX, indexsxp); + SET_VECTOR_ELT(globalstate, index, state); + UNPROTECT(2); +} + +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; +} + diff --git a/com.oracle.truffle.r.native/library/grid/src/unit.c b/com.oracle.truffle.r.native/library/grid/src/unit.c new file mode 100644 index 0000000000..2d9459ed7b --- /dev/null +++ b/com.oracle.truffle.r.native/library/grid/src/unit.c @@ -0,0 +1,1923 @@ +/* + * R : A Computer Language for Statistical Data Analysis + * Copyright (C) 2001-3 Paul Murrell + * 2003-2013 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/ + */ + +#include "grid.h" +#include <math.h> +#include <float.h> +#include <string.h> + +int isUnitArithmetic(SEXP ua) { + return inherits(ua, "unit.arithmetic"); +} + +int isUnitList(SEXP ul) { + return inherits(ul, "unit.list"); +} + +/* Function to build a single-value unit SEXP internally. + * Cannot build units requiring data as yet. + */ +SEXP unit(double value, int unit) +{ + SEXP u, units, classname; + PROTECT(u = ScalarReal(value)); + PROTECT(units = ScalarInteger(unit)); + /* NOTE that we do not set the "unit" attribute */ + setAttrib(u, install("valid.unit"), units); + setAttrib(u, install("data"), R_NilValue); + PROTECT(classname = mkString("unit")); + classgets(u, classname); + UNPROTECT(3); + return u; +} + +/* Accessor functions for unit objects + */ + +/* + * This is an attempt to extract a single numeric value from + * a unit. This is ONLY designed for use on "simple" units + * (i.e., NOT unitLists or unitArithmetics) + */ +double unitValue(SEXP unit, int index) { + /* Recycle values if necessary (used in unit arithmetic) + */ + int n = LENGTH(unit); + return numeric(unit, index % n); +} + +int unitUnit(SEXP unit, int index) { + SEXP units = getAttrib(unit, install("valid.unit")); + /* Recycle units if necessary + */ + int n = LENGTH(units); + return INTEGER(units)[index % n]; +} + +SEXP unitData(SEXP unit, int index) { + SEXP result; + SEXP data = getAttrib(unit, install("data")); + if (isNull(data)) + result = R_NilValue; + else if(TYPEOF(data) == VECSXP) { + /* Recycle data if necessary + */ + int n = LENGTH(data); + result = VECTOR_ELT(data, index % n); + } else { + warning("unit attribute 'data' is of incorrect type"); + return R_NilValue; + } + return result; +} + +/* Accessor functions for unit arithmetic object + */ +const char* fName(SEXP ua) { + return CHAR(STRING_ELT(getListElement(ua, "fname"), 0)); +} + +SEXP arg1(SEXP ua) { + return getListElement(ua, "arg1"); +} + +SEXP arg2(SEXP ua) { + return getListElement(ua, "arg2"); +} + +int fNameMatch(SEXP ua, char *aString) { + return !strcmp(fName(ua), aString); +} + +int addOp(SEXP ua) { + return fNameMatch(ua, "+"); +} + +int minusOp(SEXP ua) { + return fNameMatch(ua, "-"); +} + +int timesOp(SEXP ua) { + return fNameMatch(ua, "*"); +} + +int fOp(SEXP ua) { + return addOp(ua) || minusOp(ua) || timesOp(ua); +} + +int minFunc(SEXP ua) { + return fNameMatch(ua, "min"); +} + +int maxFunc(SEXP ua) { + return fNameMatch(ua, "max"); +} + +int sumFunc(SEXP ua) { + return fNameMatch(ua, "sum"); +} + +/* Functions in lattice.c should use this to determine the length + * of a unit/unitArithmetic object rather than just LENGTH. + */ +int unitLength(SEXP u) +{ + int result = 0; + if (isUnitList(u)) + result = LENGTH(u); + else if (isUnitArithmetic(u)) + if (fOp(u)) { + if (timesOp(u)) { + /* + * arg1 is always the numeric vector + */ + int n1 = LENGTH(arg1(u)); + int n2 = unitLength(arg2(u)); + result = (n1 > n2) ? n1 : n2; + } else { /* must be "+" or "-" */ + int n1 = unitLength(arg1(u)); + int n2 = unitLength(arg2(u)); + result = (n1 > n2) ? n1 : n2; + } + } else /* must be "min" or "max" or "sum" */ + result = 1; /* unitLength(arg1(u)); */ + else /* Must be a unit object */ + result = LENGTH(u); + return result; +} + + +/************************** + * Code for handling "null" units + ************************** + */ + +/* Global mode indicators: + * The value returned for a "null" unit depends on ... + * (i) whether layout is calling for evaluation of a "pure null" unit + * (in which case, the value of the "null" unit is returned) + * (ii) the sort of arithmetic that is being performed + * (in which case, an "identity" value is returned) + */ + +/* + * Evaluate a "null" _value_ dependent on the evaluation context + */ +static double evaluateNullUnit(double value, double thisCM, + int nullLayoutMode, int nullArithmeticMode) { + double result = value; + if (!nullLayoutMode) + switch (nullArithmeticMode) { + case L_plain: + case L_adding: + case L_subtracting: + case L_summing: + result = 0; + break; + case L_multiplying: + result = 0; + break; + case L_maximising: + result = 0; + break; + case L_minimising: + result = thisCM; + break; + } + return result; +} + +/* + * Evaluate a "null" _unit_ + * This is used by layout code to get a single "null" _value_ + * from a pureNullUnit (which may be a unitList or a unitArithmetic) + * + * This must ONLY be called on a unit which has passed the + * pureNullUnit test below. + */ +double pureNullUnitValue(SEXP unit, int index) +{ + double result = 0; + if (isUnitArithmetic(unit)) { + int i; + if (addOp(unit)) { + result = pureNullUnitValue(arg1(unit), index) + + pureNullUnitValue(arg2(unit), index); + } + else if (minusOp(unit)) { + result = pureNullUnitValue(arg1(unit), index) - + pureNullUnitValue(arg2(unit), index); + } + else if (timesOp(unit)) { + result = REAL(arg1(unit))[index] * + pureNullUnitValue(arg2(unit), index); + } + else if (minFunc(unit)) { + int n = unitLength(arg1(unit)); + double temp = DBL_MAX; + result = pureNullUnitValue(arg1(unit), 0); + for (i=1; i<n; i++) { + temp = pureNullUnitValue(arg1(unit), i); + if (temp < result) + result = temp; + } + } + else if (maxFunc(unit)) { + int n = unitLength(arg1(unit)); + double temp = DBL_MIN; + result = pureNullUnitValue(arg1(unit), 0); + for (i=1; i<n; i++) { + temp = pureNullUnitValue(arg1(unit), i); + if (temp > result) + result = temp; + } + } + else if (sumFunc(unit)) { + int n = unitLength(arg1(unit)); + result = 0.0; + for (i=0; i<n; i++) { + result += pureNullUnitValue(arg1(unit), i); + } + } + else + error(_("unimplemented unit function")); + } else if (isUnitList(unit)) { + /* + * Recycle if necessary; it is up to the calling code + * to limit indices to unit length if desired + */ + int n = unitLength(unit); + result = pureNullUnitValue(VECTOR_ELT(unit, index % n), 0); + } else + result = unitValue(unit, index); + return result; +} + +int pureNullUnitArithmetic(SEXP unit, int index, pGEDevDesc dd); + +int pureNullUnit(SEXP unit, int index, pGEDevDesc dd) { + int result; + if (isUnitArithmetic(unit)) + result = pureNullUnitArithmetic(unit, index, dd); + else if (isUnitList(unit)) { + /* + * Recycle if necessary; it is up to the calling code + * to limit indices to unit length if desired + */ + int n = unitLength(unit); + result = pureNullUnit(VECTOR_ELT(unit, index % n), 0, dd); + } else { /* Just a plain unit */ + /* Special case: if "grobwidth" or "grobheight" unit + * and width/height(grob) is pure null + */ + if (unitUnit(unit, index) == L_GROBWIDTH) { + SEXP grob, updatedgrob, width; + SEXP widthPreFn, widthFn, widthPostFn, findGrobFn; + SEXP R_fcall0, R_fcall1, R_fcall2, R_fcall3; + SEXP savedgpar, savedgrob; + /* + * The data could be a gPath to a grob + * In this case, need to find the grob first, and in order + * to do that correctly, need to call pre/postDraw code + */ + PROTECT(grob = unitData(unit, index)); + PROTECT(savedgpar = gridStateElement(dd, GSS_GPAR)); + PROTECT(savedgrob = gridStateElement(dd, GSS_CURRGROB)); + PROTECT(widthPreFn = findFun(install("preDraw"), + R_gridEvalEnv)); + PROTECT(widthFn = findFun(install("width"), R_gridEvalEnv)); + PROTECT(widthPostFn = findFun(install("postDraw"), + R_gridEvalEnv)); + if (inherits(grob, "gPath")) { + if (isNull(savedgrob)) { + PROTECT(findGrobFn = findFun(install("findGrobinDL"), + R_gridEvalEnv)); + PROTECT(R_fcall0 = lang2(findGrobFn, + getListElement(grob, "name"))); + grob = eval(R_fcall0, R_gridEvalEnv); + } else { + PROTECT(findGrobFn =findFun(install("findGrobinChildren"), + R_gridEvalEnv)); + PROTECT(R_fcall0 = lang3(findGrobFn, + getListElement(grob, "name"), + getListElement(savedgrob, + "children"))); + grob = eval(R_fcall0, R_gridEvalEnv); + } + UNPROTECT(2); + } + PROTECT(R_fcall1 = lang2(widthPreFn, grob)); + PROTECT(updatedgrob = eval(R_fcall1, R_gridEvalEnv)); + PROTECT(R_fcall2 = lang2(widthFn, updatedgrob)); + PROTECT(width = eval(R_fcall2, R_gridEvalEnv)); + result = pureNullUnit(width, 0, dd); + PROTECT(R_fcall3 = lang2(widthPostFn, updatedgrob)); + eval(R_fcall3, R_gridEvalEnv); + setGridStateElement(dd, GSS_GPAR, savedgpar); + setGridStateElement(dd, GSS_CURRGROB, savedgrob); + UNPROTECT(11); + } else if (unitUnit(unit, index) == L_GROBHEIGHT) { + SEXP grob, updatedgrob, height; + SEXP heightPreFn, heightFn, heightPostFn, findGrobFn; + SEXP R_fcall0, R_fcall1, R_fcall2, R_fcall3; + SEXP savedgpar, savedgrob; + /* + * The data could be a gPath to a grob + * In this case, need to find the grob first, and in order + * to do that correctly, need to call pre/postDraw code + */ + PROTECT(grob = unitData(unit, index)); + PROTECT(savedgpar = gridStateElement(dd, GSS_GPAR)); + PROTECT(savedgrob = gridStateElement(dd, GSS_CURRGROB)); + PROTECT(heightPreFn = findFun(install("preDraw"), + R_gridEvalEnv)); + PROTECT(heightFn = findFun(install("height"), R_gridEvalEnv)); + PROTECT(heightPostFn = findFun(install("postDraw"), + R_gridEvalEnv)); + if (inherits(grob, "gPath")) { + if (isNull(savedgrob)) { + PROTECT(findGrobFn = findFun(install("findGrobinDL"), + R_gridEvalEnv)); + PROTECT(R_fcall0 = lang2(findGrobFn, + getListElement(grob, "name"))); + grob = eval(R_fcall0, R_gridEvalEnv); + } else { + PROTECT(findGrobFn =findFun(install("findGrobinChildren"), + R_gridEvalEnv)); + PROTECT(R_fcall0 = lang3(findGrobFn, + getListElement(grob, "name"), + getListElement(savedgrob, + "children"))); + grob = eval(R_fcall0, R_gridEvalEnv); + } + UNPROTECT(2); + } + PROTECT(R_fcall1 = lang2(heightPreFn, grob)); + PROTECT(updatedgrob = eval(R_fcall1, R_gridEvalEnv)); + PROTECT(R_fcall2 = lang2(heightFn, updatedgrob)); + PROTECT(height = eval(R_fcall2, R_gridEvalEnv)); + result = pureNullUnit(height, 0, dd); + PROTECT(R_fcall3 = lang2(heightPostFn, updatedgrob)); + eval(R_fcall3, R_gridEvalEnv); + setGridStateElement(dd, GSS_GPAR, savedgpar); + setGridStateElement(dd, GSS_CURRGROB, savedgrob); + UNPROTECT(11); + } else + result = unitUnit(unit, index) == L_NULL; + } + return result; +} + +int pureNullUnitArithmetic(SEXP unit, int index, pGEDevDesc dd) { + /* + * Initialised to shut up compiler + */ + int result = 0; + if (addOp(unit) || minusOp(unit)) { + result = pureNullUnit(arg1(unit), index, dd) && + pureNullUnit(arg2(unit), index, dd); + } + else if (timesOp(unit)) { + result = pureNullUnit(arg2(unit), index, dd); + } + else if (minFunc(unit) || maxFunc(unit) || sumFunc(unit)) { + int n = unitLength(arg1(unit)); + int i = 0; + result = 1; + while (result && i<n) { + result = result && pureNullUnit(arg1(unit), i, dd); + i += 1; + } + } + else + error(_("unimplemented unit function")); + return result; +} + +/************************** + * Code for handling "grobwidth" units + ************************** + */ + +/* NOTE: this code calls back to R code to perform + * set.gpar operations, which will impact on grid state variables + * BUT that's ok(ish) because we save and restore the relevant state + * variables in here so that the overall effect is NULL. + * + * FIXME: OTOH, the calls back to R Code may also perform + * viewport operations. Again, we restore state as much as possible, + * but this can "pollute" the viewport tree in some cases. + */ + +double evaluateGrobUnit(double value, SEXP grob, + double vpwidthCM, double vpheightCM, + int nullLMode, int nullAMode, + /* + * Evaluation type + * 0 = x, 1 = y, 2 = width, 3 = height + */ + int evalType, + pGEDevDesc dd) +{ + double vpWidthCM, vpHeightCM; + double rotationAngle; + LViewportContext vpc; + R_GE_gcontext gc; + LTransform transform, savedTransform; + SEXP currentvp, currentgp; + SEXP preFn, postFn, findGrobFn; + SEXP evalFnx = R_NilValue, evalFny = R_NilValue; + SEXP R_fcall0, R_fcall1, R_fcall2x, R_fcall2y, R_fcall3; + SEXP savedgpar, savedgrob, updatedgrob; + SEXP unitx = R_NilValue, unity = R_NilValue; + double result = 0.0; + Rboolean protectedGrob = FALSE; + /* + * We are just doing calculations, not drawing, so + * we don't want anything recorded on the graphics engine DL + * + * FIXME: This should probably be done via a GraphicsEngine.h + * function call rather than directly playing with dd->recordGraphics + */ + Rboolean record = dd->recordGraphics; + dd->recordGraphics = FALSE; + /* + * Save the current viewport transform + * (use to convert location relative to current viewport) + */ + currentvp = gridStateElement(dd, GSS_VP); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + savedTransform, &rotationAngle); + /* + * Save the current gpar state and restore it at the end + */ + PROTECT(savedgpar = gridStateElement(dd, GSS_GPAR)); + /* + * Save the current grob and restore it at the end + */ + PROTECT(savedgrob = gridStateElement(dd, GSS_CURRGROB)); + /* + * Set up for calling R functions + */ + PROTECT(preFn = findFun(install("preDraw"), R_gridEvalEnv)); + switch(evalType) { + case 0: + case 1: + PROTECT(evalFnx = findFun(install("xDetails"), R_gridEvalEnv)); + PROTECT(evalFny = findFun(install("yDetails"), R_gridEvalEnv)); + break; + case 2: + PROTECT(evalFnx = findFun(install("width"), R_gridEvalEnv)); + break; + case 3: + PROTECT(evalFny = findFun(install("height"), R_gridEvalEnv)); + break; + case 4: + PROTECT(evalFny = findFun(install("ascentDetails"), R_gridEvalEnv)); + break; + case 5: + PROTECT(evalFny = findFun(install("descentDetails"), R_gridEvalEnv)); + break; + } + PROTECT(postFn = findFun(install("postDraw"), R_gridEvalEnv)); + /* + * If grob is actually a gPath, use it to find an actual grob + */ + if (inherits(grob, "gPath")) { + /* + * If the current grob is NULL then we are at the top level + * and we search the display list, otherwise we search the + * children of the current grob + * + * NOTE: assume here that only gPath of depth == 1 are valid + */ + if (isNull(savedgrob)) { + PROTECT(findGrobFn = findFun(install("findGrobinDL"), + R_gridEvalEnv)); + PROTECT(R_fcall0 = lang2(findGrobFn, + getListElement(grob, "name"))); + PROTECT(grob = eval(R_fcall0, R_gridEvalEnv)); + } else { + PROTECT(findGrobFn = findFun(install("findGrobinChildren"), + R_gridEvalEnv)); + PROTECT(R_fcall0 = lang3(findGrobFn, + getListElement(grob, "name"), + getListElement(savedgrob, "children"))); + PROTECT(grob = eval(R_fcall0, R_gridEvalEnv)); + } + /* + * Flag to make sure we UNPROTECT these at the end + */ + protectedGrob = TRUE; + } + /* Call preDraw(grob) + */ + PROTECT(R_fcall1 = lang2(preFn, grob)); + PROTECT(updatedgrob = eval(R_fcall1, R_gridEvalEnv)); + /* + * The call to preDraw may have pushed viewports and/or + * enforced gpar settings, SO we need to re-establish the + * current viewport and gpar settings before evaluating the + * width unit. + * + * NOTE: we are really relying on the grid state to be coherent + * when we do stuff like this (i.e., not to have changed since + * we started evaluating the unit [other than the changes we may + * have deliberately made above by calling preDraw]). In other + * words we are relying on no other drawing occurring at the + * same time as we are doing this evaluation. In other other + * words, we are relying on there being only ONE process + * (i.e., NOT multi-threaded). + */ + currentvp = gridStateElement(dd, GSS_VP); + currentgp = gridStateElement(dd, GSS_GPAR); + getViewportTransform(currentvp, dd, + &vpWidthCM, &vpHeightCM, + transform, &rotationAngle); + fillViewportContextFromViewport(currentvp, &vpc); + /* Call whatever(grob) + * to get the unit representing the x/y/width/height + */ + switch (evalType) { + case 0: + case 1: + /* + * When evaluating grobX/grobY, the value of the unit + * is an angle that gets passed to xDetails/yDetails + */ + { + SEXP val; + PROTECT(val = ScalarReal(value)); + PROTECT(R_fcall2x = lang3(evalFnx, updatedgrob, val)); + PROTECT(unitx = eval(R_fcall2x, R_gridEvalEnv)); + PROTECT(R_fcall2y = lang3(evalFny, updatedgrob, val)); + PROTECT(unity = eval(R_fcall2y, R_gridEvalEnv)); + } + break; + case 2: + PROTECT(R_fcall2x = lang2(evalFnx, updatedgrob)); + PROTECT(unitx = eval(R_fcall2x, R_gridEvalEnv)); + break; + case 3: + case 4: + case 5: + PROTECT(R_fcall2y = lang2(evalFny, updatedgrob)); + PROTECT(unity = eval(R_fcall2y, R_gridEvalEnv)); + break; + } + /* + * Transform the unit + * NOTE: We transform into INCHES so can produce final answer in terms + * of NPC for original context + */ + /* Special case for "null" units + */ + gcontextFromgpar(currentgp, 0, &gc, dd); + switch(evalType) { + case 0: + case 1: + if (evalType && pureNullUnit(unity, 0, dd)) { + result = evaluateNullUnit(pureNullUnitValue(unity, 0), + vpWidthCM, + nullLMode, nullAMode); + } else if (pureNullUnit(unitx, 0, dd)) { + result = evaluateNullUnit(pureNullUnitValue(unitx, 0), + vpWidthCM, + nullLMode, nullAMode); + } else { + /* + * Transform to device (to allow for viewports in grob) + * then adjust relative to current viewport. + */ + double xx, yy; + LLocation lin, lout; + LTransform invt; + invTransform(savedTransform, invt); + transformLocn(unitx, unity, 0, + vpc, &gc, + vpWidthCM, vpHeightCM, dd, + transform, &xx, &yy); + location(xx, yy, lin); + trans(lin, invt, lout); + xx = locationX(lout); + yy = locationY(lout); + if (evalType) + result = yy; + else + result = xx; + } + break; + case 2: + if (pureNullUnit(unitx, 0, dd)) { + result = evaluateNullUnit(pureNullUnitValue(unitx, 0), + vpWidthCM, + nullLMode, nullAMode); + } else { + result = transformWidthtoINCHES(unitx, 0, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + } + break; + case 3: + case 4: + case 5: + if (pureNullUnit(unity, 0, dd)) { + result = evaluateNullUnit(pureNullUnitValue(unity, 0), + vpWidthCM, + nullLMode, nullAMode); + } else { + result = transformHeighttoINCHES(unity, 0, vpc, &gc, + vpWidthCM, vpHeightCM, + dd); + } + break; + } + /* Call postDraw(grob) + */ + PROTECT(R_fcall3 = lang2(postFn, updatedgrob)); + eval(R_fcall3, R_gridEvalEnv); + /* + * Restore the saved gpar state and grob + */ + setGridStateElement(dd, GSS_GPAR, savedgpar); + setGridStateElement(dd, GSS_CURRGROB, savedgrob); + if (protectedGrob) + UNPROTECT(3); + switch(evalType) { + case 0: + case 1: + UNPROTECT(14); + break; + case 2: + case 3: + case 4: + case 5: + UNPROTECT(10); + } + /* Return the transformed width + */ + /* + * If there is an error or user-interrupt in the above + * evaluation, dd->recordGraphics is set to TRUE + * on all graphics devices (see GEonExit(); called in errors.c) + */ + dd->recordGraphics = record; + return result; +} + +double evaluateGrobXUnit(double value, SEXP grob, + double vpheightCM, double vpwidthCM, + int nullLMode, int nullAMode, + pGEDevDesc dd) +{ + return evaluateGrobUnit(value, grob, vpheightCM, vpwidthCM, + nullLMode, nullAMode, 0, dd); +} + +double evaluateGrobYUnit(double value, SEXP grob, + double vpheightCM, double vpwidthCM, + int nullLMode, int nullAMode, + pGEDevDesc dd) +{ + return evaluateGrobUnit(value, grob, vpheightCM, vpwidthCM, + nullLMode, nullAMode, 1, dd); +} + +double evaluateGrobWidthUnit(SEXP grob, + double vpheightCM, double vpwidthCM, + int nullLMode, int nullAMode, + pGEDevDesc dd) +{ + return evaluateGrobUnit(1, grob, vpheightCM, vpwidthCM, + nullLMode, nullAMode, 2, dd); +} + +double evaluateGrobHeightUnit(SEXP grob, + double vpheightCM, double vpwidthCM, + int nullLMode, int nullAMode, + pGEDevDesc dd) +{ + return evaluateGrobUnit(1, grob, vpheightCM, vpwidthCM, + nullLMode, nullAMode, 3, dd); +} + +double evaluateGrobAscentUnit(SEXP grob, + double vpheightCM, double vpwidthCM, + int nullLMode, int nullAMode, + pGEDevDesc dd) +{ + return evaluateGrobUnit(1, grob, vpheightCM, vpwidthCM, + nullLMode, nullAMode, 4, dd); +} + +double evaluateGrobDescentUnit(SEXP grob, + double vpheightCM, double vpwidthCM, + int nullLMode, int nullAMode, + pGEDevDesc dd) +{ + return evaluateGrobUnit(1, grob, vpheightCM, vpwidthCM, + nullLMode, nullAMode, 5, dd); +} + +/************************** + * TRANSFORMATIONS + ************************** + */ + +/* Map a value from arbitrary units to INCHES */ + +/* + * NULL units are a special case + * If L_nullLayoutMode = 1 then the value returned is a NULL unit value + * Otherwise it is an INCHES value + */ +double transform(double value, int unit, SEXP data, + double scalemin, double scalemax, + const pGEcontext gc, + double thisCM, double otherCM, + int nullLMode, int nullAMode, pGEDevDesc dd) +{ + double asc, dsc, wid; + double result = value; + switch (unit) { + case L_NPC: + result = (result * thisCM)/2.54; /* 2.54 cm per inch */ + break; + case L_CM: + result = result/2.54; + break; + case L_INCHES: + break; + /* FIXME: The following two assume that the pointsize specified + * by the user is actually the pointsize provided by the + * device. This is NOT a safe assumption + * One possibility would be to do a call to GReset(), just so + * that mapping() gets called, just so that things like + * xNDCPerLine are up-to-date, THEN call GStrHeight("M") + * or somesuch. + */ + case L_CHAR: + case L_MYCHAR: /* FIXME: Remove this when I can */ + result = (result * gc->ps * gc->cex)/72; /* 72 points per inch */ + break; + case L_LINES: + case L_MYLINES: /* FIXME: Remove this when I can */ + result = (result * gc->ps * gc->cex * gc->lineheight)/72; + break; + case L_SNPC: + if (thisCM <= otherCM) + result = (result * thisCM)/2.54; + else + result = (result * otherCM)/2.54; + break; + case L_MM: + result = (result/10)/2.54; + break; + /* Maybe an opportunity for some constants below here (!) + */ + case L_POINTS: + result = result/72.27; + break; + case L_PICAS: + result = (result*12)/72.27; + break; + case L_BIGPOINTS: + result = result/72; + break; + case L_DIDA: + result = result/1157*1238/72.27; + break; + case L_CICERO: + result = result*12/1157*1238/72.27; + break; + case L_SCALEDPOINTS: + result = result/65536/72.27; + break; + case L_STRINGWIDTH: + case L_MYSTRINGWIDTH: /* FIXME: Remove this when I can */ + if (isExpression(data)) + result = result* + fromDeviceWidth(GEExpressionWidth(VECTOR_ELT(data, 0), gc, dd), + GE_INCHES, dd); + else + result = result* + fromDeviceWidth(GEStrWidth(CHAR(STRING_ELT(data, 0)), + getCharCE(STRING_ELT(data, 0)), + gc, dd), + GE_INCHES, dd); + break; + case L_STRINGHEIGHT: + case L_MYSTRINGHEIGHT: /* FIXME: Remove this when I can */ + if (isExpression(data)) + result = result* + fromDeviceHeight(GEExpressionHeight(VECTOR_ELT(data, 0), + gc, dd), + GE_INCHES, dd); + else + /* FIXME: what encoding is this? */ + result = result* + fromDeviceHeight(GEStrHeight(CHAR(STRING_ELT(data, 0)), -1, + gc, dd), + GE_INCHES, dd); + break; + case L_STRINGASCENT: + if (isExpression(data)) + GEExpressionMetric(VECTOR_ELT(data, 0), gc, + &asc, &dsc, &wid, + dd); + else + GEStrMetric(CHAR(STRING_ELT(data, 0)), + getCharCE(STRING_ELT(data, 0)), gc, + &asc, &dsc, &wid, + dd); + result = result*fromDeviceHeight(asc, GE_INCHES, dd); + break; + case L_STRINGDESCENT: + if (isExpression(data)) + GEExpressionMetric(VECTOR_ELT(data, 0), gc, + &asc, &dsc, &wid, + dd); + else + GEStrMetric(CHAR(STRING_ELT(data, 0)), + getCharCE(STRING_ELT(data, 0)), gc, + &asc, &dsc, &wid, + dd); + result = result*fromDeviceHeight(dsc, GE_INCHES, dd); + break; + case L_GROBX: + result = evaluateGrobXUnit(value, data, thisCM, otherCM, + nullLMode, nullAMode, dd); + break; + case L_GROBY: + result = evaluateGrobYUnit(value, data, otherCM, thisCM, + nullLMode, nullAMode, dd); + break; + case L_GROBWIDTH: + result = value*evaluateGrobWidthUnit(data, thisCM, otherCM, + nullLMode, nullAMode, dd); + break; + case L_GROBHEIGHT: + result = value*evaluateGrobHeightUnit(data, otherCM, thisCM, + nullLMode, nullAMode, dd); + break; + case L_GROBASCENT: + result = value*evaluateGrobAscentUnit(data, otherCM, thisCM, + nullLMode, nullAMode, dd); + break; + case L_GROBDESCENT: + result = value*evaluateGrobDescentUnit(data, otherCM, thisCM, + nullLMode, nullAMode, dd); + break; + case L_NULL: + result = evaluateNullUnit(result, thisCM, nullLMode, nullAMode); + break; + default: + error(_("invalid unit or unit not yet implemented")); + } + /* + * For physical units, scale the result by GSS_SCALE (a "zoom" factor) + */ + switch (unit) { + case L_INCHES: + case L_CM: + case L_MM: + case L_POINTS: + case L_PICAS: + case L_BIGPOINTS: + case L_DIDA: + case L_CICERO: + case L_SCALEDPOINTS: + result = result * REAL(gridStateElement(dd, GSS_SCALE))[0]; + break; + default: + /* + * No need to scale relative coordinates (NPC, NATIVE, NULL) + * CHAR and LINES already scaled because of scaling in gcontextFromGPar() + * Ditto STRINGWIDTH/HEIGHT + * GROBWIDTH/HEIGHT recurse into here so scaling already done + */ + break; + } + return result; +} + +/* FIXME: scales are only linear at the moment */ +double transformLocation(double location, int unit, SEXP data, + double scalemin, double scalemax, + const pGEcontext gc, + double thisCM, double otherCM, + int nullLMode, int nullAMode, pGEDevDesc dd) +{ + double result = location; + switch (unit) { + case L_NATIVE: + /* It is invalid to create a viewport with identical limits on scale + * so we are protected from divide-by-zero + */ + result = ((result - scalemin)/(scalemax - scalemin))*thisCM/2.54; + break; + default: + result = transform(location, unit, data, scalemin, scalemax, + gc, thisCM, otherCM, nullLMode, nullAMode, dd); + } + return result; +} + +double transformXArithmetic(SEXP x, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + int nullLMode, pGEDevDesc dd); + +double transformX(SEXP x, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + int nullLMode, int nullAMode, pGEDevDesc dd) +{ + double result; + int unit; + SEXP data; + if (isUnitArithmetic(x)) + result = transformXArithmetic(x, index, vpc, gc, + widthCM, heightCM, nullLMode, dd); + else if (isUnitList(x)) { + int n = unitLength(x); + result = transformX(VECTOR_ELT(x, index % n), 0, vpc, gc, + widthCM, heightCM, nullLMode, nullAMode, dd); + } else { /* Just a plain unit */ + int nullamode; + if (nullAMode == 0) + nullamode = L_plain; + else + nullamode = nullAMode; + result = unitValue(x, index); + unit = unitUnit(x, index); + PROTECT(data = unitData(x, index)); + result = transformLocation(result, unit, data, + vpc.xscalemin, vpc.xscalemax, gc, + widthCM, heightCM, + nullLMode, + nullamode, + dd); + UNPROTECT(1); + } + return result; +} + +double transformYArithmetic(SEXP y, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + int nullLMode, pGEDevDesc dd); + +double transformY(SEXP y, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + int nullLMode, int nullAMode, pGEDevDesc dd) +{ + double result; + int unit; + SEXP data; + if (isUnitArithmetic(y)) + result = transformYArithmetic(y, index, vpc, gc, + widthCM, heightCM, nullLMode, dd); + else if (isUnitList(y)) { + int n = unitLength(y); + result = transformY(VECTOR_ELT(y, index % n), 0, vpc, gc, + widthCM, heightCM, nullLMode, nullAMode, dd); + } else { /* Just a unit object */ + int nullamode; + if (nullAMode == 0) + nullamode = L_plain; + else + nullamode = nullAMode; + result = unitValue(y, index); + unit = unitUnit(y, index); + PROTECT(data = unitData(y, index)); + result = transformLocation(result, unit, data, + vpc.yscalemin, vpc.yscalemax, gc, + heightCM, widthCM, + nullLMode, + nullamode, + dd); + UNPROTECT(1); + } + return result; +} + +double transformDimension(double dim, int unit, SEXP data, + double scalemin, double scalemax, + const pGEcontext gc, + double thisCM, double otherCM, + int nullLMode, int nullAMode, + pGEDevDesc dd) +{ + double result = dim; + switch (unit) { + case L_NATIVE: + /* It is invalid to create a viewport with identical limits on scale + * so we are protected from divide-by-zero + */ + result = ((dim)/(scalemax - scalemin))*thisCM/2.54; + break; + default: + result = transform(dim, unit, data, scalemin, scalemax, gc, + thisCM, otherCM, nullLMode, nullAMode, dd); + } + return result; +} + +double transformWidthArithmetic(SEXP width, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + int nullLMode, pGEDevDesc dd); + +double transformWidth(SEXP width, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + int nullLMode, int nullAMode, pGEDevDesc dd) +{ + double result; + int unit; + SEXP data; + if (isUnitArithmetic(width)) + result = transformWidthArithmetic(width, index, vpc, gc, + widthCM, heightCM, nullLMode, dd); + else if (isUnitList(width)) { + int n = unitLength(width); + result = transformWidth(VECTOR_ELT(width, index % n), 0, vpc, gc, + widthCM, heightCM, nullLMode, nullAMode, dd); + } else { /* Just a unit object */ + int nullamode; + if (nullAMode == 0) + nullamode = L_plain; + else + nullamode = nullAMode; + result = unitValue(width, index); + unit = unitUnit(width, index); + PROTECT(data = unitData(width, index)); + result = transformDimension(result, unit, data, + vpc.xscalemin, vpc.xscalemax, gc, + widthCM, heightCM, + nullLMode, + nullamode, + dd); + UNPROTECT(1); + } + return result; +} + +double transformHeightArithmetic(SEXP height, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + int nullLMode, pGEDevDesc dd); + +double transformHeight(SEXP height, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + int nullLMode, int nullAMode, pGEDevDesc dd) +{ + double result; + int unit; + SEXP data; + if (isUnitArithmetic(height)) + result = transformHeightArithmetic(height, index, vpc, gc, + widthCM, heightCM, nullLMode, dd); + else if (isUnitList(height)) { + int n = unitLength(height); + result = transformHeight(VECTOR_ELT(height, index % n), 0, vpc, gc, + widthCM, heightCM, nullLMode, nullAMode, dd); + } else { /* Just a unit object */ + int nullamode; + if (nullAMode == 0) + nullamode = L_plain; + else + nullamode = nullAMode; + result = unitValue(height, index); + unit = unitUnit(height, index); + PROTECT(data = unitData(height, index)); + result = transformDimension(result, unit, data, + vpc.yscalemin, vpc.yscalemax, gc, + heightCM, widthCM, + nullLMode, + nullamode, + dd); + UNPROTECT(1); + } + return result; +} + +double transformXArithmetic(SEXP x, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + int nullLMode, pGEDevDesc dd) +{ + int i; + double result = 0; + if (addOp(x)) { + result = transformX(arg1(x), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_adding, + dd) + + transformX(arg2(x), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_adding, + dd); + } + else if (minusOp(x)) { + result = transformX(arg1(x), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_subtracting, + dd) - + transformX(arg2(x), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_subtracting, + dd); + } + else if (timesOp(x)) { + result = REAL(arg1(x))[index % LENGTH(arg1(x))] * + transformX(arg2(x), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_multiplying, dd); + } + else if (minFunc(x)) { + int n = unitLength(arg1(x)); + double temp = DBL_MAX; + result = transformX(arg1(x), 0, vpc, gc, + widthCM, heightCM, + nullLMode, L_minimising, + dd); + for (i=1; i<n; i++) { + temp = transformX(arg1(x), i, vpc, gc, + widthCM, heightCM, + nullLMode, L_minimising, + dd); + if (temp < result) + result = temp; + } + } + else if (maxFunc(x)) { + int n = unitLength(arg1(x)); + double temp = DBL_MIN; + result = transformX(arg1(x), 0, vpc, gc, + widthCM, heightCM, + nullLMode, L_maximising, + dd); + for (i=1; i<n; i++) { + temp = transformX(arg1(x), i, vpc, gc, + widthCM, heightCM, + nullLMode, L_maximising, + dd); + if (temp > result) + result = temp; + } + } + else if (sumFunc(x)) { + int n = unitLength(arg1(x)); + result = 0.0; + for (i=0; i<n; i++) { + result += transformX(arg1(x), i, vpc, gc, + widthCM, heightCM, + nullLMode, L_summing, dd); + } + } + else + error(_("unimplemented unit function")); + return result; +} + +double transformYArithmetic(SEXP y, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + int nullLMode, pGEDevDesc dd) +{ + int i; + double result = 0; + if (addOp(y)) { + result = transformY(arg1(y), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_adding, + dd) + + transformY(arg2(y), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_adding, + dd); + } + else if (minusOp(y)) { + result = transformY(arg1(y), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_subtracting, + dd) - + transformY(arg2(y), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_subtracting, + dd); + } + else if (timesOp(y)) { + result = REAL(arg1(y))[index % LENGTH(arg1(y))] * + transformY(arg2(y), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_multiplying, dd); + } + else if (minFunc(y)) { + int n = unitLength(arg1(y)); + double temp = DBL_MAX; + result = transformY(arg1(y), 0, vpc, gc, + widthCM, heightCM, + nullLMode, L_minimising, + dd); + for (i=1; i<n; i++) { + temp = transformY(arg1(y), i, vpc, gc, + widthCM, heightCM, + nullLMode, L_minimising, + dd); + if (temp < result) + result = temp; + } + } + else if (maxFunc(y)) { + int n = unitLength(arg1(y)); + double temp = DBL_MIN; + result = transformY(arg1(y), 0, vpc, gc, + widthCM, heightCM, + nullLMode, L_maximising, + dd); + for (i=1; i<n; i++) { + temp = transformY(arg1(y), i, vpc, gc, + widthCM, heightCM, + nullLMode, L_maximising, + dd); + if (temp > result) + result = temp; + } + } + else if (sumFunc(y)) { + int n = unitLength(arg1(y)); + result = 0.0; + for (i=0; i<n; i++) { + result += transformY(arg1(y), i, vpc, gc, + widthCM, heightCM, + nullLMode, L_summing, dd); + } + } + else + error(_("unimplemented unit function")); + return result; +} + +double transformWidthArithmetic(SEXP width, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + int nullLMode, pGEDevDesc dd) +{ + int i; + double result = 0; + if (addOp(width)) { + result = transformWidth(arg1(width), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_adding, + dd) + + transformWidth(arg2(width), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_adding, + dd); + } + else if (minusOp(width)) { + result = transformWidth(arg1(width), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_subtracting, + dd) - + transformWidth(arg2(width), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_subtracting, + dd); + } + else if (timesOp(width)) { + result = REAL(arg1(width))[index % LENGTH(arg1(width))] * + transformWidth(arg2(width), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_multiplying, dd); + } + else if (minFunc(width)) { + int n = unitLength(arg1(width)); + double temp = DBL_MAX; + result = transformWidth(arg1(width), 0, vpc, gc, + widthCM, heightCM, + nullLMode, L_minimising, + dd); + for (i=1; i<n; i++) { + temp = transformWidth(arg1(width), i, vpc, gc, + widthCM, heightCM, + nullLMode, L_minimising, + dd); + if (temp < result) + result = temp; + } + } + else if (maxFunc(width)) { + int n = unitLength(arg1(width)); + double temp = DBL_MIN; + result = transformWidth(arg1(width), 0, vpc, gc, + widthCM, heightCM, + nullLMode, L_maximising, + dd); + for (i=1; i<n; i++) { + temp = transformWidth(arg1(width), i, vpc, gc, + widthCM, heightCM, + nullLMode, L_maximising, + dd); + if (temp > result) + result = temp; + } + } + else if (sumFunc(width)) { + int n = unitLength(arg1(width)); + result = 0.0; + for (i=0; i<n; i++) { + result += transformWidth(arg1(width), i, vpc, gc, + widthCM, heightCM, + nullLMode, L_summing, dd); + } + } + else + error(_("unimplemented unit function")); + return result; +} + +double transformHeightArithmetic(SEXP height, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + int nullLMode, pGEDevDesc dd) +{ + int i; + double result = 0; + if (addOp(height)) { + result = transformHeight(arg1(height), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_adding, + dd) + + transformHeight(arg2(height), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_adding, + dd); + } + else if (minusOp(height)) { + result = transformHeight(arg1(height), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_subtracting, + dd) - + transformHeight(arg2(height), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_subtracting, + dd); + } + else if (timesOp(height)) { + result = REAL(arg1(height))[index % LENGTH(arg1(height))] * + transformHeight(arg2(height), index, vpc, gc, + widthCM, heightCM, + nullLMode, L_multiplying, dd); + } + else if (minFunc(height)) { + int n = unitLength(arg1(height)); + double temp = DBL_MAX; + result = transformHeight(arg1(height), 0, vpc, gc, + widthCM, heightCM, + nullLMode, L_minimising, + dd); + for (i=1; i<n; i++) { + temp = transformHeight(arg1(height), i, vpc, gc, + widthCM, heightCM, + nullLMode, L_minimising, + dd); + if (temp < result) + result = temp; + } + } + else if (maxFunc(height)) { + int n = unitLength(arg1(height)); + double temp = DBL_MIN; + result = transformHeight(arg1(height), 0, vpc, gc, + widthCM, heightCM, + nullLMode, L_maximising, + dd); + for (i=1; i<n; i++) { + temp = transformHeight(arg1(height), i, vpc, gc, + widthCM, heightCM, + nullLMode, L_maximising, + dd); + if (temp > result) + result = temp; + } + } + else if (sumFunc(height)) { + int n = unitLength(arg1(height)); + result = 0.0; + for (i=0; i<n; i++) { + result += transformHeight(arg1(height), i, vpc, gc, + widthCM, heightCM, + nullLMode, L_summing, dd); + } + } + else + error(_("unimplemented unit function")); + return result; +} + +/* Code for transforming a location in INCHES using a transformation matrix. + * We work in INCHES so that rotations can be incorporated within the + * transformation matrix (i.e., the units are the same in both x- and + * y-directions). + * INCHES rather than CM because the R graphics engine only has INCHES. + */ + +/* The original transform[X | Y | Width | Height] functions + * were written to transform to NPC. Rather than muck with them, + * I am just wrappering them to get the new transformation to INCHES + * In other words, the reason for the apparent inefficiency here + * is historical. + */ + +/* It is even more inefficient-looking now because I ended up mucking + * with transform() to return INCHES (to fix bug if width/heightCM == 0) + * and by then there was too much code that called transformXtoINCHES + * to be bothered changing calls to it + */ + +/* The difference between transform*toINCHES and transformLocn/Dimn + * is that the former are just converting from one coordinate system + * to INCHES; the latter are converting from INCHES relative to + * the parent to INCHES relative to the device. + */ +double transformXtoINCHES(SEXP x, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + pGEDevDesc dd) +{ + return transformX(x, index, vpc, gc, + widthCM, heightCM, 0, 0, dd); +} + +double transformYtoINCHES(SEXP y, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + pGEDevDesc dd) +{ + return transformY(y, index, vpc, gc, + widthCM, heightCM, 0, 0, dd); +} + +void transformLocn(SEXP x, SEXP y, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + pGEDevDesc dd, + LTransform t, + double *xx, double *yy) +{ + LLocation lin, lout; + /* x and y are unit objects (i.e., values in any old coordinate + * system) so the first step is to convert them both to CM + */ + *xx = transformXtoINCHES(x, index, vpc, gc, + widthCM, heightCM, dd); + *yy = transformYtoINCHES(y, index, vpc, gc, + widthCM, heightCM, dd); + location(*xx, *yy, lin); + trans(lin, t, lout); + *xx = locationX(lout); + *yy = locationY(lout); +} + +double transformWidthtoINCHES(SEXP w, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + pGEDevDesc dd) +{ + return transformWidth(w, index, vpc, gc, + widthCM, heightCM, 0, 0, dd); +} + +double transformHeighttoINCHES(SEXP h, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + pGEDevDesc dd) +{ + return transformHeight(h, index, vpc, gc, + widthCM, heightCM, 0, 0, dd); +} + +void transformDimn(SEXP w, SEXP h, int index, + LViewportContext vpc, + const pGEcontext gc, + double widthCM, double heightCM, + pGEDevDesc dd, + double rotationAngle, + double *ww, double *hh) +{ + LLocation din, dout; + LTransform r; + *ww = transformWidthtoINCHES(w, index, vpc, gc, + widthCM, heightCM, dd); + *hh = transformHeighttoINCHES(h, index, vpc, gc, + widthCM, heightCM, dd); + location(*ww, *hh, din); + rotation(rotationAngle, r); + trans(din, r, dout); + *ww = locationX(dout); + *hh = locationY(dout); +} + +/* + * **************************** + * Inverse Transformations + * **************************** + */ + +/* + * Take a value in inches within the viewport and convert to some + * other coordinate system + */ + +double transformFromINCHES(double value, int unit, + const pGEcontext gc, + double thisCM, double otherCM, + pGEDevDesc dd) +{ + /* + * Convert to NPC + */ + double result = value; + switch (unit) { + case L_NPC: + result = result/(thisCM/2.54); + break; + case L_CM: + result = result*2.54; + break; + case L_INCHES: + break; + /* FIXME: The following two assume that the pointsize specified + * by the user is actually the pointsize provided by the + * device. This is NOT a safe assumption + * One possibility would be to do a call to GReset(), just so + * that mapping() gets called, just so that things like + * xNDCPerLine are up-to-date, THEN call GStrHeight("M") + * or somesuch. + */ + case L_CHAR: + result = (result*72)/(gc->ps*gc->cex); + break; + case L_LINES: + result = (result*72)/(gc->ps*gc->cex*gc->lineheight); + break; + case L_MM: + result = result*2.54*10; + break; + /* Maybe an opportunity for some constants below here (!) + */ + case L_POINTS: + result = result*72.27; + break; + case L_PICAS: + result = result/12*72.27; + break; + case L_BIGPOINTS: + result = result*72; + break; + case L_DIDA: + result = result/1238*1157*72.27; + break; + case L_CICERO: + result = result/1238*1157*72.27/12; + break; + case L_SCALEDPOINTS: + result = result*65536*72.27; + break; + /* + * I'm not sure the remaining ones makes any sense. + * For simplicity, these are just forbidden for now. + */ + case L_SNPC: + case L_MYCHAR: + case L_MYLINES: + case L_STRINGWIDTH: + case L_MYSTRINGWIDTH: + case L_STRINGHEIGHT: + case L_MYSTRINGHEIGHT: + case L_GROBX: + case L_GROBY: + case L_GROBWIDTH: + case L_GROBHEIGHT: + case L_NULL: + default: + error(_("invalid unit or unit not yet implemented")); + } + /* + * For physical units, reverse the scale by GSS_SCALE (a "zoom" factor) + */ + switch (unit) { + case L_INCHES: + case L_CM: + case L_MM: + case L_POINTS: + case L_PICAS: + case L_BIGPOINTS: + case L_DIDA: + case L_CICERO: + case L_SCALEDPOINTS: + result = result / REAL(gridStateElement(dd, GSS_SCALE))[0]; + break; + default: + /* + * No need to scale relative coordinates (NPC, NATIVE, NULL) + * All other units forbidden anyway + */ + break; + } + return result; +} + +/* + * This corresponds to transform[X|Y]toINCHES() because + * it works only within the current viewport, BUT + * it is much simpler because it is supplied with a + * double value in INCHES (rather than a unit object in + * an arbitrary unit). + * + * For conceptual symmetry, it should probably return a + * unit object, but it only returns a double value. + * The construction of a unit object with the appropriate + * unit must be performed by the calling function (or higher). + * This is probably easiest done right up in R code. + */ +double transformXYFromINCHES(double location, int unit, + double scalemin, double scalemax, + const pGEcontext gc, + double thisCM, double otherCM, + pGEDevDesc dd) +{ + double result = location; + /* Special case if "thisCM == 0": + * If converting FROM relative unit, result will already be zero + * so leave it there. + * If converting FROM absolute unit that is zero, ditto. + * Otherwise (converting FROM non-zero absolute unit), + * converting to relative unit is an error. + */ + if ((unit == L_NATIVE || unit == L_NPC) && + thisCM < 1e-6) { + if (result != 0) + error(_("Viewport has zero dimension(s)")); + } else { + switch (unit) { + case L_NATIVE: + result = scalemin + (result/(thisCM/2.54))*(scalemax - scalemin); + break; + default: + result = transformFromINCHES(location, unit, gc, + thisCM, otherCM, dd); + } + } + return result; +} + +double transformWidthHeightFromINCHES(double dimension, int unit, + double scalemin, double scalemax, + const pGEcontext gc, + double thisCM, double otherCM, + pGEDevDesc dd) +{ + double result = dimension; + /* Special case if "thisCM == 0": + * If converting FROM relative unit, result will already be zero + * so leave it there. + * If converting FROM absolute unit that is zero, ditto. + * Otherwise (converting FROM non-zero absolute unit), + * converting to relative unit is an error. + */ + if ((unit == L_NATIVE || unit == L_NPC) && + thisCM < 1e-6) { + if (result != 0) + error(_("Viewport has zero dimension(s)")); + } else { + switch (unit) { + case L_NATIVE: + result = (result/(thisCM/2.54))*(scalemax - scalemin); + break; + default: + result = transformFromINCHES(dimension, unit, gc, + thisCM, otherCM, dd); + } + } + return result; +} + +/* + * Special case conversion from relative unit to relative unit, + * only used when relevant widthCM or heightCM is zero, so + * we cannot transform thru INCHES (or we get divide-by-zero) + * + * Protected from divide-by-zero here because viewport with + * identical scale limits is disallowed. + */ +double transformXYtoNPC(double x, int from, double min, double max) +{ + double result = x; + switch (from) { + case L_NPC: + break; + case L_NATIVE: + result = (x - min)/(max - min); + break; + default: + error(_("Unsupported unit conversion")); + } + return(result); +} + +double transformWHtoNPC(double x, int from, double min, double max) +{ + double result = x; + switch (from) { + case L_NPC: + break; + case L_NATIVE: + result = x/(max - min); + break; + default: + error(_("Unsupported unit conversion")); + } + return(result); +} + +double transformXYfromNPC(double x, int to, double min, double max) +{ + double result = x; + switch (to) { + case L_NPC: + break; + case L_NATIVE: + result = min + x*(max - min); + break; + default: + error(_("Unsupported unit conversion")); + } + return(result); +} + +double transformWHfromNPC(double x, int to, double min, double max) +{ + double result = x; + switch (to) { + case L_NPC: + break; + case L_NATIVE: + result = x*(max - min); + break; + default: + error(_("Unsupported unit conversion")); + } + return(result); +} + +/* Attempt to make validating units faster + */ +typedef struct { + char *name; + int code; +} UnitTab; + +/* NOTE this table must match the order in grid.h + */ +static UnitTab UnitTable[] = { + { "npc", 0 }, + { "cm", 1 }, + { "inches", 2 }, + { "lines", 3 }, + { "native", 4 }, + { "null", 5 }, + { "snpc", 6 }, + { "mm", 7 }, + { "points", 8 }, + { "picas", 9 }, + { "bigpts", 10 }, + { "dida", 11 }, + { "cicero", 12 }, + { "scaledpts", 13 }, + { "strwidth", 14 }, + { "strheight", 15 }, + { "strascent", 16 }, + { "strdescent", 17 }, + + { "char", 18 }, + { "grobx", 19 }, + { "groby", 20 }, + { "grobwidth", 21 }, + { "grobheight", 22 }, + { "grobascent", 23 }, + { "grobdescent", 24 }, + + { "mylines", 103 }, + { "mychar", 104 }, + { "mystrwidth", 105 }, + { "mystrheight", 106 }, + + /* + * Some pseudonyms + */ + { "centimetre", 1001 }, + { "centimetres", 1001 }, + { "centimeter", 1001 }, + { "centimeters", 1001 }, + { "in", 1002 }, + { "inch", 1002 }, + { "line", 1003 }, + { "millimetre", 1007 }, + { "millimetres", 1007 }, + { "millimeter", 1007 }, + { "millimeters", 1007 }, + { "point", 1008 }, + { "pt", 1008 }, + + { NULL, -1 } +}; + +int convertUnit(SEXP unit, int index) +{ + int i = 0; + int result = 0; + int found = 0; + while (result >= 0 && !found) { + if (UnitTable[i].name == NULL) + result = -1; + else { + found = !strcmp(CHAR(STRING_ELT(unit, index)), UnitTable[i].name); + if (found) { + result = UnitTable[i].code; + /* resolve pseudonyms */ + if (result > 1000) { + result = result - 1000; + } + } + } + i += 1; + } + if (result < 0) + error(_("Invalid unit")); + return result; +} + +SEXP validUnits(SEXP units) +{ + int i; + int n = LENGTH(units); + SEXP answer = R_NilValue; + if (n > 0) { + if (isString(units)) { + PROTECT(answer = allocVector(INTSXP, n)); + for (i = 0; i<n; i++) + INTEGER(answer)[i] = convertUnit(units, i); + UNPROTECT(1); + } else { + error(_("'units' must be character")); + } + } else { + error(_("'units' must be of length > 0")); + } + return answer; +} + + + diff --git a/com.oracle.truffle.r.native/library/grid/src/util.c b/com.oracle.truffle.r.native/library/grid/src/util.c new file mode 100644 index 0000000000..3780cb11c9 --- /dev/null +++ b/com.oracle.truffle.r.native/library/grid/src/util.c @@ -0,0 +1,288 @@ +/* + * R : A Computer Language for Statistical Data Analysis + * Copyright (C) 2001-3 Paul Murrell + * 2003-8 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/ + */ + +#include "grid.h" +#include <string.h> + +/* Get the list element named str, or return NULL. + * Copied from the Writing R Extensions manual (which copied it from nls) + */ +SEXP getListElement(SEXP list, char *str) +{ + SEXP elmt = R_NilValue; + SEXP names = getAttrib(list, R_NamesSymbol); + int i; + + for (i = 0; i < length(list); i++) + if(strcmp(CHAR(STRING_ELT(names, i)), str) == 0) { + elmt = VECTOR_ELT(list, i); + break; + } + return elmt; +} + +void setListElement(SEXP list, char *str, SEXP value) +{ + SEXP names = getAttrib(list, R_NamesSymbol); + int i; + + for (i = 0; i < length(list); i++) + if(strcmp(CHAR(STRING_ELT(names, i)), str) == 0) { + SET_VECTOR_ELT(list, i, value); + break; + } +} + +/* The lattice R code checks values to make sure that they are numeric + * BUT we do not know whether the values are integer or real + * SO we have to be careful when extracting numeric values. + * This function assumes that x is numeric (obviously). + */ +double numeric(SEXP x, int index) +{ + if (isReal(x)) + return REAL(x)[index]; + else if (isInteger(x)) + return INTEGER(x)[index]; + return NA_REAL; +} + +/*********************** + * Stuff for rectangles + ***********************/ + +/* Fill a rectangle struct + */ +void rect(double x1, double x2, double x3, double x4, + double y1, double y2, double y3, double y4, + LRect *r) +{ + r->x1 = x1; + r->x2 = x2; + r->x3 = x3; + r->x4 = x4; + r->y1 = y1; + r->y2 = y2; + r->y3 = y3; + r->y4 = y4; +} + +void copyRect(LRect r1, LRect *r) +{ + r->x1 = r1.x1; + r->x2 = r1.x2; + r->x3 = r1.x3; + r->x4 = r1.x4; + r->y1 = r1.y1; + r->y2 = r1.y2; + r->y3 = r1.y3; + r->y4 = r1.y4; +} + +/* Do two lines intersect ? + * Algorithm from Paul Bourke + * (http://www.swin.edu.au/astronomy/pbourke/geometry/lineline2d/index.html) + */ +int linesIntersect(double x1, double x2, double x3, double x4, + double y1, double y2, double y3, double y4) +{ + double result = 0; + double denom = (y4 - y3)*(x2 - x1) - (x4 - x3)*(y2 - y1); + double ua = ((x4 - x3)*(y1 - y3) - (y4 - y3)*(x1 - x3)); + /* If the lines are parallel ... + */ + if (denom == 0) { + /* If the lines are coincident ... + */ + if (ua == 0) { + /* If the lines are vertical ... + */ + if (x1 == x2) { + /* Compare y-values + */ + if (!((y1 < y3 && fmax2(y1, y2) < fmin2(y3, y4)) || + (y3 < y1 && fmax2(y3, y4) < fmin2(y1, y2)))) + result = 1; + } else { + /* Compare x-values + */ + if (!((x1 < x3 && fmax2(x1, x2) < fmin2(x3, x4)) || + (x3 < x1 && fmax2(x3, x4) < fmin2(x1, x2)))) + result = 1; + } + } + } + /* ... otherwise, calculate where the lines intersect ... + */ + else { + double ub = ((x2 - x1)*(y1 - y3) - (y2 - y1)*(x1 - x3)); + ua = ua/denom; + ub = ub/denom; + /* Check for overlap + */ + if ((ua > 0 && ua < 1) && (ub > 0 && ub < 1)) + result = 1; + } + return (int) result; +} + +int edgesIntersect(double x1, double x2, double y1, double y2, + LRect r) +{ + int result = 0; + if (linesIntersect(x1, x2, r.x1, r.x2, y1, y2, r.y1, r.y2) || + linesIntersect(x1, x2, r.x2, r.x3, y1, y2, r.y2, r.y3) || + linesIntersect(x1, x2, r.x3, r.x4, y1, y2, r.y3, r.y4) || + linesIntersect(x1, x2, r.x4, r.x1, y1, y2, r.y4, r.y1)) + result = 1; + return result; +} + +/* Do two rects intersect ? + * For each edge in r1, does the edge intersect with any edge in r2 ? + * FIXME: Should add first check for non-intersection of + * bounding boxes of rects (?) + */ +int intersect(LRect r1, LRect r2) +{ + int result = 0; + if (edgesIntersect(r1.x1, r1.x2, r1.y1, r1.y2, r2) || + edgesIntersect(r1.x2, r1.x3, r1.y2, r1.y3, r2) || + edgesIntersect(r1.x3, r1.x4, r1.y3, r1.y4, r2) || + edgesIntersect(r1.x4, r1.x1, r1.y4, r1.y1, r2)) + result = 1; + return result; +} + +/* Calculate the bounding rectangle for a string. + * x and y assumed to be in INCHES. + */ +void textRect(double x, double y, SEXP text, int i, + const pGEcontext gc, + double xadj, double yadj, + double rot, pGEDevDesc dd, LRect *r) +{ + /* NOTE that we must work in inches for the angles to be correct + */ + LLocation bl, br, tr, tl; + LLocation tbl, tbr, ttr, ttl; + LTransform thisLocation, thisRotation, thisJustification; + LTransform tempTransform, transform; + double w, h; + if (isExpression(text)) { + SEXP expr = VECTOR_ELT(text, i % LENGTH(text)); + w = fromDeviceWidth(GEExpressionWidth(expr, gc, dd), + GE_INCHES, dd); + h = fromDeviceHeight(GEExpressionHeight(expr, gc, dd), + GE_INCHES, dd); + } else { + const char* string = CHAR(STRING_ELT(text, i % LENGTH(text))); + w = fromDeviceWidth(GEStrWidth(string, + (gc->fontface == 5) ? CE_SYMBOL : + getCharCE(STRING_ELT(text, i % LENGTH(text))), + gc, dd), + GE_INCHES, dd); + h = fromDeviceHeight(GEStrHeight(string, + (gc->fontface == 5) ? CE_SYMBOL : + getCharCE(STRING_ELT(text, i % LENGTH(text))), + gc, dd), + GE_INCHES, dd); + } + location(0, 0, bl); + location(w, 0, br); + location(w, h, tr); + location(0, h, tl); + translation(-xadj*w, -yadj*h, thisJustification); + translation(x, y, thisLocation); + if (rot != 0) + rotation(rot, thisRotation); + else + identity(thisRotation); + /* Position relative to origin of rotation THEN rotate. + */ + multiply(thisJustification, thisRotation, tempTransform); + /* Translate to (x, y) + */ + multiply(tempTransform, thisLocation, transform); + trans(bl, transform, tbl); + trans(br, transform, tbr); + trans(tr, transform, ttr); + trans(tl, transform, ttl); + rect(locationX(tbl), locationX(tbr), locationX(ttr), locationX(ttl), + locationY(tbl), locationY(tbr), locationY(ttr), locationY(ttl), + r); + /* For debugging, the following prints out an R statement to draw the + * bounding box + */ + /* + Rprintf("\ngrid.lines(c(%f, %f, %f, %f, %f), c(%f, %f, %f, %f, %f), default.units=\"inches\")\n", + locationX(tbl), locationX(tbr), locationX(ttr), locationX(ttl), + locationX(tbl), + locationY(tbl), locationY(tbr), locationY(ttr), locationY(ttl), + locationY(tbl) + ); + */ +} + +/*********************** + * Stuff for making persistent graphical objects + ***********************/ + +/* Will have already created an SEXP in R. This just stores the + * SEXP in an external reference so that I can get multiple + * references to it. + */ +SEXP L_CreateSEXPPtr(SEXP s) +{ + /* Allocate a list of length one on the R heap + */ + SEXP data, result; + PROTECT(data = allocVector(VECSXP, 1)); + SET_VECTOR_ELT(data, 0, s); + result = R_MakeExternalPtr(data, R_NilValue, data); + UNPROTECT(1); + return result; +} + +SEXP L_GetSEXPPtr(SEXP sp) +{ + SEXP data = R_ExternalPtrAddr(sp); + /* Check for NULL ptr + * This can occur if, for example, a grid grob is saved + * and then loaded. The saved grob has its ptr null'ed + */ + if (data == NULL) + error("grid grob object is empty"); + return VECTOR_ELT(data, 0); +} + +SEXP L_SetSEXPPtr(SEXP sp, SEXP s) +{ + SEXP data = R_ExternalPtrAddr(sp); + /* Check for NULL ptr + * This can occur if, for example, a grid grob is saved + * and then loaded. The saved grob has its ptr null'ed + */ + if (data == NULL) + error("grid grob object is empty"); + SET_VECTOR_ELT(data, 0, s); + return R_NilValue; +} + diff --git a/com.oracle.truffle.r.native/library/grid/src/viewport.c b/com.oracle.truffle.r.native/library/grid/src/viewport.c new file mode 100644 index 0000000000..29004412ae --- /dev/null +++ b/com.oracle.truffle.r.native/library/grid/src/viewport.c @@ -0,0 +1,397 @@ +/* + * R : A Computer Language for Statistical Data Analysis + * Copyright (C) 2001-3 Paul Murrell + * 2003-5 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/ + */ + +#include "grid.h" +#include <string.h> + +extern int gridRegisterIndex; + +/* 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'; + /* 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); +} + diff --git a/com.oracle.truffle.r.native/library/lib.mk b/com.oracle.truffle.r.native/library/lib.mk index 1ece2c3703..72f3be3322 100644 --- a/com.oracle.truffle.r.native/library/lib.mk +++ b/com.oracle.truffle.r.native/library/lib.mk @@ -52,6 +52,8 @@ C_OBJECTS := $(subst $(SRC),$(OBJ),$(C_SOURCES:.c=.o)) H_SOURCES := $(wildcard $(SRC)/*.h) +CFLAGS := $(CFLAGS) -DFASTR + LIBDIR := $(OBJ) # packages seem to use .so even on Mac OS X and no "lib" diff --git a/com.oracle.truffle.r.native/osextras/Makefile b/com.oracle.truffle.r.native/osextras/Makefile index ba2ad13e84..19138d1976 100644 --- a/com.oracle.truffle.r.native/osextras/Makefile +++ b/com.oracle.truffle.r.native/osextras/Makefile @@ -37,7 +37,7 @@ C_SOURCES := $(wildcard $(SRC)/*.c) C_LIBNAME := libosextras$(DYLIB_EXT) C_OBJECTS := $(subst $(SRC),$(OBJ),$(C_SOURCES:.c=.o)) C_LIB := $(TOPDIR)/builtinlibs/$(OBJ)/$(C_LIBNAME) -CFLAGS := $(CFLAGS) -DFASTR +#CFLAGS := $(CFLAGS) -DFASTR JNI_INCLUDES = -I $(JAVA_HOME)/include -I $(JAVA_HOME)/include/$(JDK_OS_DIR) diff --git a/com.oracle.truffle.r.native/run/edMakeconf.etc b/com.oracle.truffle.r.native/run/edMakeconf.etc index 3a31b7548f..4e0dff0bb2 100644 --- a/com.oracle.truffle.r.native/run/edMakeconf.etc +++ b/com.oracle.truffle.r.native/run/edMakeconf.etc @@ -1,3 +1,4 @@ +/^CFLAGS/s/$/ -DFASTR/ /LIBINTL=/ d /LIBR =/ diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java index 9d3e21b195..3228b7b5eb 100644 --- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java +++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIHelper.java @@ -306,4 +306,21 @@ public class CallRFFIHelper { static Object validate(Object x) { return x; } + + static Object getGlobalEnv() { + return RContext.getREnvironmentState().getGlobalEnv(); + } + + static Object getBaseEnv() { + return RContext.getREnvironmentState().getBaseEnv(); + } + + static Object getBaseNamespace() { + return RContext.getREnvironmentState().getBaseNamespace(); + } + + static Object getNamespaceRegistry() { + return RContext.getREnvironmentState().getNamespaceRegistry(); + } + } diff --git a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIWithJNI.java b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIWithJNI.java index 1e8f3a64ad..587bca5861 100644 --- a/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIWithJNI.java +++ b/com.oracle.truffle.r.runtime.ffi/src/com/oracle/truffle/r/runtime/ffi/jnr/CallRFFIWithJNI.java @@ -48,14 +48,56 @@ public class CallRFFIWithJNI implements CallRFFI { private static final boolean ForceRTLDGlobal = false; - // The order must match that expected in rfficall.c - // @formatter:off - private static final Object[] INITIALIZE_VALUES = new Object[]{ - REnvironment.emptyEnv(), - RNull.instance, RUnboundValue.instance, RMissing.instance, - RDataFactory.createSymbol("class") - }; - // @formatter:on + public enum RVariables { + R_NilValue(RNull.instance), + R_UnboundValue(RUnboundValue.instance), + R_MissingArg(RMissing.instance), + R_GlobalEnv(null), + R_EmptyEnv(REnvironment.emptyEnv()), + R_BaseEnv(null), + R_BaseNamespace(null), + R_NamespaceRegistry(null), + R_Srcref(null), + R_Bracket2Symbol(null), + R_BracketSymbol(null), + R_BraceSymbol(null), + R_ClassSymbol(RDataFactory.createSymbol("class")), + R_DeviceSymbol(null), + R_DimNamesSymbol(RDataFactory.createStringVectorFromScalar(RRuntime.DIMNAMES_ATTR_KEY)), + R_DimSymbol(RDataFactory.createStringVectorFromScalar(RRuntime.DIM_ATTR_KEY)), + R_DollarSymbol(null), + R_DotsSymbol(null), + R_DropSymbol(null), + R_LastvalueSymbol(null), + R_LevelsSymbol(null), + R_ModeSymbol(null), + R_NameSymbol(null), + R_NamesSymbol(null), + R_NaRmSymbol(null), + R_PackageSymbol(null), + R_QuoteSymbol(null), + R_RowNamesSymbol(null), + R_SeedsSymbol(null), + R_SourceSymbol(null), + R_TspSymbol(null), + R_dot_defined(null), + R_dot_Method(null), + R_dot_target(null), + R_SrcrefSymbol(RDataFactory.createSymbol("srcref")), + R_SrcfileSymbol(RDataFactory.createSymbol("srcfile")), + R_NaString(RDataFactory.createStringVectorFromScalar(RRuntime.STRING_NA)), + R_BlankString(RDataFactory.createStringVectorFromScalar("")); + + private Object value; + + RVariables(Object value) { + this.value = value; + } + + public Object getValue() { + return value; + } + } /** * Load the {@code librfficall} library. N.B. this library defines some non-JNI global symbols @@ -73,7 +115,7 @@ public class CallRFFIWithJNI implements CallRFFI { throw RError.error(RError.NO_NODE, ex); } System.load(librffiPath); - initialize(INITIALIZE_VALUES); + initialize(RVariables.values()); } private static final Semaphore inCritical = new Semaphore(1, false); @@ -109,7 +151,7 @@ public class CallRFFIWithJNI implements CallRFFI { throw RInternalError.unimplemented(".External"); } - private static native void initialize(Object[] initialValues); + private static native void initialize(RVariables[] variables); private static native Object call(long address, Object[] args); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java index 11f0be5931..581553fd19 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java @@ -99,6 +99,8 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa Base getBaseEnv(); + REnvironment getBaseNamespace(); + REnvironment getNamespaceRegistry(); REnvironment.SearchPath getSearchPath(); @@ -139,6 +141,10 @@ public abstract class REnvironment extends RAttributeStorage implements RTypedVa return baseEnv; } + public REnvironment getBaseNamespace() { + return baseEnv.getNamespace(); + } + public REnvironment getNamespaceRegistry() { return namespaceRegistry; } diff --git a/com.oracle.truffle.r.test.cran/r/install.cran.packages.R b/com.oracle.truffle.r.test.cran/r/install.cran.packages.R new file mode 100644 index 0000000000..bdb1406713 --- /dev/null +++ b/com.oracle.truffle.r.test.cran/r/install.cran.packages.R @@ -0,0 +1,183 @@ +# A script to install CRAN packages, with a blacklist mechanism starting from a known +# set of packages that we cannot handle, e.g. Rcpp (due to C++) +# By default all packages are candidates for installation, but this +# can be limited by a regexp pattern + +args <- commandArgs(TRUE) + +usage <- function() { + cat("usage: Rscript [--contriburl url] [--verbose | -v] [-dryrun] [--save-blacklist file] [--blacklist file] [package-pattern\n") + quit(status=1) +} + +# blacklist is a vector of package (names) that are known to be bad, i.e. uninstallable. +# the result is a vector of new packages that depend/import/suggest/linkto any package on blacklist +create.blacklist.with <- function(blacklist) { + this.blacklist <- vector() + + trim <- function (x) gsub("^\\s+|\\s+$", "", x) + + strip.version <- function(x) gsub("\\s+\\(.*\\)$", "", x) + + for (i in (1:length(rownames(avail.pkgs)))) { + pkg <- avail.pkgs[i, ] + if (!(pkg["Package"] %in% blacklist)) { + all.deps <- vector() + for (dep in c("Depends", "Imports", "LinkingTo")) { + deps <- pkg[dep] + if (!is.na(dep)) { + if (very.verbose) { + cat(dep, " deps for: ", pkg["Package"], " ", deps, "\n") + } + all.deps <- append(all.deps, strip.version(trim(unlist(strsplit(deps, fixed=T, ", "))))) + } + } + + match.result <- match(blacklist, all.deps, nomatch=0) + in.result <- match.result > 0 + if (any(in.result)) { + if (verbose) { + names(all.deps) <- NULL + cat("adding: ", pkg["Package"], " to blacklist (", all.deps[match.result], ")\n") + } + this.blacklist <- append(this.blacklist, pkg["Package"]) + } + } + } + + names(this.blacklist) <- NULL + this.blacklist +} + +# iteratively adds to blacklist until no new blackisted packages are found +create.blacklist.iter <- function(blacklist) { + v <-blacklist + result <-v + while (length(v) > 0) { + v <- create.blacklist.with(result) + result <- append(result, v) + } + result +} + +# known to be uninstallable +initial.blacklist <- c("Rcpp", "grid", "splines", "parallel") + +create.blacklist <- function() { + create.blacklist.iter(initial.blacklist) +} + +abort <- function(msg) { + print(msg) + quit("no", 1) +} + +# find the available packages from contriburl and macth those against pkg.pattern +# sets global variables avail.pkgs and toinstall.pkgs +get.pkgs <- function() { + avail.pkgs <<- available.packages(contriburl=contriburl, type="source") + matched.avail.pkgs <- apply(avail.pkgs, 1, function(x) grepl(pkg.pattern, x["Package"])) + toinstall.pkgs <<-avail.pkgs[matched.avail.pkgs, , drop=F] +} + +# performs the installation, or logs what it would install if dry.run = T +# either creates the blacklist or reads it from a file +do.install <- function() { + get.pkgs() + + if (read.blacklist) { + if (is.na(blacklist.file) || !file.exists(blacklist.file)) { + abort("blacklist file not set or does not exist") + } else { + blacklist <- readLines(con=file(blacklist.file)) + } + } else { + blacklist <- create.blacklist() + } + + if (save.blacklist) { + if (is.na(blacklist.file)) { + abort("blacklist file not set") + } else { + writeLines(sort(blacklist), con=blacklist.file) + } + } + + pkgnames <- rownames(toinstall.pkgs) + for (pkgname in pkgnames) { + if (pkgname %in% blacklist) { + cat("not installing: ", pkgname, " - blacklisted\n") + } else { + if (dry.run) { + cat("would install: ", pkgname, "\n") + } else { + cat("installing: ", pkgname, "\n") + install.packages(pkgname, contriburl=contriburl, type="source", INSTALL_opts="--install-tests") + } + } + } +} + +# parse the command line arguments when run as a script +parse.args <- function() { + while (length(args)) { + a <- args[1L] + if (a %in% c("-h", "--help")) { + usage() + } else if (a == "--contriburl") { + if (length(args) >= 2L) { + contriburl <<- args[2L] + args <<- args[-1L] + } else { + usage() + } + } else if (a == "--verbose" || a == "-v") { + verbose <<- T + } else if (a == "-V") { + verbose <<- T + very.verbose <- T + } else if (a == "--dryrun") { + dry.run <<- T + } else if (a == "--save-blacklist") { + save.blacklist <<- T + } else if (a == "--read-blacklist") { + read.blacklist <<- T + } else if (a == "--blacklist") { + if (length(args) >= 2L) { + blacklist.file <<- args[2L] + args <<- args[-1L] + } else { + usage() + } + } else { + pkg.pattern <<- a + break + } + + args <<- args[-1L] + } +} + +# global variables used by the installation +contriburl <- Sys.getenv("LOCAL_CRAN_REPO", unset=NA) +if (is.na(contriburl)) { + contriburl <- paste("file://", getwd(), "/cran/LOCAL_REPO/src/contrib", sep="") +} +blacklist.file <- Sys.getenv("PACKAGE_BLACKLIST", unset=NA) + +pkg.pattern <- "^.*" +verbose <- F +very.verbose <- F +dry.run <- F +avail.pkgs <- NULL +toinstall.pkgs <- NULL +save.blacklist <- F +read.blacklist <- F + +if (!interactive()) { + parse.args() + do.install() +} + +#tryCatch(url(contriburl, open="r"), error=abort) + diff --git a/mx.fastr/mx_fastr.py b/mx.fastr/mx_fastr.py index 67ae1eb180..9ceb3128ba 100644 --- a/mx.fastr/mx_fastr.py +++ b/mx.fastr/mx_fastr.py @@ -136,12 +136,12 @@ def _fastr_gate_runner(args, tasks): with mx_gate.Task('UnitTests: +EST', tasks) as t: if t: - if junit(['--J', '@-DR:+ExperimentalStateTrans', '--tests', _gate_unit_tests()]) != 0: + if junit(['--J', '@-DR:+NewStateTransition', '--tests', _gate_unit_tests()]) != 0: t.abort('unit tests failed') with mx_gate.Task('UnitTests: -EST', tasks) as t: if t: - if junit(['--J', '@-DR:-ExperimentalStateTrans', '--tests', _gate_unit_tests()]) != 0: + if junit(['--J', '@-DR:-NewStateTransition', '--tests', _gate_unit_tests()]) != 0: t.abort('unit tests failed') mx_gate.add_gate_runner(_fastr_suite, _fastr_gate_runner) @@ -279,8 +279,11 @@ def _ser_unit_tests(): def _app_unit_tests(): return _test_subpackage('apps') +def _tck_unit_tests(): + return _test_subpackage('tck') + def _gate_unit_tests(): - return ','.join((_library_unit_tests(), _rffi_unit_tests(), _rpackages_unit_tests(), _builtins_unit_tests(), _functions_unit_tests(), _ser_unit_tests(), _app_unit_tests(), _nodes_unit_tests())) + return ','.join((_library_unit_tests(), _rffi_unit_tests(), _rpackages_unit_tests(), _builtins_unit_tests(), _functions_unit_tests(), _ser_unit_tests(), _app_unit_tests(), _nodes_unit_tests(), _tck_unit_tests())) def _all_unit_tests(): return _gate_unit_tests() @@ -401,6 +404,11 @@ def runRREPL(args, nonZeroIsFatal=True, extraVmArgs=None): '''run R repl''' return runR(args, _rREPLClass(), nonZeroIsFatal=nonZeroIsFatal, extraVmArgs=['-DR:+Instrument']) +def installcran(args): + cran = 'com.oracle.truffle.r.test.cran' + join(mx.project(cran).dir, 'r', 'install.cran.packages.R') + return runR(args, rscript_command_class()) + def load_optional_suite(name): hg_base = mx.get_env('MX_HG_BASE') urlinfos = None if hg_base is None else [mx.SuiteImportURLInfo(join(hg_base, name), 'hg', mx.vc_system('hg'))] @@ -434,6 +442,7 @@ _commands = { 'rcmplib' : [rcmplib, ['options']], 'test' : [test, ['options']], 'rrepl' : [runRREPL, '[options]'], + 'installcran' : [installcran, '[options]'] } mx.update_commands(_fastr_suite, _commands) diff --git a/mx.fastr/suite.py b/mx.fastr/suite.py index c31aea79bd..4f11e66ae8 100644 --- a/mx.fastr/suite.py +++ b/mx.fastr/suite.py @@ -267,6 +267,7 @@ suite = { "sourceDirs" : ["src"], "dependencies" : [ "mx:JUNIT", + "truffle:TRUFFLE_TCK", "com.oracle.truffle.r.engine", ], "checkstyle" : "com.oracle.truffle.r.runtime", @@ -282,6 +283,12 @@ suite = { "workingSets" : "FastR", }, + "com.oracle.truffle.r.test.cran" : { + "sourceDirs" : ["r"], + "javaCompliance" : "1.8", + "workingSets" : "FastR", + }, + "com.oracle.truffle.r.engine" : { "sourceDirs" : ["src"], "dependencies" : [ -- GitLab