1"""
2Mapping of the R library "grid" for graphics.
3
4The R library provides a low-level coordinate
5system and graphic primitives to built visualizations.
6
7
8"""
9
10import rpy2.robjects as robjects
11import rpy2.robjects.conversion as conversion
12from rpy2.rlike.container import OrdDict
13from rpy2.robjects.packages import importr, WeakPackage
14
15NULL = robjects.NULL
16
17grid = importr('grid')
18
19grid = WeakPackage(grid._env,
20                   grid.__rname__,
21                   translation=grid._translation,
22                   exported_names=grid._exported_names,
23                   on_conflict="warn",
24                   version=grid.__version__,
25                   symbol_r2python=grid._symbol_r2python,
26                   symbol_resolve=grid._symbol_resolve)
27
28grid_env = robjects.baseenv['as.environment']('package:grid')
29
30
31layout = grid.grid_layout
32newpage = grid.grid_newpage
33grill = grid.grid_grill
34edit = grid.grid_edit
35get = grid.grid_get
36remove = grid.grid_remove
37add = grid.grid_add
38xaxis = grid.grid_xaxis
39yaxis = grid.grid_yaxis
40
41
42class Unit(robjects.RObject):
43    """ Vector of unit values (as in R's grid package) """
44    _unit = grid_env['unit']
45
46    @classmethod
47    def unit(cls, *args, **kwargs):
48        """ Constructor (uses the R function grid::unit())"""
49        res = cls._unit(*args, **kwargs)
50        return res
51
52
53unit = Unit.unit
54
55
56class Gpar(robjects.RObject):
57    """ Graphical parameters """
58    _gpar = grid_env['gpar']
59    _get_gpar = grid_env['get.gpar']
60
61    @classmethod
62    def gpar(cls, *args, **kwargs):
63        """ Constructor (uses the R function grid::gpar())"""
64        res = cls._gpar(*args, **kwargs)
65        return res
66
67    def get(self, names=None):
68        return self._get_gpar(names)
69
70
71gpar = Gpar.gpar
72
73
74class Grob(robjects.RObject):
75    """ Graphical object """
76    _grob = grid_env['grob']
77    _draw = grid_env['grid.draw']
78
79    def __init__(self, *args, **kwargs):
80        od = OrdDict()
81        for item in args:
82            od[None] = conversion.py2rpy(item)
83        for k, v in kwargs.items():
84            od[k] = conversion.py2rpy(v)
85        res = self._constructor.rcall(tuple(od.items()), robjects.globalenv)
86        super().__init__(res.__sexp__)
87
88    @classmethod
89    def grob(cls, **kwargs):
90        """ Constructor (uses the R function grid::grob())"""
91        res = cls._grob(**kwargs)
92        return res
93
94    def draw(self, recording=True):
95        """ Draw a graphical object (calling the R function
96        grid::grid.raw())"""
97        self._draw(self, recording=recording)
98
99
100grob = Grob.grob
101
102
103class Rect(Grob):
104    _constructor = grid_env['rectGrob']
105
106
107rect = Rect
108
109
110class Lines(Grob):
111    _constructor = grid_env['linesGrob']
112
113
114lines = Lines
115
116
117class Circle(Grob):
118    _constructor = grid_env['circleGrob']
119
120
121circle = Circle
122
123
124class Points(Grob):
125    _constructor = grid_env['pointsGrob']
126
127
128points = Points
129
130
131class Text(Grob):
132    _constructor = grid_env['textGrob']
133
134
135text = Text
136
137
138class GTree(Grob):
139    """ gTree """
140    _gtree = grid_env['gTree']
141    _grobtree = grid_env['grobTree']
142
143    @classmethod
144    def gtree(cls, **kwargs):
145        """ Constructor (uses the R function grid::gTree())"""
146        res = cls._gtree(**kwargs)
147        return res
148
149    @classmethod
150    def grobtree(cls, **kwargs):
151        """ Constructor (uses the R function grid::grobTree())"""
152        res = cls._grobtree(**kwargs)
153        return res
154
155
156class Axis(GTree):
157    pass
158
159
160class XAxis(Axis):
161    _xaxis = xaxis
162    _xaxisgrob = grid.xaxisGrob
163
164    @classmethod
165    def xaxis(cls, **kwargs):
166        """ Constructor (uses the R function grid::xaxis())"""
167        res = cls._xaxis(**kwargs)
168        return res
169
170    @classmethod
171    def xaxisgrob(cls, **kwargs):
172        """ Constructor (uses the R function grid::xaxisgrob())"""
173        res = cls._xaxisgrob(**kwargs)
174        return res
175
176
177class YAxis(Axis):
178    _yaxis = yaxis
179    _yaxisgrob = grid.yaxisGrob
180
181    @classmethod
182    def yaxis(cls, **kwargs):
183        """ Constructor (uses the R function grid::yaxis())"""
184        res = cls._yaxis(**kwargs)
185        return res
186
187    @classmethod
188    def yaxisgrob(cls, **kwargs):
189        """ Constructor (uses the R function grid::yaxisgrob())"""
190        res = cls._yaxisgrob(**kwargs)
191        return res
192
193
194class Viewport(robjects.RObject):
195    """ Drawing context.
196    Viewports can be thought of as nodes in a scene graph. """
197
198    _pushviewport = grid_env['pushViewport']
199    _popviewport = grid_env['popViewport']
200    _current = grid_env['current.viewport']
201    _plotviewport = grid_env['plotViewport']
202    _downviewport = grid_env['downViewport']
203    _seek = grid_env['seekViewport']
204    _upviewport = grid_env['upViewport']
205    _viewport = grid_env['viewport']
206
207    def push(self, recording=True):
208        self._pushviewport(self, recording=recording)
209
210    @classmethod
211    def pop(cls, n):
212        """ Pop n viewports from the stack. """
213        cls._popviewport(n)
214
215    @classmethod
216    def current(cls):
217        """ Return the current viewport in the stack. """
218        cls._current()
219
220    @classmethod
221    def default(cls, **kwargs):
222        cls._plotviewport(**kwargs)
223
224    @classmethod
225    def down(cls, name, strict=False, recording=True):
226        """ Return the number of Viewports it went down """
227        cls._downviewport(name, strict=strict, recording=recording)
228
229    @classmethod
230    def seek(cls, name, recording=True):
231        """ Seek and return a Viewport given its name """
232        cls._seek(name, recording=recording)
233
234    @classmethod
235    def up(cls, n, recording=True):
236        """ Go up n viewports """
237        cls._downviewport(n, recording=recording)
238
239    @classmethod
240    def viewport(cls, **kwargs):
241        """ Constructor: create a Viewport """
242        res = cls._viewport(**kwargs)
243        res = cls(res)
244        return res
245
246
247viewport = Viewport.viewport
248
249_grid_dict = {
250    'gpar': Gpar,
251    'grob': Grob,
252    'gTree': GTree,
253    'unit': Unit,
254    'xaxis': XAxis,
255    'yaxis': YAxis,
256    'viewport': Viewport
257}
258
259original_py2rpy = None
260original_rpy2py = None
261
262
263def grid_rpy2py(robj):
264
265    pyobj = original_rpy2py(robj)
266
267    if not isinstance(pyobj, robjects.RS4):
268        rcls = pyobj.rclass
269        if rcls is NULL:
270            rcls = (None, )
271        try:
272            cls = _grid_dict[rcls[0]]
273            pyobj = cls(pyobj)
274        except KeyError:
275            pass
276
277    return pyobj
278
279
280def activate():
281    global original_py2rpy, original_rpy2py
282
283    # If module is already activated, there is nothing to do
284    if original_py2rpy:
285        return
286
287    original_py2rpy = conversion.py2rpy
288    original_rpy2py = conversion.rpy2py
289
290    conversion.rpy2py = grid_rpy2py
291
292
293def deactivate():
294    global original_py2rpy, original_rpy2py
295
296    # If module has never been activated or already deactivated,
297    # there is nothing to do
298    if not original_py2rpy:
299        return
300
301    conversion.py2rpy = original_py2rpy
302    conversion.rpy2py = original_rpy2py
303    original_py2rpy = original_rpy2py = None
304