【问题标题】:How do I set the device-copy parameters when initializing a graphics device?初始化图形设备时如何设置设备复制参数?
【发布时间】:2020-06-26 15:55:03
【问题描述】:

我正在使用 R 包中的图形设备,需要设置一些 初始化或重置设备上的文本/标签的图形参数。

R Internals 中描述了此功能:

GPar结构的三个副本用于存储当前参数(通过gpptr访问), “设备副本”(通过 dpptr 访问)和用于保存“设备副本”参数副本的空间。 显然,当前参数是当前正在使用的参数,并且是从“设备副本”复制而来的 每当调用 plot.new() 时(无论是否前进到下一个“页面”)。保存的副本 保持设备上次完全清除时的状态(例如,当 plot.new() 被调用时 与 par(new=TRUE)),并用于重播显示列表。

我如何从包中实际访问和初始化“设备副本”?

我能找到的只是一个包含较旧的评论, 复制粘贴GraphicsDevice.h中的评论:

  * 2. I found this comment in the doc for dev_Open -- looks nasty
  *    Any known instances of such a thing happening?  Should be
  *    replaced by a function to query the device for preferred gpars
  *    settings? (to be called when the device is initialised)
          *
          * NOTE that it is perfectly acceptable for this
          * function to set generic graphics parameters too
          * (i.e., override the generic parameter settings
          * which GInit sets up) all at the author's own risk
          * of course :)

【问题讨论】:

  • R-devel 或 r-package-devel 问题比 Rcpp 可以帮助解决的问题更多,不是吗?
  • 我已经在 r-package-devel 上询问过,一周后没有任何回复,所以我在这里再试一次。如果一周后仍然没有线索,我可能会增加赏金。
  • @NealFultz 你能详细说明你想要实现的目标吗?当用户打开新的图形设备时,您是否尝试检查设备功能、尺寸等?您是否要检查和设置par 中未列出的图形参数?如果有,有哪些?对graphicsgrDevices 后面的C 代码进行一些低级调用是相当容易的,但是从这个问题中我不清楚你到底想要做什么。 dpptr 函数(在 C 中)从字面上返回一个指向设备的内存指针,但没有图形参数
  • 我正在一个包中实现新的图形设备,需要在创建设备时设置文本/轴标签的默认颜色(黑色不可见),以便为每个新绘图保留它们该设备创建但不会覆盖每个 cran 策略的用户提供的设置。 dpptr / Graphics.h 不是公共 API 包的一部分,可以使用 afaict,但是在我找不到的地方有一个暗示的回调。我想知道这样做的“官方正确方法”。
  • 也许我们彼此误解了。 当然 Rcpp 可以帮助您进行自定义 编程和特定应用程序,就像您在此处所做的那样。我们中的许多人都这样做。我想表达的是为你解决这个问题不在Rcpp自己的领域。这样更有意义?不管怎样,很高兴你修好了。

标签: r


【解决方案1】:

我不知道我是否完全理解您想要做什么,但我认为您可能会发现 this guide 很有用。一些关键摘录:

要使用我们自己的函数创建一个正在运行的图形设备,我们调用 graphicsDevice() 函数。虽然有几种方法 这个,本质上我们给它一个指定函数的列表 部分或全部 21 个图形原语的实现 操作。我们可以把它作为一个列表或作为一个实例 RDevDescMethods 或我们为特定类型定义的子类 的设备。所以我们专注于编写这些函数。

然后:

每个方法都传递了一个 DevDescPtr 类的对象。这是 也是顶级函数返回的值的类型 图形设备()。这是一个参考 C 级数据结构 表示图形设备。我们可以使用它来查询设置 图形设备。

使用了设备中的某些字段 在初始化设备时而不是在函数内(例如 那些名字以“start”为前缀的人)。其他领域是 关于渲染不同方面的结构信息 设备。例如,我们可以找到绘图区域的尺寸, DevDescPtr 类本质上是 R 中的一种不透明数据类型 (包含指向 C 级数据结构的外部指针)并且是 旨在将其用作 R 级列表。我们可以使用 $ 运算符来访问各个字段,我们可以找到 这些带有名称()的字段。

