警告
我将在这里提出一个主要使用 C++ 代码的解决方案。为了使它更具可重复性,以便它可以在 R 控制台中运行,我使用Rcpp::cppFunction 完成了这项工作。但是,这显然不是您在构建包时会使用的方法。生成的函数通过访问指向用户必须指定的 R 图形设备的原始指针来工作,如果您使用不存在的设备编号调用它们,您的 R 会话将崩溃。
解决方案
这些 cmets 所描述的 GPar 结构的三个副本一起保存在另一个名为 baseSystemState 的结构中,该结构定义为 here。
每个图形设备都有一个指向baseSystemState 的指针,如果我们在自己的代码中包含头文件include/R_ext/GraphicsEngine.h,我们就可以使用C 或C++ 代码访问图形设备。
但是,有一个障碍。虽然我们可以得到一个指向 baseSystemState 结构的指针,但我们的代码并不知道它到底是什么,因为 baseSystemState 和 GPar 的定义不是公共 API 的一部分。
因此,为了阅读其中包含的baseSystemState 和GPars,我们必须在我们自己的代码中重新定义这些结构(正如Dirk 在他的评论中所建议的那样)。 GPar 的一些成员也是需要先定义的类型或枚举。
我们可以采用这些定义,将它们压缩成一个字符串,并在Rcpp::cppFunction 调用中将它们用作includes。这是一个包装函数,它允许您编写可以访问现有图形设备参数的 C++ 函数:
cppFunction_graphics <- function(s)
{
include <- paste0("#include \"", R.home("include/R_ext/GraphicsEngine.h\""))
Rcpp::cppFunction(s, includes = c(include,
"typedef enum {DEVICE= 0, NDC= 1, INCHES = 13,
NIC = 6, OMA1= 2, OMA2= 3, OMA3 = 4,OMA4= 5,NFC = 7, NPC= 16,USER= 12, MAR1 = 8,
MAR2= 9, MAR3= 10,MAR4= 11, LINES = 14, CHARS =15 } GUnit; typedef struct {
double ax; double bx; double ay; double by;} GTrans; typedef struct {int state;
Rboolean valid; double adj; Rboolean ann; rcolor bg; char bty; double cex;
double lheight; rcolor col; double crt; double din[2]; int err; rcolor fg;
char family[201]; int font; double gamma; int lab[3]; int las; int lty;
double lwd; R_GE_lineend lend; R_GE_linejoin ljoin; double lmitre; double mgp[3];
double mkh; int pch; double ps; int smo; double srt; double tck; double tcl;
double xaxp[3]; char xaxs; char xaxt; Rboolean xlog; int xpd; int oldxpd;
double yaxp[3]; char yaxs; char yaxt; Rboolean ylog; double cexbase;
double cexmain; double cexlab; double cexsub; double cexaxis; int fontmain;
int fontlab; int fontsub; int fontaxis; rcolor colmain; rcolor collab;
rcolor colsub; rcolor colaxis; Rboolean layout; int numrows; int numcols;
int currentFigure; int lastFigure; double heights[200]; double widths[200];
int cmHeights[200]; int cmWidths[200]; unsigned short order[10007]; int rspct;
unsigned char respect[10007]; int mfind; double fig[4]; double fin[2];
GUnit fUnits; double plt[4]; double pin[2]; GUnit pUnits; Rboolean defaultFigure;
Rboolean defaultPlot; double mar[4]; double mai[4]; GUnit mUnits; double mex;
double oma[4]; double omi[4]; double omd[4]; GUnit oUnits; char pty;
double usr[4]; double logusr[4]; Rboolean new_one; int devmode;
double xNDCPerChar; double yNDCPerChar; double xNDCPerLine; double yNDCPerLine;
double xNDCPerInch; double yNDCPerInch; GTrans fig2dev; GTrans inner2dev;
GTrans ndc2dev; GTrans win2fig; double scale;} GPar; typedef struct {GPar dp;
GPar gp; GPar dpSaved; Rboolean baseDevice;} baseSystemState;"),
env = parent.frame(2))
}
所以现在我们可以编写一个函数,从设备的起始参数中提取或写入我们选择的图形参数。在这里,我们将让我们的函数返回各种颜色参数的列表,但是您可以从GPar 返回任何您喜欢的参数,其中大部分在GPar struct definition 中是不言自明的
cppFunction_graphics("
Rcpp::List get_default_GPar(int devnum)
{
pGEDevDesc dd = GEgetDevice(devnum);
baseSystemState *bss = (baseSystemState*) dd->gesd[0]->systemSpecific;
GPar GP = bss->dp;
auto get_colour = [](rcolor rcol){
return Rcpp::NumericVector::create(
Rcpp::Named(\"red\") = rcol & 0xff,
Rcpp::Named(\"green\") = (rcol >> 8) & 0xff,
Rcpp::Named(\"blue\") = (rcol >> 16) & 0xff);
};
return Rcpp::List::create(Rcpp::Named(\"fg\") = get_colour(GP.fg),
Rcpp::Named(\"bg\") = get_colour(GP.bg),
Rcpp::Named(\"col\") = get_colour(GP.col),
Rcpp::Named(\"colmain\") = get_colour(GP.colmain),
Rcpp::Named(\"collab\") = get_colour(GP.collab),
Rcpp::Named(\"colaxis\") = get_colour(GP.colaxis));
}
")
所以现在在 R 中,我可以通过以下方式确保我的设备在运行:
plot(1:10)
我可以访问当前设备的默认图形参数:
get_default_GPar(dev.cur() - 1)
#> $fg
#> red green blue
#> 0 0 0
#>
#> $bg
#> red green blue
#> 255 255 255
#>
#> $col
#> red green blue
#> 0 0 0
#>
#> $colmain
#> red green blue
#> 0 0 0
#>
#> $collab
#> red green blue
#> 0 0 0
#>
#> $colaxis
#> red green blue
#> 0 0 0
这为我提供了默认设备参数的正确值。
现在,如果我定义另一个函数,我也可以写入默认设备参数。假设我希望能够更改设备标签的默认颜色:
cppFunction_graphics("
void set_col(int dn, int red, int green, int blue, int alpha)
{
int new_col = red | (green << 8) | (blue << 16) | (alpha << 24);
pGEDevDesc dd = GEgetDevice(dn);
baseSystemState *bss = (baseSystemState*) dd->gesd[0]->systemSpecific;
bss->dp.collab = new_col;
}
")
现在我在 R 中有一个函数可以覆盖设备的默认标签颜色。让我们将默认标签设为红色:
set_col(dev.cur() - 1, 255, 0, 0, 255)
所以现在当我在同一设备上绘制新图时,标签会自动变为红色:
plot(1:10)
因此,您可以根据需要更改设备的 gpar,而不会直接干扰par。
至于访问已保存的GPar和当前的GPar,这只是将GPar GP = bss->dp;这一行更改为GPar GP = bss->gp或GPar GP = bss->dpSaved的情况