最后:

在极少数情况下,可以方便地转换 引用 R 对象。我们可以通过强制它来做到这一点 对应的名为 DevDesc 的 R 类(即带有“Ptr”删除),即 作为(开发,“DevDesc”)。这会复制 C 级中的每个字段 结构到R类中对应的槽。

例如设备的circle方法有这个签名:

circle ( numeric, numeric, numeric, R_GE_gcontextPtr, DevDescPtr )

R_GE_gcontextPtr 是:

...另一个对 C 级数据类型实例的引用。这是 有关设备“当前”设置的信息。这给了我们 关于当前笔/前景色、背景的信息 颜色,伽马级别的设置,线宽,样式,连接, 字符点大小和扩展/放大,以及字体 信息。可用的字段是

names(new("R_GE_gcontextPtr"))
 [1] "col"        "fill"       "gamma"      "lwd"        "lty"       
 [6] "lend"       "ljoin"      "lmitre"     "cex"        "ps"        
[11] "lineheight" "fontface"   "fontfamily"

【讨论】:

  • 这看起来很有前途@Ifalin,但在过去的 7 年里似乎没有得到维护,我无法在我的机器上构建它。
  • 我不一定提倡图书馆本身,而是指南中的概念。例如,如果您查看当前 R 源中的 circle 回调,您可以看到指南中概述的参数和概念是相同的。例如,请参阅github.com/wch/r-source/blob/…github.com/wch/r-source/blob/…
【解决方案2】:

警告

我将在这里提出一个主要使用 C++ 代码的解决方案。为了使它更具可重复性,以便它可以在 R 控制台中运行,我使用Rcpp::cppFunction 完成了这项工作。但是,这显然不是您在构建包时会使用的方法。生成的函数通过访问指向用户必须指定的 R 图形设备的原始指针来工作,如果您使用不存在的设备编号调用它们,您的 R 会话将崩溃。


解决方案

这些 cmets 所描述的 GPar 结构的三个副本一起保存在另一个名为 baseSystemState 的结构中,该结构定义为 here

每个图形设备都有一个指向baseSystemState 的指针,如果我们在自己的代码中包含头文件include/R_ext/GraphicsEngine.h,我们就可以使用C 或C++ 代码访问图形设备。

但是,有一个障碍。虽然我们可以得到一个指向 baseSystemState 结构的指针,但我们的代码并不知道它到底是什么,因为 baseSystemStateGPar 的定义不是公共 API 的一部分。

因此,为了阅读其中包含的baseSystemStateGPars,我们必须在我们自己的代码中重新定义这些结构(正如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-&gt;dp;这一行更改为GPar GP = bss-&gt;gpGPar GP = bss-&gt;dpSaved的情况

【讨论】:

  • 这似乎是目前最好的解决方案。在编写包的上下文中,我想我必须将匹配版本的 R 中的 Graphics.hGraphicsBase.h 复制到我的 src/ 文件夹中,并将它们包含在我的 c++ 代码中。看起来只有辅助函数 Rf_dpptr 被列入R CMD check 中的 cran 的黑名单;我想这种方法现在会被接受,因为它直接访问设置。你有没有发现dev_Open API 或者它根本不存在?
  • @NealFultz 这取决于您要访问或修改的内容。如果您只想要 GPar,您可以将 GTrans、GPar 和 baseSystemState 结构(加上 GUnit 枚举)复制并粘贴到 #includes Rinternals.h 的自定义头文件中,并且您有一个 GPar API。 dev_Open 评论有点神秘。它实际上是指来自devx11.c 早期版本的评论,它专门指的是x11_Open - 这要么是错字,要么可能是x11_Open 在R 1.3 之前被称为dev_Open,这是我检查的最早版本。
猜你喜欢
  • 2017-04-03
  • 1970-01-01
  • 2012-11-08
  • 2016-09-11
  • 1970-01-01
  • 2021-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多