diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPort.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPort.java
new file mode 100644
index 0000000000000000000000000000000000000000..591c4e25f4ef7b9780e6870b47f142c4998ecb4f
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPort.java
@@ -0,0 +1,410 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asAbstractContainer;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asDouble;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asList;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asListOrNull;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.sum;
+import static com.oracle.truffle.r.library.fastrGrid.TransformMatrix.flatten;
+import static com.oracle.truffle.r.library.fastrGrid.TransformMatrix.fromFlat;
+import static com.oracle.truffle.r.library.fastrGrid.TransformMatrix.multiply;
+import static com.oracle.truffle.r.library.fastrGrid.TransformMatrix.rotation;
+import static com.oracle.truffle.r.library.fastrGrid.TransformMatrix.translation;
+import static com.oracle.truffle.r.library.fastrGrid.Unit.newUnit;
+import static com.oracle.truffle.r.nodes.builtin.casts.fluent.CastNodeBuilder.newCastBuilder;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.r.library.fastrGrid.Unit.IsRelativeUnitNode;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.ViewPort.LayoutPos;
+import com.oracle.truffle.r.library.fastrGrid.ViewPort.LayoutSize;
+import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.nodes.unary.CastNode;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RDoubleVector;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
+
+class DoSetViewPort extends RBaseNode {
+    @Child private CastNode castScalarDouble = newCastBuilder().asDoubleVector().findFirst().buildCastNode();
+    @Child private CastNode castDoubleVector = newCastBuilder().asDoubleVector().buildCastNode();
+    @Child private CastNode castChildrenEnv = newCastBuilder().mustBe(REnvironment.class).buildCastNode();
+    @Child private Unit.UnitToInchesNode unitsToInches = Unit.UnitToInchesNode.create();
+    @Child private IsRelativeUnitNode isRelativeUnit = new IsRelativeUnitNode();
+
+    @TruffleBoundary
+    public RList doSetViewPort(RList pushedViewPort, boolean hasParent, boolean pushing) {
+        GridState gridState = GridContext.getContext().getGridState();
+        Object[] pushedVPData = pushedViewPort.getDataWithoutCopying();
+        if (hasParent && pushing) {
+            RList parent = gridState.getViewPort();
+            pushedVPData[ViewPort.PVP_PARENT] = parent;
+            REnvironment children = (REnvironment) castChildrenEnv.execute(parent.getDataAt(ViewPort.PVP_CHILDREN));
+            safePutToEnv(pushedViewPort, pushedVPData[ViewPort.VP_NAME], children);
+        }
+
+        GridDevice currentDevice = GridContext.getContext().getCurrentDevice();
+        DrawingContext deviceDrawingContext = GridState.getInitialGPar(currentDevice);
+
+        RList parent = asListOrNull(pushedViewPort.getDataAt(ViewPort.PVP_PARENT));
+        boolean doNotRecalculateParent = hasParent && !ViewPort.updateDeviceSizeInVP(parent, currentDevice);
+        calcViewportTransform(pushedViewPort, parent, doNotRecalculateParent, currentDevice, deviceDrawingContext);
+
+        // TODO: clipping
+        pushedVPData[ViewPort.PVP_CLIPRECT] = RDataFactory.createDoubleVector(new double[]{0, 0, 0, 0}, RDataFactory.COMPLETE_VECTOR);
+        pushedVPData[ViewPort.PVP_DEVWIDTHCM] = scalar(Unit.inchesToCm(currentDevice.getWidth()));
+        pushedVPData[ViewPort.PVP_DEVHEIGHTCM] = scalar(Unit.inchesToCm(currentDevice.getHeight()));
+        return pushedViewPort;
+    }
+
+    /**
+     * Calculates and sets the view-port width and height in inches, and transformation matrix and
+     * rotation angle.
+     *
+     * @param viewPort The view-port to be updated.
+     * @param parent The parent of the view-port, null if the view-port is top level.
+     * @param incremental If {@code true} it is assumed that we can just take the transformation
+     *            matrix and other values from the parent without re-calculating them recursively.
+     * @param device This method needs the device in order to convert units
+     * @param deviceDrawingContext This method needs to know the device default drawing context in
+     *            order to convert units for the top level view port
+     */
+    @TruffleBoundary
+    public void calcViewportTransform(RList viewPort, Object parent, boolean incremental, GridDevice device, DrawingContext deviceDrawingContext) {
+        double[][] parentTransform;
+        ViewPortContext parentContext;
+        ViewPortLocation vpl;
+        Size parentSize;
+        DrawingContext drawingContext;
+        double parentAngle;
+        if (parent == null || parent == RNull.instance) {
+            parentTransform = TransformMatrix.identity();
+            parentContext = ViewPortContext.createDefault();
+            parentSize = new Size(device.getWidth(), device.getHeight());
+            vpl = ViewPortLocation.fromViewPort(viewPort);
+            drawingContext = deviceDrawingContext;
+            parentAngle = 0;
+        } else {
+            assert parent instanceof RList : "inconsistent data: parent of a viewport must be a list";
+            RList parentVPList = (RList) parent;
+            Object[] parentData = parentVPList.getDataWithoutCopying();
+            if (!incremental) {
+                calcViewportTransform(parentVPList, parentData[ViewPort.PVP_PARENT], false, device, deviceDrawingContext);
+            }
+            parentSize = new Size(Unit.cmToInches(castScalar(parentData[ViewPort.PVP_WIDTHCM])), Unit.cmToInches(castScalar(parentData[ViewPort.PVP_HEIGHTCM])));
+            parentTransform = fromFlat(castDoubleVector(parentData[ViewPort.PVP_TRANS]).materialize().getDataWithoutCopying());
+            parentContext = ViewPortContext.fromViewPort(parentVPList);
+            parentAngle = asDouble(parentData[ViewPort.PVP_ROTATION]);
+
+            drawingContext = GPar.asDrawingContext(asList(viewPort.getDataAt(ViewPort.PVP_PARENTGPAR)));
+            boolean noLayout = (isNull(viewPort.getDataAt(ViewPort.VP_VALIDLPOSROW)) && isNull(viewPort.getDataAt(ViewPort.VP_VALIDLPOSCOL))) || isNull(parentData[ViewPort.VP_LAYOUT]);
+            if (noLayout) {
+                vpl = ViewPortLocation.fromViewPort(viewPort);
+            } else {
+                vpl = calcViewportLocationFromLayout(getLayoutPos(viewPort, parentVPList), parentVPList, parentSize);
+            }
+        }
+
+        UnitConversionContext conversionCtx = new UnitConversionContext(parentSize, parentContext, device, drawingContext);
+        double xInches = unitsToInches.convertX(vpl.x, 0, conversionCtx);
+        double yInches = unitsToInches.convertY(vpl.y, 0, conversionCtx);
+        double width = unitsToInches.convertWidth(vpl.width, 0, conversionCtx);
+        double height = unitsToInches.convertHeight(vpl.height, 0, conversionCtx);
+
+        if (!Double.isFinite(xInches) || !Double.isFinite(yInches) || !Double.isFinite(width) || !Double.isFinite(height)) {
+            throw error(Message.GENERIC, "non-finite location and/or size for viewport");
+        }
+
+        double xadj = GridUtils.justification(width, vpl.hjust);
+        double yadj = GridUtils.justification(height, vpl.vjust);
+
+        // Produce transform for this viewport
+        double[][] thisLocation = translation(xInches, yInches);
+        double[][] thisJustification = translation(xadj, yadj);
+        // Position relative to origin of rotation THEN rotate.
+        double viewPortAngle = asDouble(viewPort.getDataAt(ViewPort.VP_ANGLE));
+        double[][] thisRotation = rotation(viewPortAngle);
+        double[][] tempTransform = multiply(thisJustification, thisRotation);
+        // Translate to bottom-left corner.
+        double[][] thisTransform = multiply(tempTransform, thisLocation);
+        // Combine with parent's transform
+        double[][] transform = multiply(thisTransform, parentTransform);
+
+        // Sum up the rotation angles
+        double rotationAngle = parentAngle + viewPortAngle;
+
+        // Finally, allocate the rows and columns for this viewport's layout if it has one
+        if (!isNull(viewPort.getDataAt(ViewPort.VP_LAYOUT))) {
+            ViewPortContext vpCtx = ViewPortContext.fromViewPort(viewPort);
+            DrawingContext drawingCtx = GPar.asDrawingContext(asList(viewPort.getDataAt(ViewPort.PVP_GPAR)));
+            calcViewPortLayout(viewPort, new Size(width, height), vpCtx, device, drawingCtx);
+        }
+
+        Object[] viewPortData = viewPort.getDataWithoutCopying();
+        viewPortData[ViewPort.PVP_WIDTHCM] = scalar(Unit.inchesToCm(width));
+        viewPortData[ViewPort.PVP_HEIGHTCM] = scalar(Unit.inchesToCm(height));
+        viewPortData[ViewPort.PVP_ROTATION] = scalar(rotationAngle);
+        viewPortData[ViewPort.PVP_TRANS] = RDataFactory.createDoubleVector(flatten(transform), RDataFactory.COMPLETE_VECTOR, new int[]{3, 3});
+    }
+
+    private void calcViewPortLayout(RList viewPort, Size size, ViewPortContext parentVPCtx, GridDevice device, DrawingContext drawingCtx) {
+        LayoutSize layoutSize = LayoutSize.fromViewPort(viewPort);
+        double[] npcWidths = new double[layoutSize.ncol];
+        double[] npcHeights = new double[layoutSize.nrow];
+        boolean[] relativeWidths = new boolean[layoutSize.ncol];
+        boolean[] relativeHeights = new boolean[layoutSize.nrow];
+        UnitConversionContext conversionCtx = new UnitConversionContext(size, parentVPCtx, device, drawingCtx);
+
+        // For both dimensions we find out which units are other than "null" for those we can
+        // immediately calculate the physical size in npcWidth/npcHeights. The reducedWidth/Height
+        // gives us how much of width/height is left after we take up the space by physical units
+        Object[] layoutData = asList(viewPort.getDataAt(ViewPort.VP_LAYOUT)).getDataWithoutCopying();
+        RAbstractContainer layoutWidths = asAbstractContainer(layoutData[ViewPort.LAYOUT_WIDTHS]);
+        assert layoutWidths.getLength() == layoutSize.ncol : "inconsistent layout size with layout widths";
+        double reducedWidth = getReducedDimension(layoutWidths, npcWidths, relativeWidths, size.getWidth(), conversionCtx, true);
+
+        RAbstractContainer layoutHeights = asAbstractContainer(layoutData[ViewPort.LAYOUT_HEIGHTS]);
+        assert layoutHeights.getLength() == layoutSize.nrow : "inconsistent layout size with layout height";
+        double reducedHeight = getReducedDimension(layoutHeights, npcHeights, relativeHeights, size.getHeight(), conversionCtx, false);
+
+        // npcHeight (and npcWidth) has some 'holes' in them, at indexes with
+        // relativeHeights[index]==true, we will calculate the values for them now.
+        // Firstly allocate the respected widths/heights and calculate how much space remains
+        RList layoutAsList = asList(viewPort.getDataAt(ViewPort.VP_LAYOUT));
+        int respect = RRuntime.asInteger(layoutAsList.getDataAt(ViewPort.LAYOUT_VRESPECT));
+        int[] layoutRespectMat = ((RAbstractIntVector) layoutAsList.getDataAt(ViewPort.LAYOUT_MRESPECT)).materialize().getDataWithoutCopying();
+        if ((reducedHeight > 0 || reducedWidth > 0) && respect > 0) {
+            double sumRelWidth = sumRelativeDimension(layoutWidths, relativeWidths, parentVPCtx, device, drawingCtx, true);
+            double sumRelHeight = sumRelativeDimension(layoutHeights, relativeHeights, parentVPCtx, device, drawingCtx, false);
+            double tempWidth = reducedWidth;
+            double tempHeight = reducedHeight;
+            double denom;
+            double mult;
+            // Determine whether aspect ratio of available space is bigger or smaller than
+            // aspect ratio of layout
+            if (tempHeight * sumRelWidth > sumRelHeight * tempWidth) {
+                denom = sumRelWidth;
+                mult = tempWidth;
+            } else {
+                denom = sumRelHeight;
+                mult = tempHeight;
+            }
+            for (int i = 0; i < layoutSize.ncol; i++) {
+                if (relativeWidths[i] && colRespected(respect, i, layoutRespectMat, layoutSize)) {
+                    /*
+                     * 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 (sumRelHeight == 0) {
+                        denom = sumRelWidth;
+                        mult = tempWidth;
+                    }
+                    // Build a unit SEXP with a single value and no data
+                    npcWidths[i] = Unit.pureNullUnitValue(layoutWidths, i) / denom * mult;
+                    reducedWidth -= npcWidths[i];
+                }
+            }
+            for (int i = 0; i < layoutSize.nrow; i++) {
+                if (relativeHeights[i] && rowRespected(respect, i, layoutRespectMat, layoutSize)) {
+                    if (sumRelWidth == 0) {
+                        denom = sumRelHeight;
+                        mult = tempHeight;
+                    }
+                    npcHeights[i] = Unit.pureNullUnitValue(layoutHeights, i) / denom * mult;
+                    reducedHeight -= npcHeights[i];
+                }
+            }
+        } else if (respect > 0) {
+            for (int i = 0; i < layoutSize.ncol; i++) {
+                if (relativeWidths[i] && colRespected(respect, i, layoutRespectMat, layoutSize)) {
+                    npcWidths[i] = 0;
+                }
+            }
+            for (int i = 0; i < layoutSize.nrow; i++) {
+                if (relativeHeights[i] && rowRespected(respect, i, layoutRespectMat, layoutSize)) {
+                    npcHeights[i] = 0;
+                }
+            }
+        }
+
+        // Secondly, allocate remaining relative widths and heights in the remaining space
+        allocateRelativeDim(layoutSize, layoutWidths, npcWidths, relativeWidths, reducedWidth, respect, layoutRespectMat, device, drawingCtx, parentVPCtx, true);
+        allocateRelativeDim(layoutSize, layoutHeights, npcHeights, relativeHeights, reducedHeight, respect, layoutRespectMat, device, drawingCtx, parentVPCtx, false);
+
+        // Create the result
+        Object[] vpData = viewPort.getDataWithoutCopying();
+        vpData[ViewPort.PVP_WIDTHS] = RDataFactory.createDoubleVector(npcWidths, RDataFactory.COMPLETE_VECTOR);
+        vpData[ViewPort.PVP_HEIGHTS] = RDataFactory.createDoubleVector(npcHeights, RDataFactory.COMPLETE_VECTOR);
+    }
+
+    private void allocateRelativeDim(LayoutSize layoutSize, RAbstractContainer layoutItems, double[] npcItems, boolean[] relativeItems, double reducedDim, int respect, int[] layoutRespectMat,
+                    GridDevice device, DrawingContext drawingCtx, ViewPortContext parentVPCtx, boolean isWidth) {
+        assert relativeItems.length == npcItems.length;
+        UnitConversionContext layoutModeCtx = new UnitConversionContext(new Size(0, 0), parentVPCtx, device, drawingCtx, 1, 0);
+        double totalUnrespectedSize = 0;
+        if (reducedDim > 0) {
+            for (int i = 0; i < relativeItems.length; i++) {
+                if (relativeItems[i] && !rowColRespected(respect, i, layoutRespectMat, layoutSize, isWidth)) {
+                    totalUnrespectedSize += unitsToInches.convertDimension(layoutItems, i, layoutModeCtx, isWidth);
+                }
+            }
+        }
+        // set the remaining width/height to zero or to proportion of totalUnrespectedSize
+        for (int i = 0; i < relativeItems.length; i++) {
+            if (relativeItems[i] && !rowColRespected(respect, i, layoutRespectMat, layoutSize, isWidth)) {
+                npcItems[i] = 0;
+                if (totalUnrespectedSize > 0) {
+                    // if there was some with left, then totalUnrespectedSize contains sum of it
+                    npcItems[i] = reducedDim * unitsToInches.convertDimension(layoutItems, i, layoutModeCtx, isWidth) / totalUnrespectedSize;
+                }
+            }
+        }
+    }
+
+    private boolean rowColRespected(int respected, int rowOrCol, int[] layoutRespectMat, LayoutSize layoutSize, boolean isColumn) {
+        return isColumn ? colRespected(respected, rowOrCol, layoutRespectMat, layoutSize) : rowRespected(respected, rowOrCol, layoutRespectMat, layoutSize);
+    }
+
+    private boolean rowRespected(int respected, int row, int[] layoutRespectMat, LayoutSize layoutSize) {
+        if (respected == 1) {
+            return true;
+        }
+        for (int i = 0; i < layoutSize.ncol; i++) {
+            if (layoutRespectMat[i * layoutSize.nrow + row] != 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean colRespected(int respected, int col, int[] layoutRespectMat, LayoutSize layoutSize) {
+        if (respected == 1) {
+            return true;
+        }
+        for (int i = 0; i < layoutSize.nrow; i++) {
+            if (layoutRespectMat[col * layoutSize.nrow + i] != 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private double sumRelativeDimension(RAbstractContainer layoutItems, boolean[] relativeItems, ViewPortContext parentVPCtx, GridDevice device, DrawingContext drawingCtx,
+                    boolean isWidth) {
+        UnitConversionContext layoutModeCtx = new UnitConversionContext(new Size(0, 0), parentVPCtx, device, drawingCtx, 1, 0);
+        double totalWidth = 0;
+        for (int i = 0; i < relativeItems.length; i++) {
+            if (relativeItems[i]) {
+                totalWidth += unitsToInches.convertDimension(layoutItems, i, layoutModeCtx, isWidth);
+            }
+        }
+        return totalWidth;
+    }
+
+    private double getReducedDimension(RAbstractContainer layoutItems, double[] npcItems, boolean[] relativeItems, double initialSize, UnitConversionContext conversionCtx,
+                    boolean isWidth) {
+        double reducedSize = initialSize;
+        for (int i = 0; i < npcItems.length; i++) {
+            boolean currIsRel = isRelativeUnit.execute(layoutItems, i);
+            relativeItems[i] = currIsRel;
+            if (!currIsRel) {
+                npcItems[i] = unitsToInches.convertDimension(layoutItems, i, conversionCtx, isWidth);
+                reducedSize -= npcItems[i];
+            }
+        }
+        return reducedSize;
+    }
+
+    private RAbstractDoubleVector castDoubleVector(Object obj) {
+        return (RAbstractDoubleVector) castDoubleVector.execute(obj);
+    }
+
+    private double castScalar(Object obj) {
+        return (double) castScalarDouble.execute(obj);
+    }
+
+    private static RDoubleVector scalar(double val) {
+        return RDataFactory.createDoubleVectorFromScalar(val);
+    }
+
+    private static void safePutToEnv(RList pushedViewPort, Object pushedVPDatum, REnvironment children) {
+        try {
+            children.put(RRuntime.asString(pushedVPDatum), pushedViewPort);
+        } catch (PutException e) {
+            throw RInternalError.shouldNotReachHere("Cannot update children environment in a view port list");
+        }
+    }
+
+    // Note: unlike the GnuR conterpart of this method, we expect the LayoutPos to have the NULL
+    // positions replaced with nrow/ncol already.
+    private ViewPortLocation calcViewportLocationFromLayout(LayoutPos pos, RList parentVP, Size parentSize) {
+        // unlike in GnuR, we maintain parent viewport widths/heights in inches like anything else
+        double[] widths = castDoubleVector(parentVP.getDataAt(ViewPort.PVP_WIDTHS)).materialize().getDataWithoutCopying();
+        double[] heights = castDoubleVector(parentVP.getDataAt(ViewPort.PVP_HEIGHTS)).materialize().getDataWithoutCopying();
+        double totalWidth = sum(widths, 0, pos.layoutSize.ncol);
+        double totalHeight = sum(heights, 0, pos.layoutSize.nrow);
+        double width = sum(widths, pos.colMin, pos.colMax - pos.colMin + 1);
+        double height = sum(heights, pos.rowMin, pos.rowMax - pos.rowMin + 1);
+        double left = parentSize.getWidth() * pos.layoutSize.hjust - totalWidth * pos.layoutSize.hjust + sum(widths, 0, pos.colMin);
+        double bottom = parentSize.getHeight() * pos.layoutSize.vjust + (1 - pos.layoutSize.vjust) * totalHeight - sum(heights, 0, pos.rowMax + 1);
+        ViewPortLocation result = new ViewPortLocation();
+        result.width = newUnit(width, Unit.INCHES);
+        result.height = newUnit(height, Unit.INCHES);
+        result.x = newUnit(left, Unit.INCHES);
+        result.y = newUnit(bottom, Unit.INCHES);
+        result.hjust = result.vjust = 0;
+        return result;
+    }
+
+    private LayoutPos getLayoutPos(RList vp, RList parent) {
+        LayoutSize size = LayoutSize.fromViewPort(parent);
+        Object rowObj = vp.getDataAt(ViewPort.VP_VALIDLPOSROW);
+        int rowMin = 1;
+        int rowMax = size.nrow;
+        if (rowObj instanceof RAbstractIntVector) {
+            rowMin = ((RAbstractIntVector) rowObj).getDataAt(0);
+            rowMax = ((RAbstractIntVector) rowObj).getDataAt(1);
+            if (rowMin < 1 || rowMax > size.nrow) {
+                throw error(Message.GENERIC, "invalid 'layout.pos.row'");
+            }
+        }
+        Object colObj = vp.getDataAt(ViewPort.VP_VALIDLPOSCOL);
+        int colMin = 1;
+        int colMax = size.ncol;
+        if (colObj instanceof RAbstractIntVector) {
+            colMin = ((RAbstractIntVector) colObj).getDataAt(0);
+            colMax = ((RAbstractIntVector) colObj).getDataAt(1);
+            if (colMin < 1 || colMax > size.ncol) {
+                throw error(Message.GENERIC, "invalid 'layout.pos.row'");
+            }
+        }
+        // the indexes in LayoutPos are to be interpreted as 0-based
+        return new LayoutPos(colMin - 1, colMax - 1, rowMin - 1, rowMax - 1, size);
+    }
+
+    private static boolean isNull(Object value) {
+        return value == null || value == RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPortBuiltin.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPortBuiltin.java
new file mode 100644
index 0000000000000000000000000000000000000000..d8b2359414dd6ac40bab72f1e23d2375f8f3d421
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPortBuiltin.java
@@ -0,0 +1,51 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.runtime.builtins.RBehavior;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+
+/**
+ * On the top of what {@link DoSetViewPort} node does, this node sets the resulting view port as the
+ * current view port in the current {@link GridState} instance. This builtin allows us to write some
+ * of the grid code in R.
+ */
+@RBuiltin(name = ".fastr.grid.doSetViewPort", parameterNames = {"vp", "hasParent", "pushing"}, kind = RBuiltinKind.INTERNAL, behavior = RBehavior.COMPLEX)
+public abstract class DoSetViewPortBuiltin extends RBuiltinNode {
+    @Child private DoSetViewPort doSetViewPort = new DoSetViewPort();
+
+    static {
+        Casts casts = new Casts(DoSetViewPortBuiltin.class);
+        casts.arg("vp").mustBe(RList.class);
+        casts.arg("hasParent").mustBe(logicalValue()).asLogicalVector().findFirst().map(toBoolean());
+        casts.arg("pushing").mustBe(logicalValue()).asLogicalVector().findFirst().map(toBoolean());
+    }
+
+    @Specialization
+    RNull doIt(RList pushedVP, boolean hasParent, boolean pushing) {
+        RList vp = doSetViewPort.doSetViewPort(pushedVP, hasParent, pushing);
+        GridContext.getContext().getGridState().setViewPort(vp);
+        return RNull.instance;
+    }
+
+    public static DoSetViewPortBuiltin create() {
+        return DoSetViewPortBuiltinNodeGen.create();
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DrawArrowsNode.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DrawArrowsNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..e177c722b19cefe51bc3679c57b7ba22255b7908
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DrawArrowsNode.java
@@ -0,0 +1,99 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asAbstractContainer;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asDouble;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asInt;
+
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitToInchesNode;
+import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+
+class DrawArrowsNode extends Node {
+    // Structure of an arrow description
+    private static final int ARROWANGLE = 0;
+    private static final int ARROWLENGTH = 1;
+    private static final int ARROWENDS = 2;
+    private static final int ARROWTYPE = 3;
+    // known values of ARROWTYPE
+    private static final int ARROWTYPE_LINES = 1;
+    private static final int ARROWTYPE_POLYGON = 2;
+
+    @Child private UnitToInchesNode unitToInches = Unit.createToInchesNode();
+
+    /**
+     * Draws arrows at the start and end of given lines.
+     *
+     * @param x x-positions of the line(s)
+     * @param y y-positions of the line(s)
+     * @param startIndex consider arrays x,y to start from this index
+     * @param length consider arrays x,y to have this length
+     * @param parentIndex the index of the line we are drawing, this is used for choosing the right
+     *            index into vectors extracted from arrow
+     * @param arrow list with various attributes of the arrow
+     * @param start should we draw start arrow if the arrow list says so. Otherwise never draw it.
+     * @param end should we draw end arrow if the arrow list says so. Otherwise never draw it.
+     * @param conversionCtx needed for unit conversions.
+     */
+    public void drawArrows(double[] x, double[] y, int startIndex, int length, int parentIndex, RList arrow, boolean start, boolean end, UnitConversionContext conversionCtx) {
+        assert x.length == y.length;
+        int endsVal = asInt(arrow.getDataAt(ARROWENDS), parentIndex);
+        boolean first = endsVal != 2;
+        boolean last = endsVal != 1;
+        if ((!first || !start) && (!last || !end)) {
+            // if we are not going to draw any arrow anyway, just finish
+            return;
+        }
+        // extract angle, length in inches and arrow type from 'arrow'
+        double angle = asDouble(arrow.getDataAt(ARROWANGLE), parentIndex);
+        int arrowType = asInt(arrow.getDataAt(ARROWTYPE), parentIndex);
+        RAbstractContainer lengthVec = asAbstractContainer(arrow.getDataAt(ARROWLENGTH));
+        double arrowLength = unitToInches.convertHeight(lengthVec, parentIndex, conversionCtx);
+        arrowLength = Math.max(arrowLength, unitToInches.convertWidth(lengthVec, parentIndex, conversionCtx));
+        // draw the arrows
+        GridDevice device = conversionCtx.device;
+        DrawingContext drawingCtx = conversionCtx.drawingContext;
+        if (first && start) {
+            drawArrow(drawingCtx, device, arrowType, x[startIndex], y[startIndex], x[startIndex + 1], y[startIndex + 1], angle, arrowLength);
+        }
+        if (last && end) {
+            int n = startIndex + length;
+            drawArrow(drawingCtx, device, arrowType, x[n - 1], y[n - 1], x[n - 2], y[n - 2], angle, arrowLength);
+        }
+    }
+
+    private void drawArrow(DrawingContext drawingCtx, GridDevice device, int arrowType, double x0, double y0, double x1, double y1, double angle, double length) {
+        double a = Math.toRadians(angle);
+        double xc = x1 - x0;
+        double yc = y1 - y0;
+        double rot = Math.atan2(yc, xc);
+        double[] vertx = new double[3];
+        double[] verty = new double[3];
+        vertx[0] = x0 + length * Math.cos(rot + a);
+        verty[0] = y0 + length * Math.sin(rot + a);
+        vertx[1] = x0;
+        verty[1] = y0;
+        vertx[2] = x0 + length * Math.cos(rot - a);
+        verty[2] = y0 + length * Math.sin(rot - a);
+        if (arrowType == ARROWTYPE_LINES) {
+            device.drawPolyLines(drawingCtx, vertx, verty, 0, 3);
+        } else if (arrowType == ARROWTYPE_POLYGON) {
+            device.drawPolyLines(drawingCtx, vertx, verty, 0, 3);
+            // TODO: real polygon
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/EdgeDetection.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/EdgeDetection.java
new file mode 100644
index 0000000000000000000000000000000000000000..b9d4070415da9c45435dc397973f973b1bd156c3
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/EdgeDetection.java
@@ -0,0 +1,223 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.fmax;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.fmin;
+import static com.oracle.truffle.r.runtime.nmath.MathConstants.M_PI;
+import static com.oracle.truffle.r.runtime.nmath.RMath.fmax2;
+import static com.oracle.truffle.r.runtime.nmath.RMath.fmin2;
+import static com.oracle.truffle.r.runtime.nmath.TOMS708.fabs;
+
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.RInternalError;
+
+/**
+ * Contains static method related to edge detection for bounds calculations.
+ */
+final class EdgeDetection {
+    private EdgeDetection() {
+        // only static members
+    }
+
+    /**
+     * Do two lines intersect? Algorithm from Paul Bourke
+     * (http://www.swin.edu.au/astronomy/pbourke/geometry/lineline2d/index.html)
+     */
+    private static boolean linesIntersect(double x1, double x2, double x3, double x4,
+                    double y1, double y2, double y3, double y4) {
+        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)))) {
+                        return true;
+                    }
+                } else {
+                    // Compare x-values
+                    if (!((x1 < x3 && fmax2(x1, x2) < fmin2(x3, x4)) || (x3 < x1 && fmax2(x3, x4) < fmin2(x1, x2)))) {
+                        return true;
+                    }
+                }
+            }
+        } else {
+            // ... otherwise, calculate where the lines intersect ...
+            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)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean edgesIntersect(double x1, double x2, double y1, double y2, Rectangle r) {
+        return linesIntersect(x1, x2, r.x[0], r.x[1], y1, y2, r.y[0], r.y[1]) ||
+                        linesIntersect(x1, x2, r.x[1], r.x[2], y1, y2, r.y[1], r.y[2]) ||
+                        linesIntersect(x1, x2, r.x[2], r.x[3], y1, y2, r.y[2], r.y[3]) ||
+                        linesIntersect(x1, x2, r.x[3], r.x[0], y1, y2, r.y[3], r.y[0]);
+    }
+
+    static Point rectEdge(double xmin, double ymin, double xmax, double ymax, double theta) {
+        double xm = (xmin + xmax) / 2;
+        double ym = (ymin + ymax) / 2;
+        double dx = (xmax - xmin) / 2;
+        double dy = (ymax - ymin) / 2;
+        /*
+         * GNUR fixme: Special case 0 width or 0 height
+         */
+        // Special case angles
+        if (theta == 0) {
+            return new Point(xmax, ym);
+        } else if (theta == 270) {
+            return new Point(xm, ymin);
+        } else if (theta == 180) {
+            return new Point(xmin, ym);
+        } else if (theta == 90) {
+            return new Point(xm, ymax);
+        } else {
+            double cutoff = dy / dx;
+            double angle = theta / 180 * M_PI;
+            double tanTheta = Math.tan(angle);
+            double cosTheta = Math.cos(angle);
+            double sinTheta = Math.sin(angle);
+            if (fabs(tanTheta) < cutoff) { /* Intersect with side */
+                if (cosTheta > 0) { /* Right side */
+                    return new Point(xmax, ym + tanTheta * dx);
+                } else { /* Left side */
+                    return new Point(xmin, ym - tanTheta * dx);
+                }
+            } else { /* Intersect with top/bottom */
+                if (sinTheta > 0) { /* Top */
+                    return new Point(ymax, xm + dy / tanTheta);
+                } else { /* Bottom */
+                    return new Point(ymin, xm - dy / tanTheta);
+                }
+            }
+        }
+    }
+
+    static Point polygonEdge(double[] x, double[] y, int n, double theta) {
+        // centre of the polygon
+        double xmin = fmin(Double.MAX_VALUE, x);
+        double xmax = fmax(Double.MIN_VALUE, x);
+        double ymin = fmin(Double.MAX_VALUE, y);
+        double ymax = fmax(Double.MIN_VALUE, y);
+        double xm = (xmin + xmax) / 2;
+        double ym = (ymin + ymax) / 2;
+
+        // Special case zero-width or zero-height
+        if (fabs(xmin - xmax) < 1e-6) {
+            double resultY = theta == 90 ? ymax : theta == 270 ? ymin : ym;
+            return new Point(xmin, resultY);
+        }
+        if (fabs(ymin - ymax) < 1e-6) {
+            double resultX = theta == 0 ? xmax : theta == 180 ? xmin : xm;
+            return new Point(resultX, ymin);
+        }
+
+        /*
+         * Find edge that intersects line from centre at angle theta
+         */
+        boolean found = false;
+        double angle = theta / 180 * M_PI;
+        double vangle1;
+        double vangle2;
+        int v1 = 0;
+        int v2 = 1;
+        for (int 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 = Math.atan2(y[v1] - ym, x[v1] - xm);
+            if (vangle1 < 0) {
+                vangle1 = vangle1 + 2 * M_PI;
+            }
+            vangle2 = Math.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 = true;
+                break;
+            }
+        }
+        /*
+         * Find intersection point of "line from centre to bounding rect" and edge
+         */
+        if (!found) {
+            throw RError.error(RError.NO_CALLER, Message.GENERIC, "polygon edge not found");
+        }
+        double x3 = x[v1];
+        double y3 = y[v1];
+        double x4 = x[v2];
+        double y4 = y[v2];
+        Point tmp = rectEdge(xmin, ymin, xmax, ymax, theta);
+        double x2 = tmp.x;
+        double y2 = tmp.y;
+        double numa = ((x4 - x3) * (ym - y3) - (y4 - y3) * (xm - x3));
+        double denom = ((y4 - y3) * (x2 - xm) - (x4 - x3) * (y2 - ym));
+        double ua = numa / denom;
+        if (!Double.isFinite(ua)) {
+            /*
+             * Should only happen if lines are parallel, which shouldn't happen! Unless, perhaps the
+             * polygon has zero extent vertically or horizontally ... ?
+             */
+            throw RInternalError.shouldNotReachHere("polygon edge not found (zero-width or zero-height?)");
+        }
+        /*
+         * numb = ((x2 - x1)*(y1 - y3) - (y2 - y1)*(x1 - x3)); ub = numb/denom;
+         */
+        return new Point(xm + ua * (x2 - xm), ym + ua * (y2 - ym));
+    }
+
+    /**
+     * An arbitrarily-oriented rectangle. The vertices are assumed to be in order going
+     * anticlockwise around the rectangle.
+     */
+    public static final class Rectangle {
+        public final double[] x;
+        public final double[] y;
+
+        Rectangle(Point p1, Point p2, Point p3, Point p4) {
+            x = new double[]{p1.x, p2.x, p3.x, p4.x};
+            y = new double[]{p1.y, p2.y, p3.y, p4.y};
+        }
+
+        public boolean intersects(Rectangle r2) {
+            return edgesIntersect(this.x[0], this.x[1], this.y[0], this.y[1], r2) ||
+                            edgesIntersect(this.x[1], this.x[2], this.y[1], this.y[2], r2) ||
+                            edgesIntersect(this.x[2], this.x[3], this.y[2], this.y[3], r2) ||
+                            edgesIntersect(this.x[3], this.x[0], this.y[3], this.y[0], r2);
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/FastRGridExternalLookup.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/FastRGridExternalLookup.java
new file mode 100644
index 0000000000000000000000000000000000000000..5863de72a47db700e3e773967266d68c1312da39
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/FastRGridExternalLookup.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.r.library.fastrGrid.grDevices.InitWindowedDevice;
+import com.oracle.truffle.r.library.fastrGrid.graphics.CPar;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.nodes.builtin.RInternalCodeBuiltinNode;
+import com.oracle.truffle.r.runtime.RInternalCode;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+
+/**
+ * Implements the lookup for externals replaced by the FastR grid package.
+ */
+public class FastRGridExternalLookup {
+
+    public static RExternalBuiltinNode lookupDotExternal(String name) {
+        switch (name) {
+            case "devholdflush":
+                return new IgnoredGridExternal(RNull.instance);
+            case "PDF":
+                return new IgnoredGridExternal(RNull.instance);
+            default:
+                return null;
+        }
+    }
+
+    public static RExternalBuiltinNode lookupDotExternal2(String name) {
+        switch (name) {
+            case "C_par":
+                return new CPar();
+            case "X11":
+                return new InitWindowedDevice();
+            default:
+                return null;
+        }
+    }
+
+    public static RExternalBuiltinNode lookupDotCall(String name) {
+        switch (name) {
+            case "L_gridDirty":
+                return new LGridDirty();
+            case "L_initGrid":
+                return LInitGrid.create();
+            case "L_newpage":
+                return new LNewPage();
+            case "L_convert":
+                return LConvert.create();
+
+            // Viewport management
+            case "L_upviewport":
+                return LUpViewPort.create();
+            case "L_initViewportStack":
+                return new LInitViewPortStack();
+            case "L_unsetviewport":
+                return LUnsetViewPort.create();
+            case "L_setviewport":
+            case "L_downviewport":
+                return getExternalFastRGridBuiltinNode(name);
+
+            // Drawing primitives
+            case "L_rect":
+                return LRect.create();
+            case "L_lines":
+                return LLines.create();
+            case "L_polygon":
+                return LPolygon.create();
+            case "L_text":
+                return LText.create();
+            case "L_textBounds":
+                return LTextBounds.create();
+            case "L_segments":
+                return LSegments.create();
+            case "L_circle":
+                return LCircle.create();
+            case "L_points":
+                return LPoints.create();
+
+            // Simple grid state access
+            case "L_getGPar":
+                return new GridStateGetNode(GridState::getGpar);
+            case "L_setGPar":
+                return GridStateSetNode.create((state, val) -> state.setGpar((RList) val));
+            case "L_getCurrentGrob":
+                return new GridStateGetNode(GridState::getCurrentGrob);
+            case "L_setCurrentGrob":
+                return GridStateSetNode.create(GridState::setCurrentGrob);
+            case "L_currentViewport":
+                return new GridStateGetNode(GridState::getViewPort);
+            case "L_initGPar":
+                return new LInitGPar();
+
+            // Display list stuff: not implemented atm
+            case "L_getDisplayList":
+                return new IgnoredGridExternal(RDataFactory.createList());
+            case "L_getDLindex":
+                return new IgnoredGridExternal(0);
+            case "L_getDLon":
+            case "L_getEngineDLon":
+                return new IgnoredGridExternal(RRuntime.LOGICAL_FALSE);
+            case "L_initDisplayList":
+            case "L_newpagerecording":
+            case "L_setDisplayList":
+            case "L_setDLelt":
+            case "L_setDLindex":
+            case "L_setDLon":
+                return new IgnoredGridExternal(RNull.instance);
+
+            // These methods do not use graphics system or any global state. For now,
+            // we can re-use the native implementation, which in the future should be rewritten
+            // to managed code.
+            case "L_validUnits":
+                return null;
+            default:
+                if (name.startsWith("L_")) {
+                    throw RInternalError.shouldNotReachHere("Unimplemented grid external " + name);
+                } else {
+                    return null;
+                }
+        }
+    }
+
+    private static RExternalBuiltinNode getExternalFastRGridBuiltinNode(String name) {
+        return new RInternalCodeBuiltinNode(RContext.getInstance(), "grid", RInternalCode.loadSourceRelativeTo(LInitGrid.class, "fastrGrid.R"), name);
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java
new file mode 100644
index 0000000000000000000000000000000000000000..41d785ee1fcffc9bfc4c4e39a4a719d10aadb226
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java
@@ -0,0 +1,212 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asAbstractContainer;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asDouble;
+
+import java.util.Arrays;
+
+import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
+import com.oracle.truffle.r.library.fastrGrid.device.GridColor;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+
+/**
+ * In the context of grid package, GPar is a list that contains the parameters for the drawing, like
+ * line style, color, etc. This class wraps the list and provides type-safe access to its elements.
+ */
+public final class GPar {
+    private static final int GP_FILL = 0;
+    private static final int GP_COL = 1;
+    private static final int GP_GAMMA = 2;
+    private static final int GP_LTY = 3;
+    private static final int GP_LWD = 4;
+
+    /**
+     * Multiplier added to the final font size.
+     */
+    private static final int GP_CEX = 5;
+
+    /**
+     * Font size in points, however, the real font size will be this multiplied by {@link #GP_CEX}.
+     */
+    private static final int GP_FONTSIZE = 6;
+
+    /**
+     * Size of the line in terms of a multiply of "one line". The final real size of a line is
+     * fontsize*cex*lineheight.
+     */
+    private static final int GP_LINEHEIGHT = 7;
+    private static final int GP_FONT = 8;
+    private static final int GP_FONTFAMILY = 9;
+    private static final int GP_ALPHA = 10;
+    private static final int GP_LINEEND = 11;
+    private static final int GP_LINEJOIN = 12;
+    private static final int GP_LINEMITRE = 13;
+    private static final int GP_LEX = 14;
+    private static final int GP_FONTFACE = 15;
+    private static final int GP_LENGTH = 16;
+    private static final String[] NAMES = new String[]{
+                    "fill",
+                    "col",
+                    "gamma",
+                    "lty",
+                    "lwd",
+                    "cex",
+                    "fontsize",
+                    "lineheight",
+                    "font",
+                    "fontfamily",
+                    "alpha",
+                    "lineend",
+                    "linejoin",
+                    "linemitre",
+                    "lex",
+                    "fontface"  // TODO: could not find this name in grid sources
+    };
+    private static final RStringVector NAMES_VECTOR = (RStringVector) RDataFactory.createStringVector(NAMES, RDataFactory.COMPLETE_VECTOR).makeSharedPermanent();
+
+    public static RList createNew() {
+        Object[] data = new Object[GP_LENGTH];
+        Arrays.fill(data, RNull.instance);
+        data[GP_FILL] = "transparent";
+        data[GP_COL] = "black";
+        data[GP_GAMMA] = newDoubleVec(0);
+        data[GP_LTY] = "solid";
+        data[GP_LWD] = newDoubleVec(1);
+        data[GP_CEX] = newDoubleVec(1);
+        data[GP_FONTSIZE] = newDoubleVec(16);
+        data[GP_LINEHEIGHT] = newDoubleVec(1.2);
+        data[GP_FONT] = RDataFactory.createIntVectorFromScalar(1);  // TODO: font constants?
+        data[GP_FONTFAMILY] = ""; // means default font (probably)
+        data[GP_ALPHA] = newDoubleVec(1);
+        data[GP_LINEEND] = "round";
+        data[GP_LINEJOIN] = "round";
+        data[GP_LINEMITRE] = newDoubleVec(10);
+        data[GP_LEX] = newDoubleVec(1);
+        return RDataFactory.createList(data, NAMES_VECTOR);
+    }
+
+    public static double getCex(RList gpar) {
+        return asDouble(gpar.getDataAt(GP_CEX));
+    }
+
+    public static DrawingContext asDrawingContext(RList gpar) {
+        return new GParDrawingContext(gpar);
+    }
+
+    private static RAbstractDoubleVector newDoubleVec(double val) {
+        return RDataFactory.createDoubleVectorFromScalar(val);
+    }
+
+    private static final class GParDrawingContext implements DrawingContext {
+        private final Object[] data;
+
+        private GParDrawingContext(RList list) {
+            data = list.getDataWithoutCopying();
+        }
+
+        @Override
+        public GridLineType getLineType() {
+            Object lty = data[GP_LTY];
+            if (lty == null || lty == RNull.instance) {
+                return GridLineType.SOLID;
+            }
+            String name = RRuntime.asString(lty);
+            if (name != null) {
+                return lineTypeFromName(name);
+            }
+            RAbstractContainer ltyVec = asAbstractContainer(lty);
+            int num;
+            if (ltyVec.getLength() == 0) {
+                num = RRuntime.INT_NA;
+            } else if (ltyVec instanceof RAbstractDoubleVector) {
+                double realVal = ((RAbstractDoubleVector) ltyVec).getDataAt(0);
+                num = RRuntime.isNA(realVal) ? RRuntime.INT_NA : (int) realVal;
+            } else if (ltyVec instanceof RAbstractIntVector) {
+                num = ((RAbstractIntVector) ltyVec).getDataAt(0);
+            } else {
+                num = RRuntime.INT_NA;
+            }
+
+            if (RRuntime.isNA(num)) {
+                throw RError.error(RError.NO_CALLER, Message.GENERIC, "Invalid line type.");
+            }
+            return GridLineType.fromInt(num);
+        }
+
+        @Override
+        public GridColor getColor() {
+            return getGridColor(GP_COL);
+        }
+
+        @Override
+        public void setColor(GridColor color) {
+            data[GP_COL] = GridColorUtils.gridColorToRString(color);
+        }
+
+        @Override
+        public double getFontSize() {
+            return asDouble(data[GP_FONTSIZE]) * asDouble(data[GP_CEX]);
+        }
+
+        @Override
+        public double getLineHeight() {
+            return asDouble(data[GP_LINEHEIGHT]);
+        }
+
+        @Override
+        public GridColor getFillColor() {
+            return getGridColor(GP_FILL);
+        }
+
+        @Override
+        public void setFillColor(GridColor color) {
+            data[GP_FILL] = GridColorUtils.gridColorToRString(color);
+        }
+
+        private GridColor getGridColor(int index) {
+            return GridColorUtils.gridColorFromString(RRuntime.asString(data[index]));
+        }
+
+        private GridLineType lineTypeFromName(String name) {
+            switch (name) {
+                case "solid":
+                    return GridLineType.SOLID;
+                case "dashed":
+                    return GridLineType.DASHED;
+                case "dotted":
+                    return GridLineType.DOTTED;
+                case "dotdashed":
+                    return GridLineType.DOTDASHED;
+                case "longdash":
+                    return GridLineType.LONGDASH;
+                case "twodash":
+                    return GridLineType.TWODASH;
+                case "blank":
+                    return GridLineType.BLANK;
+                default:
+                    // TODO: implement hex digits as line style
+                    throw RError.error(RError.NO_CALLER, Message.GENERIC, "Unexpected line type '" + name + "'.");
+            }
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridColorUtils.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridColorUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..1fc036ea84d2814955a45b3294f477d44dd81581
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridColorUtils.java
@@ -0,0 +1,745 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (c) 1997-2014, The R Core Team
+ * Copyright (c) 2003, The R Foundation
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import java.util.HashMap;
+import java.util.Locale;
+
+import com.oracle.truffle.r.library.fastrGrid.device.GridColor;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
+
+public class GridColorUtils {
+    private static HashMap<String, Object> synonymToColor;
+
+    /**
+     * Converts the representation of color used within R, e.g. as value for
+     * {@code gpar(col='value')}, to our internal representation that grid device should understand.
+     * The acceptable color formats are: name of known color, HTML style hex value, and HTML style
+     * hex value including alpha.
+     */
+    public static GridColor gridColorFromString(String value) {
+        if (value.startsWith("#") && (value.length() == 7 || value.length() == 9)) {
+            return parseHex(value);
+        }
+
+        Object result = findByName(value);
+        if (result == null) {
+            throw RError.error(RError.NO_CALLER, Message.GENERIC, "Invalid color '" + value + "'.");
+        }
+        if (result instanceof String) {
+            return parseHex((String) result);
+        }
+        assert result instanceof GridColor : "synonyms map should only contain Strings and GridColors";
+        return (GridColor) result;
+    }
+
+    public static String gridColorToRString(GridColor color) {
+        if (color.getAlpha() == GridColor.OPAQUE_ALPHA) {
+            return String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue());
+        }
+        return String.format("#%02x%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
+    }
+
+    private static GridColor parseHex(String value) {
+        // hex format, e.g. #ffffff
+        int red = Integer.parseInt(value.substring(1, 3), 16);
+        int green = Integer.parseInt(value.substring(3, 5), 16);
+        int blue = Integer.parseInt(value.substring(5, 7), 16);
+        int alpha = GridColor.OPAQUE_ALPHA;
+        if (value.length() == 9) {
+            alpha = Integer.parseInt(value.substring(7, 9), 16);
+        }
+        return new GridColor(red, green, blue, alpha);
+    }
+
+    private static Object findByName(String synonym) {
+        if (synonymToColor == null) {
+            initialize();
+        }
+        return synonymToColor.get(normalizeColorName(synonym));
+    }
+
+    // GnuR compares the user given color name to the dictionary ignoring spaces and case. We remove
+    // the spaces and make it lowercase and then use normal string comparison.
+    private static String normalizeColorName(String synonym) {
+        boolean isNormalized = true;
+        for (int i = 0; i < synonym.length(); i++) {
+            char c = synonym.charAt(i);
+            isNormalized &= (!Character.isAlphabetic(c) || Character.isLowerCase(c)) && c != ' ';
+        }
+        return isNormalized ? synonym : synonym.replace(" ", "").toLowerCase(Locale.ROOT);
+    }
+
+    private static void initialize() {
+        synonymToColor = new HashMap<>(700);
+        synonymToColor.put("transparent", GridColor.TRANSPARENT);
+        synonymToColor.put("NA", GridColor.TRANSPARENT);
+        synonymToColor.put("white", "#FFFFFF");
+        synonymToColor.put("aliceblue", "#F0F8FF");
+        synonymToColor.put("antiquewhite", "#FAEBD7");
+        synonymToColor.put("antiquewhite1", "#FFEFDB");
+        synonymToColor.put("antiquewhite2", "#EEDFCC");
+        synonymToColor.put("antiquewhite3", "#CDC0B0");
+        synonymToColor.put("antiquewhite4", "#8B8378");
+        synonymToColor.put("aquamarine", "#7FFFD4");
+        synonymToColor.put("aquamarine1", "#7FFFD4");
+        synonymToColor.put("aquamarine2", "#76EEC6");
+        synonymToColor.put("aquamarine3", "#66CDAA");
+        synonymToColor.put("aquamarine4", "#458B74");
+        synonymToColor.put("azure", "#F0FFFF");
+        synonymToColor.put("azure1", "#F0FFFF");
+        synonymToColor.put("azure2", "#E0EEEE");
+        synonymToColor.put("azure3", "#C1CDCD");
+        synonymToColor.put("azure4", "#838B8B");
+        synonymToColor.put("beige", "#F5F5DC");
+        synonymToColor.put("bisque", "#FFE4C4");
+        synonymToColor.put("bisque1", "#FFE4C4");
+        synonymToColor.put("bisque2", "#EED5B7");
+        synonymToColor.put("bisque3", "#CDB79E");
+        synonymToColor.put("bisque4", "#8B7D6B");
+        synonymToColor.put("black", "#000000");
+        synonymToColor.put("blanchedalmond", "#FFEBCD");
+        synonymToColor.put("blue", "#0000FF");
+        synonymToColor.put("blue1", "#0000FF");
+        synonymToColor.put("blue2", "#0000EE");
+        synonymToColor.put("blue3", "#0000CD");
+        synonymToColor.put("blue4", "#00008B");
+        synonymToColor.put("blueviolet", "#8A2BE2");
+        synonymToColor.put("brown", "#A52A2A");
+        synonymToColor.put("brown1", "#FF4040");
+        synonymToColor.put("brown2", "#EE3B3B");
+        synonymToColor.put("brown3", "#CD3333");
+        synonymToColor.put("brown4", "#8B2323");
+        synonymToColor.put("burlywood", "#DEB887");
+        synonymToColor.put("burlywood1", "#FFD39B");
+        synonymToColor.put("burlywood2", "#EEC591");
+        synonymToColor.put("burlywood3", "#CDAA7D");
+        synonymToColor.put("burlywood4", "#8B7355");
+        synonymToColor.put("cadetblue", "#5F9EA0");
+        synonymToColor.put("cadetblue1", "#98F5FF");
+        synonymToColor.put("cadetblue2", "#8EE5EE");
+        synonymToColor.put("cadetblue3", "#7AC5CD");
+        synonymToColor.put("cadetblue4", "#53868B");
+        synonymToColor.put("chartreuse", "#7FFF00");
+        synonymToColor.put("chartreuse1", "#7FFF00");
+        synonymToColor.put("chartreuse2", "#76EE00");
+        synonymToColor.put("chartreuse3", "#66CD00");
+        synonymToColor.put("chartreuse4", "#458B00");
+        synonymToColor.put("chocolate", "#D2691E");
+        synonymToColor.put("chocolate1", "#FF7F24");
+        synonymToColor.put("chocolate2", "#EE7621");
+        synonymToColor.put("chocolate3", "#CD661D");
+        synonymToColor.put("chocolate4", "#8B4513");
+        synonymToColor.put("coral", "#FF7F50");
+        synonymToColor.put("coral1", "#FF7256");
+        synonymToColor.put("coral2", "#EE6A50");
+        synonymToColor.put("coral3", "#CD5B45");
+        synonymToColor.put("coral4", "#8B3E2F");
+        synonymToColor.put("cornflowerblue", "#6495ED");
+        synonymToColor.put("cornsilk", "#FFF8DC");
+        synonymToColor.put("cornsilk1", "#FFF8DC");
+        synonymToColor.put("cornsilk2", "#EEE8CD");
+        synonymToColor.put("cornsilk3", "#CDC8B1");
+        synonymToColor.put("cornsilk4", "#8B8878");
+        synonymToColor.put("cyan", "#00FFFF");
+        synonymToColor.put("cyan1", "#00FFFF");
+        synonymToColor.put("cyan2", "#00EEEE");
+        synonymToColor.put("cyan3", "#00CDCD");
+        synonymToColor.put("cyan4", "#008B8B");
+        synonymToColor.put("darkblue", "#00008B");
+        synonymToColor.put("darkcyan", "#008B8B");
+        synonymToColor.put("darkgoldenrod", "#B8860B");
+        synonymToColor.put("darkgoldenrod1", "#FFB90F");
+        synonymToColor.put("darkgoldenrod2", "#EEAD0E");
+        synonymToColor.put("darkgoldenrod3", "#CD950C");
+        synonymToColor.put("darkgoldenrod4", "#8B6508");
+        synonymToColor.put("darkgray", "#A9A9A9");
+        synonymToColor.put("darkgreen", "#006400");
+        synonymToColor.put("darkgrey", "#A9A9A9");
+        synonymToColor.put("darkkhaki", "#BDB76B");
+        synonymToColor.put("darkmagenta", "#8B008B");
+        synonymToColor.put("darkolivegreen", "#556B2F");
+        synonymToColor.put("darkolivegreen1", "#CAFF70");
+        synonymToColor.put("darkolivegreen2", "#BCEE68");
+        synonymToColor.put("darkolivegreen3", "#A2CD5A");
+        synonymToColor.put("darkolivegreen4", "#6E8B3D");
+        synonymToColor.put("darkorange", "#FF8C00");
+        synonymToColor.put("darkorange1", "#FF7F00");
+        synonymToColor.put("darkorange2", "#EE7600");
+        synonymToColor.put("darkorange3", "#CD6600");
+        synonymToColor.put("darkorange4", "#8B4500");
+        synonymToColor.put("darkorchid", "#9932CC");
+        synonymToColor.put("darkorchid1", "#BF3EFF");
+        synonymToColor.put("darkorchid2", "#B23AEE");
+        synonymToColor.put("darkorchid3", "#9A32CD");
+        synonymToColor.put("darkorchid4", "#68228B");
+        synonymToColor.put("darkred", "#8B0000");
+        synonymToColor.put("darksalmon", "#E9967A");
+        synonymToColor.put("darkseagreen", "#8FBC8F");
+        synonymToColor.put("darkseagreen1", "#C1FFC1");
+        synonymToColor.put("darkseagreen2", "#B4EEB4");
+        synonymToColor.put("darkseagreen3", "#9BCD9B");
+        synonymToColor.put("darkseagreen4", "#698B69");
+        synonymToColor.put("darkslateblue", "#483D8B");
+        synonymToColor.put("darkslategray", "#2F4F4F");
+        synonymToColor.put("darkslategray1", "#97FFFF");
+        synonymToColor.put("darkslategray2", "#8DEEEE");
+        synonymToColor.put("darkslategray3", "#79CDCD");
+        synonymToColor.put("darkslategray4", "#528B8B");
+        synonymToColor.put("darkslategrey", "#2F4F4F");
+        synonymToColor.put("darkturquoise", "#00CED1");
+        synonymToColor.put("darkviolet", "#9400D3");
+        synonymToColor.put("deeppink", "#FF1493");
+        synonymToColor.put("deeppink1", "#FF1493");
+        synonymToColor.put("deeppink2", "#EE1289");
+        synonymToColor.put("deeppink3", "#CD1076");
+        synonymToColor.put("deeppink4", "#8B0A50");
+        synonymToColor.put("deepskyblue", "#00BFFF");
+        synonymToColor.put("deepskyblue1", "#00BFFF");
+        synonymToColor.put("deepskyblue2", "#00B2EE");
+        synonymToColor.put("deepskyblue3", "#009ACD");
+        synonymToColor.put("deepskyblue4", "#00688B");
+        synonymToColor.put("dimgray", "#696969");
+        synonymToColor.put("dimgrey", "#696969");
+        synonymToColor.put("dodgerblue", "#1E90FF");
+        synonymToColor.put("dodgerblue1", "#1E90FF");
+        synonymToColor.put("dodgerblue2", "#1C86EE");
+        synonymToColor.put("dodgerblue3", "#1874CD");
+        synonymToColor.put("dodgerblue4", "#104E8B");
+        synonymToColor.put("firebrick", "#B22222");
+        synonymToColor.put("firebrick1", "#FF3030");
+        synonymToColor.put("firebrick2", "#EE2C2C");
+        synonymToColor.put("firebrick3", "#CD2626");
+        synonymToColor.put("firebrick4", "#8B1A1A");
+        synonymToColor.put("floralwhite", "#FFFAF0");
+        synonymToColor.put("forestgreen", "#228B22");
+        synonymToColor.put("gainsboro", "#DCDCDC");
+        synonymToColor.put("ghostwhite", "#F8F8FF");
+        synonymToColor.put("gold", "#FFD700");
+        synonymToColor.put("gold1", "#FFD700");
+        synonymToColor.put("gold2", "#EEC900");
+        synonymToColor.put("gold3", "#CDAD00");
+        synonymToColor.put("gold4", "#8B7500");
+        synonymToColor.put("goldenrod", "#DAA520");
+        synonymToColor.put("goldenrod1", "#FFC125");
+        synonymToColor.put("goldenrod2", "#EEB422");
+        synonymToColor.put("goldenrod3", "#CD9B1D");
+        synonymToColor.put("goldenrod4", "#8B6914");
+        synonymToColor.put("gray", "#BEBEBE");
+        synonymToColor.put("gray0", "#000000");
+        synonymToColor.put("gray1", "#030303");
+        synonymToColor.put("gray2", "#050505");
+        synonymToColor.put("gray3", "#080808");
+        synonymToColor.put("gray4", "#0A0A0A");
+        synonymToColor.put("gray5", "#0D0D0D");
+        synonymToColor.put("gray6", "#0F0F0F");
+        synonymToColor.put("gray7", "#121212");
+        synonymToColor.put("gray8", "#141414");
+        synonymToColor.put("gray9", "#171717");
+        synonymToColor.put("gray10", "#1A1A1A");
+        synonymToColor.put("gray11", "#1C1C1C");
+        synonymToColor.put("gray12", "#1F1F1F");
+        synonymToColor.put("gray13", "#212121");
+        synonymToColor.put("gray14", "#242424");
+        synonymToColor.put("gray15", "#262626");
+        synonymToColor.put("gray16", "#292929");
+        synonymToColor.put("gray17", "#2B2B2B");
+        synonymToColor.put("gray18", "#2E2E2E");
+        synonymToColor.put("gray19", "#303030");
+        synonymToColor.put("gray20", "#333333");
+        synonymToColor.put("gray21", "#363636");
+        synonymToColor.put("gray22", "#383838");
+        synonymToColor.put("gray23", "#3B3B3B");
+        synonymToColor.put("gray24", "#3D3D3D");
+        synonymToColor.put("gray25", "#404040");
+        synonymToColor.put("gray26", "#424242");
+        synonymToColor.put("gray27", "#454545");
+        synonymToColor.put("gray28", "#474747");
+        synonymToColor.put("gray29", "#4A4A4A");
+        synonymToColor.put("gray30", "#4D4D4D");
+        synonymToColor.put("gray31", "#4F4F4F");
+        synonymToColor.put("gray32", "#525252");
+        synonymToColor.put("gray33", "#545454");
+        synonymToColor.put("gray34", "#575757");
+        synonymToColor.put("gray35", "#595959");
+        synonymToColor.put("gray36", "#5C5C5C");
+        synonymToColor.put("gray37", "#5E5E5E");
+        synonymToColor.put("gray38", "#616161");
+        synonymToColor.put("gray39", "#636363");
+        synonymToColor.put("gray40", "#666666");
+        synonymToColor.put("gray41", "#696969");
+        synonymToColor.put("gray42", "#6B6B6B");
+        synonymToColor.put("gray43", "#6E6E6E");
+        synonymToColor.put("gray44", "#707070");
+        synonymToColor.put("gray45", "#737373");
+        synonymToColor.put("gray46", "#757575");
+        synonymToColor.put("gray47", "#787878");
+        synonymToColor.put("gray48", "#7A7A7A");
+        synonymToColor.put("gray49", "#7D7D7D");
+        synonymToColor.put("gray50", "#7F7F7F");
+        synonymToColor.put("gray51", "#828282");
+        synonymToColor.put("gray52", "#858585");
+        synonymToColor.put("gray53", "#878787");
+        synonymToColor.put("gray54", "#8A8A8A");
+        synonymToColor.put("gray55", "#8C8C8C");
+        synonymToColor.put("gray56", "#8F8F8F");
+        synonymToColor.put("gray57", "#919191");
+        synonymToColor.put("gray58", "#949494");
+        synonymToColor.put("gray59", "#969696");
+        synonymToColor.put("gray60", "#999999");
+        synonymToColor.put("gray61", "#9C9C9C");
+        synonymToColor.put("gray62", "#9E9E9E");
+        synonymToColor.put("gray63", "#A1A1A1");
+        synonymToColor.put("gray64", "#A3A3A3");
+        synonymToColor.put("gray65", "#A6A6A6");
+        synonymToColor.put("gray66", "#A8A8A8");
+        synonymToColor.put("gray67", "#ABABAB");
+        synonymToColor.put("gray68", "#ADADAD");
+        synonymToColor.put("gray69", "#B0B0B0");
+        synonymToColor.put("gray70", "#B3B3B3");
+        synonymToColor.put("gray71", "#B5B5B5");
+        synonymToColor.put("gray72", "#B8B8B8");
+        synonymToColor.put("gray73", "#BABABA");
+        synonymToColor.put("gray74", "#BDBDBD");
+        synonymToColor.put("gray75", "#BFBFBF");
+        synonymToColor.put("gray76", "#C2C2C2");
+        synonymToColor.put("gray77", "#C4C4C4");
+        synonymToColor.put("gray78", "#C7C7C7");
+        synonymToColor.put("gray79", "#C9C9C9");
+        synonymToColor.put("gray80", "#CCCCCC");
+        synonymToColor.put("gray81", "#CFCFCF");
+        synonymToColor.put("gray82", "#D1D1D1");
+        synonymToColor.put("gray83", "#D4D4D4");
+        synonymToColor.put("gray84", "#D6D6D6");
+        synonymToColor.put("gray85", "#D9D9D9");
+        synonymToColor.put("gray86", "#DBDBDB");
+        synonymToColor.put("gray87", "#DEDEDE");
+        synonymToColor.put("gray88", "#E0E0E0");
+        synonymToColor.put("gray89", "#E3E3E3");
+        synonymToColor.put("gray90", "#E5E5E5");
+        synonymToColor.put("gray91", "#E8E8E8");
+        synonymToColor.put("gray92", "#EBEBEB");
+        synonymToColor.put("gray93", "#EDEDED");
+        synonymToColor.put("gray94", "#F0F0F0");
+        synonymToColor.put("gray95", "#F2F2F2");
+        synonymToColor.put("gray96", "#F5F5F5");
+        synonymToColor.put("gray97", "#F7F7F7");
+        synonymToColor.put("gray98", "#FAFAFA");
+        synonymToColor.put("gray99", "#FCFCFC");
+        synonymToColor.put("gray100", "#FFFFFF");
+        synonymToColor.put("green", "#00FF00");
+        synonymToColor.put("green1", "#00FF00");
+        synonymToColor.put("green2", "#00EE00");
+        synonymToColor.put("green3", "#00CD00");
+        synonymToColor.put("green4", "#008B00");
+        synonymToColor.put("greenyellow", "#ADFF2F");
+        synonymToColor.put("grey", "#BEBEBE");
+        synonymToColor.put("grey0", "#000000");
+        synonymToColor.put("grey1", "#030303");
+        synonymToColor.put("grey2", "#050505");
+        synonymToColor.put("grey3", "#080808");
+        synonymToColor.put("grey4", "#0A0A0A");
+        synonymToColor.put("grey5", "#0D0D0D");
+        synonymToColor.put("grey6", "#0F0F0F");
+        synonymToColor.put("grey7", "#121212");
+        synonymToColor.put("grey8", "#141414");
+        synonymToColor.put("grey9", "#171717");
+        synonymToColor.put("grey10", "#1A1A1A");
+        synonymToColor.put("grey11", "#1C1C1C");
+        synonymToColor.put("grey12", "#1F1F1F");
+        synonymToColor.put("grey13", "#212121");
+        synonymToColor.put("grey14", "#242424");
+        synonymToColor.put("grey15", "#262626");
+        synonymToColor.put("grey16", "#292929");
+        synonymToColor.put("grey17", "#2B2B2B");
+        synonymToColor.put("grey18", "#2E2E2E");
+        synonymToColor.put("grey19", "#303030");
+        synonymToColor.put("grey20", "#333333");
+        synonymToColor.put("grey21", "#363636");
+        synonymToColor.put("grey22", "#383838");
+        synonymToColor.put("grey23", "#3B3B3B");
+        synonymToColor.put("grey24", "#3D3D3D");
+        synonymToColor.put("grey25", "#404040");
+        synonymToColor.put("grey26", "#424242");
+        synonymToColor.put("grey27", "#454545");
+        synonymToColor.put("grey28", "#474747");
+        synonymToColor.put("grey29", "#4A4A4A");
+        synonymToColor.put("grey30", "#4D4D4D");
+        synonymToColor.put("grey31", "#4F4F4F");
+        synonymToColor.put("grey32", "#525252");
+        synonymToColor.put("grey33", "#545454");
+        synonymToColor.put("grey34", "#575757");
+        synonymToColor.put("grey35", "#595959");
+        synonymToColor.put("grey36", "#5C5C5C");
+        synonymToColor.put("grey37", "#5E5E5E");
+        synonymToColor.put("grey38", "#616161");
+        synonymToColor.put("grey39", "#636363");
+        synonymToColor.put("grey40", "#666666");
+        synonymToColor.put("grey41", "#696969");
+        synonymToColor.put("grey42", "#6B6B6B");
+        synonymToColor.put("grey43", "#6E6E6E");
+        synonymToColor.put("grey44", "#707070");
+        synonymToColor.put("grey45", "#737373");
+        synonymToColor.put("grey46", "#757575");
+        synonymToColor.put("grey47", "#787878");
+        synonymToColor.put("grey48", "#7A7A7A");
+        synonymToColor.put("grey49", "#7D7D7D");
+        synonymToColor.put("grey50", "#7F7F7F");
+        synonymToColor.put("grey51", "#828282");
+        synonymToColor.put("grey52", "#858585");
+        synonymToColor.put("grey53", "#878787");
+        synonymToColor.put("grey54", "#8A8A8A");
+        synonymToColor.put("grey55", "#8C8C8C");
+        synonymToColor.put("grey56", "#8F8F8F");
+        synonymToColor.put("grey57", "#919191");
+        synonymToColor.put("grey58", "#949494");
+        synonymToColor.put("grey59", "#969696");
+        synonymToColor.put("grey60", "#999999");
+        synonymToColor.put("grey61", "#9C9C9C");
+        synonymToColor.put("grey62", "#9E9E9E");
+        synonymToColor.put("grey63", "#A1A1A1");
+        synonymToColor.put("grey64", "#A3A3A3");
+        synonymToColor.put("grey65", "#A6A6A6");
+        synonymToColor.put("grey66", "#A8A8A8");
+        synonymToColor.put("grey67", "#ABABAB");
+        synonymToColor.put("grey68", "#ADADAD");
+        synonymToColor.put("grey69", "#B0B0B0");
+        synonymToColor.put("grey70", "#B3B3B3");
+        synonymToColor.put("grey71", "#B5B5B5");
+        synonymToColor.put("grey72", "#B8B8B8");
+        synonymToColor.put("grey73", "#BABABA");
+        synonymToColor.put("grey74", "#BDBDBD");
+        synonymToColor.put("grey75", "#BFBFBF");
+        synonymToColor.put("grey76", "#C2C2C2");
+        synonymToColor.put("grey77", "#C4C4C4");
+        synonymToColor.put("grey78", "#C7C7C7");
+        synonymToColor.put("grey79", "#C9C9C9");
+        synonymToColor.put("grey80", "#CCCCCC");
+        synonymToColor.put("grey81", "#CFCFCF");
+        synonymToColor.put("grey82", "#D1D1D1");
+        synonymToColor.put("grey83", "#D4D4D4");
+        synonymToColor.put("grey84", "#D6D6D6");
+        synonymToColor.put("grey85", "#D9D9D9");
+        synonymToColor.put("grey86", "#DBDBDB");
+        synonymToColor.put("grey87", "#DEDEDE");
+        synonymToColor.put("grey88", "#E0E0E0");
+        synonymToColor.put("grey89", "#E3E3E3");
+        synonymToColor.put("grey90", "#E5E5E5");
+        synonymToColor.put("grey91", "#E8E8E8");
+        synonymToColor.put("grey92", "#EBEBEB");
+        synonymToColor.put("grey93", "#EDEDED");
+        synonymToColor.put("grey94", "#F0F0F0");
+        synonymToColor.put("grey95", "#F2F2F2");
+        synonymToColor.put("grey96", "#F5F5F5");
+        synonymToColor.put("grey97", "#F7F7F7");
+        synonymToColor.put("grey98", "#FAFAFA");
+        synonymToColor.put("grey99", "#FCFCFC");
+        synonymToColor.put("grey100", "#FFFFFF");
+        synonymToColor.put("honeydew", "#F0FFF0");
+        synonymToColor.put("honeydew1", "#F0FFF0");
+        synonymToColor.put("honeydew2", "#E0EEE0");
+        synonymToColor.put("honeydew3", "#C1CDC1");
+        synonymToColor.put("honeydew4", "#838B83");
+        synonymToColor.put("hotpink", "#FF69B4");
+        synonymToColor.put("hotpink1", "#FF6EB4");
+        synonymToColor.put("hotpink2", "#EE6AA7");
+        synonymToColor.put("hotpink3", "#CD6090");
+        synonymToColor.put("hotpink4", "#8B3A62");
+        synonymToColor.put("indianred", "#CD5C5C");
+        synonymToColor.put("indianred1", "#FF6A6A");
+        synonymToColor.put("indianred2", "#EE6363");
+        synonymToColor.put("indianred3", "#CD5555");
+        synonymToColor.put("indianred4", "#8B3A3A");
+        synonymToColor.put("ivory", "#FFFFF0");
+        synonymToColor.put("ivory1", "#FFFFF0");
+        synonymToColor.put("ivory2", "#EEEEE0");
+        synonymToColor.put("ivory3", "#CDCDC1");
+        synonymToColor.put("ivory4", "#8B8B83");
+        synonymToColor.put("khaki", "#F0E68C");
+        synonymToColor.put("khaki1", "#FFF68F");
+        synonymToColor.put("khaki2", "#EEE685");
+        synonymToColor.put("khaki3", "#CDC673");
+        synonymToColor.put("khaki4", "#8B864E");
+        synonymToColor.put("lavender", "#E6E6FA");
+        synonymToColor.put("lavenderblush", "#FFF0F5");
+        synonymToColor.put("lavenderblush1", "#FFF0F5");
+        synonymToColor.put("lavenderblush2", "#EEE0E5");
+        synonymToColor.put("lavenderblush3", "#CDC1C5");
+        synonymToColor.put("lavenderblush4", "#8B8386");
+        synonymToColor.put("lawngreen", "#7CFC00");
+        synonymToColor.put("lemonchiffon", "#FFFACD");
+        synonymToColor.put("lemonchiffon1", "#FFFACD");
+        synonymToColor.put("lemonchiffon2", "#EEE9BF");
+        synonymToColor.put("lemonchiffon3", "#CDC9A5");
+        synonymToColor.put("lemonchiffon4", "#8B8970");
+        synonymToColor.put("lightblue", "#ADD8E6");
+        synonymToColor.put("lightblue1", "#BFEFFF");
+        synonymToColor.put("lightblue2", "#B2DFEE");
+        synonymToColor.put("lightblue3", "#9AC0CD");
+        synonymToColor.put("lightblue4", "#68838B");
+        synonymToColor.put("lightcoral", "#F08080");
+        synonymToColor.put("lightcyan", "#E0FFFF");
+        synonymToColor.put("lightcyan1", "#E0FFFF");
+        synonymToColor.put("lightcyan2", "#D1EEEE");
+        synonymToColor.put("lightcyan3", "#B4CDCD");
+        synonymToColor.put("lightcyan4", "#7A8B8B");
+        synonymToColor.put("lightgoldenrod", "#EEDD82");
+        synonymToColor.put("lightgoldenrod1", "#FFEC8B");
+        synonymToColor.put("lightgoldenrod2", "#EEDC82");
+        synonymToColor.put("lightgoldenrod3", "#CDBE70");
+        synonymToColor.put("lightgoldenrod4", "#8B814C");
+        synonymToColor.put("lightgoldenrodyellow", "#FAFAD2");
+        synonymToColor.put("lightgray", "#D3D3D3");
+        synonymToColor.put("lightgreen", "#90EE90");
+        synonymToColor.put("lightgrey", "#D3D3D3");
+        synonymToColor.put("lightpink", "#FFB6C1");
+        synonymToColor.put("lightpink1", "#FFAEB9");
+        synonymToColor.put("lightpink2", "#EEA2AD");
+        synonymToColor.put("lightpink3", "#CD8C95");
+        synonymToColor.put("lightpink4", "#8B5F65");
+        synonymToColor.put("lightsalmon", "#FFA07A");
+        synonymToColor.put("lightsalmon1", "#FFA07A");
+        synonymToColor.put("lightsalmon2", "#EE9572");
+        synonymToColor.put("lightsalmon3", "#CD8162");
+        synonymToColor.put("lightsalmon4", "#8B5742");
+        synonymToColor.put("lightseagreen", "#20B2AA");
+        synonymToColor.put("lightskyblue", "#87CEFA");
+        synonymToColor.put("lightskyblue1", "#B0E2FF");
+        synonymToColor.put("lightskyblue2", "#A4D3EE");
+        synonymToColor.put("lightskyblue3", "#8DB6CD");
+        synonymToColor.put("lightskyblue4", "#607B8B");
+        synonymToColor.put("lightslateblue", "#8470FF");
+        synonymToColor.put("lightslategray", "#778899");
+        synonymToColor.put("lightslategrey", "#778899");
+        synonymToColor.put("lightsteelblue", "#B0C4DE");
+        synonymToColor.put("lightsteelblue1", "#CAE1FF");
+        synonymToColor.put("lightsteelblue2", "#BCD2EE");
+        synonymToColor.put("lightsteelblue3", "#A2B5CD");
+        synonymToColor.put("lightsteelblue4", "#6E7B8B");
+        synonymToColor.put("lightyellow", "#FFFFE0");
+        synonymToColor.put("lightyellow1", "#FFFFE0");
+        synonymToColor.put("lightyellow2", "#EEEED1");
+        synonymToColor.put("lightyellow3", "#CDCDB4");
+        synonymToColor.put("lightyellow4", "#8B8B7A");
+        synonymToColor.put("limegreen", "#32CD32");
+        synonymToColor.put("linen", "#FAF0E6");
+        synonymToColor.put("magenta", "#FF00FF");
+        synonymToColor.put("magenta1", "#FF00FF");
+        synonymToColor.put("magenta2", "#EE00EE");
+        synonymToColor.put("magenta3", "#CD00CD");
+        synonymToColor.put("magenta4", "#8B008B");
+        synonymToColor.put("maroon", "#B03060");
+        synonymToColor.put("maroon1", "#FF34B3");
+        synonymToColor.put("maroon2", "#EE30A7");
+        synonymToColor.put("maroon3", "#CD2990");
+        synonymToColor.put("maroon4", "#8B1C62");
+        synonymToColor.put("mediumaquamarine", "#66CDAA");
+        synonymToColor.put("mediumblue", "#0000CD");
+        synonymToColor.put("mediumorchid", "#BA55D3");
+        synonymToColor.put("mediumorchid1", "#E066FF");
+        synonymToColor.put("mediumorchid2", "#D15FEE");
+        synonymToColor.put("mediumorchid3", "#B452CD");
+        synonymToColor.put("mediumorchid4", "#7A378B");
+        synonymToColor.put("mediumpurple", "#9370DB");
+        synonymToColor.put("mediumpurple1", "#AB82FF");
+        synonymToColor.put("mediumpurple2", "#9F79EE");
+        synonymToColor.put("mediumpurple3", "#8968CD");
+        synonymToColor.put("mediumpurple4", "#5D478B");
+        synonymToColor.put("mediumseagreen", "#3CB371");
+        synonymToColor.put("mediumslateblue", "#7B68EE");
+        synonymToColor.put("mediumspringgreen", "#00FA9A");
+        synonymToColor.put("mediumturquoise", "#48D1CC");
+        synonymToColor.put("mediumvioletred", "#C71585");
+        synonymToColor.put("midnightblue", "#191970");
+        synonymToColor.put("mintcream", "#F5FFFA");
+        synonymToColor.put("mistyrose", "#FFE4E1");
+        synonymToColor.put("mistyrose1", "#FFE4E1");
+        synonymToColor.put("mistyrose2", "#EED5D2");
+        synonymToColor.put("mistyrose3", "#CDB7B5");
+        synonymToColor.put("mistyrose4", "#8B7D7B");
+        synonymToColor.put("moccasin", "#FFE4B5");
+        synonymToColor.put("navajowhite", "#FFDEAD");
+        synonymToColor.put("navajowhite1", "#FFDEAD");
+        synonymToColor.put("navajowhite2", "#EECFA1");
+        synonymToColor.put("navajowhite3", "#CDB38B");
+        synonymToColor.put("navajowhite4", "#8B795E");
+        synonymToColor.put("navy", "#000080");
+        synonymToColor.put("navyblue", "#000080");
+        synonymToColor.put("oldlace", "#FDF5E6");
+        synonymToColor.put("olivedrab", "#6B8E23");
+        synonymToColor.put("olivedrab1", "#C0FF3E");
+        synonymToColor.put("olivedrab2", "#B3EE3A");
+        synonymToColor.put("olivedrab3", "#9ACD32");
+        synonymToColor.put("olivedrab4", "#698B22");
+        synonymToColor.put("orange", "#FFA500");
+        synonymToColor.put("orange1", "#FFA500");
+        synonymToColor.put("orange2", "#EE9A00");
+        synonymToColor.put("orange3", "#CD8500");
+        synonymToColor.put("orange4", "#8B5A00");
+        synonymToColor.put("orangered", "#FF4500");
+        synonymToColor.put("orangered1", "#FF4500");
+        synonymToColor.put("orangered2", "#EE4000");
+        synonymToColor.put("orangered3", "#CD3700");
+        synonymToColor.put("orangered4", "#8B2500");
+        synonymToColor.put("orchid", "#DA70D6");
+        synonymToColor.put("orchid1", "#FF83FA");
+        synonymToColor.put("orchid2", "#EE7AE9");
+        synonymToColor.put("orchid3", "#CD69C9");
+        synonymToColor.put("orchid4", "#8B4789");
+        synonymToColor.put("palegoldenrod", "#EEE8AA");
+        synonymToColor.put("palegreen", "#98FB98");
+        synonymToColor.put("palegreen1", "#9AFF9A");
+        synonymToColor.put("palegreen2", "#90EE90");
+        synonymToColor.put("palegreen3", "#7CCD7C");
+        synonymToColor.put("palegreen4", "#548B54");
+        synonymToColor.put("paleturquoise", "#AFEEEE");
+        synonymToColor.put("paleturquoise1", "#BBFFFF");
+        synonymToColor.put("paleturquoise2", "#AEEEEE");
+        synonymToColor.put("paleturquoise3", "#96CDCD");
+        synonymToColor.put("paleturquoise4", "#668B8B");
+        synonymToColor.put("palevioletred", "#DB7093");
+        synonymToColor.put("palevioletred1", "#FF82AB");
+        synonymToColor.put("palevioletred2", "#EE799F");
+        synonymToColor.put("palevioletred3", "#CD6889");
+        synonymToColor.put("palevioletred4", "#8B475D");
+        synonymToColor.put("papayawhip", "#FFEFD5");
+        synonymToColor.put("peachpuff", "#FFDAB9");
+        synonymToColor.put("peachpuff1", "#FFDAB9");
+        synonymToColor.put("peachpuff2", "#EECBAD");
+        synonymToColor.put("peachpuff3", "#CDAF95");
+        synonymToColor.put("peachpuff4", "#8B7765");
+        synonymToColor.put("peru", "#CD853F");
+        synonymToColor.put("pink", "#FFC0CB");
+        synonymToColor.put("pink1", "#FFB5C5");
+        synonymToColor.put("pink2", "#EEA9B8");
+        synonymToColor.put("pink3", "#CD919E");
+        synonymToColor.put("pink4", "#8B636C");
+        synonymToColor.put("plum", "#DDA0DD");
+        synonymToColor.put("plum1", "#FFBBFF");
+        synonymToColor.put("plum2", "#EEAEEE");
+        synonymToColor.put("plum3", "#CD96CD");
+        synonymToColor.put("plum4", "#8B668B");
+        synonymToColor.put("powderblue", "#B0E0E6");
+        synonymToColor.put("purple", "#A020F0");
+        synonymToColor.put("purple1", "#9B30FF");
+        synonymToColor.put("purple2", "#912CEE");
+        synonymToColor.put("purple3", "#7D26CD");
+        synonymToColor.put("purple4", "#551A8B");
+        synonymToColor.put("red", "#FF0000");
+        synonymToColor.put("red1", "#FF0000");
+        synonymToColor.put("red2", "#EE0000");
+        synonymToColor.put("red3", "#CD0000");
+        synonymToColor.put("red4", "#8B0000");
+        synonymToColor.put("rosybrown", "#BC8F8F");
+        synonymToColor.put("rosybrown1", "#FFC1C1");
+        synonymToColor.put("rosybrown2", "#EEB4B4");
+        synonymToColor.put("rosybrown3", "#CD9B9B");
+        synonymToColor.put("rosybrown4", "#8B6969");
+        synonymToColor.put("royalblue", "#4169E1");
+        synonymToColor.put("royalblue1", "#4876FF");
+        synonymToColor.put("royalblue2", "#436EEE");
+        synonymToColor.put("royalblue3", "#3A5FCD");
+        synonymToColor.put("royalblue4", "#27408B");
+        synonymToColor.put("snewData.addlebrown", "#8B4513");
+        synonymToColor.put("salmon", "#FA8072");
+        synonymToColor.put("salmon1", "#FF8C69");
+        synonymToColor.put("salmon2", "#EE8262");
+        synonymToColor.put("salmon3", "#CD7054");
+        synonymToColor.put("salmon4", "#8B4C39");
+        synonymToColor.put("sandybrown", "#F4A460");
+        synonymToColor.put("seagreen", "#2E8B57");
+        synonymToColor.put("seagreen1", "#54FF9F");
+        synonymToColor.put("seagreen2", "#4EEE94");
+        synonymToColor.put("seagreen3", "#43CD80");
+        synonymToColor.put("seagreen4", "#2E8B57");
+        synonymToColor.put("seashell", "#FFF5EE");
+        synonymToColor.put("seashell1", "#FFF5EE");
+        synonymToColor.put("seashell2", "#EEE5DE");
+        synonymToColor.put("seashell3", "#CDC5BF");
+        synonymToColor.put("seashell4", "#8B8682");
+        synonymToColor.put("sienna", "#A0522D");
+        synonymToColor.put("sienna1", "#FF8247");
+        synonymToColor.put("sienna2", "#EE7942");
+        synonymToColor.put("sienna3", "#CD6839");
+        synonymToColor.put("sienna4", "#8B4726");
+        synonymToColor.put("skyblue", "#87CEEB");
+        synonymToColor.put("skyblue1", "#87CEFF");
+        synonymToColor.put("skyblue2", "#7EC0EE");
+        synonymToColor.put("skyblue3", "#6CA6CD");
+        synonymToColor.put("skyblue4", "#4A708B");
+        synonymToColor.put("slateblue", "#6A5ACD");
+        synonymToColor.put("slateblue1", "#836FFF");
+        synonymToColor.put("slateblue2", "#7A67EE");
+        synonymToColor.put("slateblue3", "#6959CD");
+        synonymToColor.put("slateblue4", "#473C8B");
+        synonymToColor.put("slategray", "#708090");
+        synonymToColor.put("slategray1", "#C6E2FF");
+        synonymToColor.put("slategray2", "#B9D3EE");
+        synonymToColor.put("slategray3", "#9FB6CD");
+        synonymToColor.put("slategray4", "#6C7B8B");
+        synonymToColor.put("slategrey", "#708090");
+        synonymToColor.put("snow", "#FFFAFA");
+        synonymToColor.put("snow1", "#FFFAFA");
+        synonymToColor.put("snow2", "#EEE9E9");
+        synonymToColor.put("snow3", "#CDC9C9");
+        synonymToColor.put("snow4", "#8B8989");
+        synonymToColor.put("springgreen", "#00FF7F");
+        synonymToColor.put("springgreen1", "#00FF7F");
+        synonymToColor.put("springgreen2", "#00EE76");
+        synonymToColor.put("springgreen3", "#00CD66");
+        synonymToColor.put("springgreen4", "#008B45");
+        synonymToColor.put("steelblue", "#4682B4");
+        synonymToColor.put("steelblue1", "#63B8FF");
+        synonymToColor.put("steelblue2", "#5CACEE");
+        synonymToColor.put("steelblue3", "#4F94CD");
+        synonymToColor.put("steelblue4", "#36648B");
+        synonymToColor.put("tan", "#D2B48C");
+        synonymToColor.put("tan1", "#FFA54F");
+        synonymToColor.put("tan2", "#EE9A49");
+        synonymToColor.put("tan3", "#CD853F");
+        synonymToColor.put("tan4", "#8B5A2B");
+        synonymToColor.put("thistle", "#D8BFD8");
+        synonymToColor.put("thistle1", "#FFE1FF");
+        synonymToColor.put("thistle2", "#EED2EE");
+        synonymToColor.put("thistle3", "#CDB5CD");
+        synonymToColor.put("thistle4", "#8B7B8B");
+        synonymToColor.put("tomato", "#FF6347");
+        synonymToColor.put("tomato1", "#FF6347");
+        synonymToColor.put("tomato2", "#EE5C42");
+        synonymToColor.put("tomato3", "#CD4F39");
+        synonymToColor.put("tomato4", "#8B3626");
+        synonymToColor.put("turquoise", "#40E0D0");
+        synonymToColor.put("turquoise1", "#00F5FF");
+        synonymToColor.put("turquoise2", "#00E5EE");
+        synonymToColor.put("turquoise3", "#00C5CD");
+        synonymToColor.put("turquoise4", "#00868B");
+        synonymToColor.put("violet", "#EE82EE");
+        synonymToColor.put("violetred", "#D02090");
+        synonymToColor.put("violetred1", "#FF3E96");
+        synonymToColor.put("violetred2", "#EE3A8C");
+        synonymToColor.put("violetred3", "#CD3278");
+        synonymToColor.put("violetred4", "#8B2252");
+        synonymToColor.put("wheat", "#F5DEB3");
+        synonymToColor.put("wheat1", "#FFE7BA");
+        synonymToColor.put("wheat2", "#EED8AE");
+        synonymToColor.put("wheat3", "#CDBA96");
+        synonymToColor.put("wheat4", "#8B7E66");
+        synonymToColor.put("whitesmoke", "#F5F5F5");
+        synonymToColor.put("yellow", "#FFFF00");
+        synonymToColor.put("yellow1", "#FFFF00");
+        synonymToColor.put("yellow2", "#EEEE00");
+        synonymToColor.put("yellow3", "#CDCD00");
+        synonymToColor.put("yellow4", "#8B8B00");
+        synonymToColor.put("yellowgreen", "#9ACD32");
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..2980378933908efae26bb8a345024acd57714529
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridContext.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.library.fastrGrid.device.JFrameDevice;
+
+/**
+ * Encapsulated the acces to the global grid state.
+ */
+public final class GridContext {
+    private static final GridContext INSTANCE = new GridContext();
+    private final GridState gridState = new GridState();
+    private GridDevice currentDevice;
+
+    public static GridContext getContext() {
+        return INSTANCE;
+    }
+
+    public GridState getGridState() {
+        return gridState;
+    }
+
+    public GridDevice getCurrentDevice() {
+        if (currentDevice == null) {
+            currentDevice = new JFrameDevice();
+        }
+        return currentDevice;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridLinesNode.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridLinesNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..2cceaeb82a9845570abd46d2f9f3b9386e55d9de
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridLinesNode.java
@@ -0,0 +1,125 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asIntVector;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.ViewPortTransform.GetViewPortTransformNode;
+import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+/**
+ * Common code shared between {@code L_lines} and {@code L_polygon} externals. Both draw a series of
+ * lines, but only the later connects the last point with the first point and only the former draws
+ * arrows (which is not implemented yet). Note: the third parameter contains sequences
+ * {@code 1:max(length(x),length(y))}, where the 'length' dispatches to S3 method giving us unit
+ * length like {@link com.oracle.truffle.r.library.fastrGrid.Unit.UnitLengthNode}. This means that
+ * we do not have to use the {@link com.oracle.truffle.r.library.fastrGrid.Unit.UnitLengthNode} to
+ * get the length.
+ */
+public abstract class GridLinesNode extends Node {
+    public static GridLinesNode createLines() {
+        return new GridLinesImpl();
+    }
+
+    public static GridLinesNode createPolygon() {
+        return new GridLinesPolygon();
+    }
+
+    @Child private Unit.UnitToInchesNode unitToInches = Unit.createToInchesNode();
+    @Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode();
+    @Child private DrawArrowsNode drawArrowsNode = new DrawArrowsNode();
+
+    @TruffleBoundary
+    void execute(RAbstractVector x, RAbstractVector y, RList lengths, RList arrow) {
+        GridContext ctx = GridContext.getContext();
+        GridDevice dev = ctx.getCurrentDevice();
+
+        RList currentVP = ctx.getGridState().getViewPort();
+        DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
+        ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, dev);
+        ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
+
+        // Convert the list of vectors of indexes to type-safe array and calculate the max length of
+        // the vectors.
+        RAbstractIntVector[] unitIndexesList = new RAbstractIntVector[lengths.getLength()];
+        int maxIndexesLen = 0;
+        for (int i = 0; i < lengths.getLength(); i++) {
+            unitIndexesList[i] = asIntVector(lengths.getDataAt(i));
+            maxIndexesLen = Math.max(maxIndexesLen, unitIndexesList[i].getLength());
+        }
+
+        double[] xx = new double[maxIndexesLen + 1];    // plus one for polygons
+        double[] yy = new double[maxIndexesLen + 1];
+        for (RAbstractIntVector unitIndexes : unitIndexesList) {
+            boolean oldIsFinite = false;
+            int start = 0;
+            int unitIndexesLen = unitIndexes.getLength();
+            // following loop finds series of valid points (finite x and y values) and draws each
+            // such series as a polyline
+            for (int i = 0; i < unitIndexesLen; i++) {
+                int unitIndex = unitIndexes.getDataAt(i) - 1;   // coverting R's 1-based index
+                Point origLoc = Point.fromUnits(unitToInches, x, y, unitIndex, conversionCtx);
+                Point loc = TransformMatrix.transLocation(origLoc, vpTransform.transform);
+                xx[i] = loc.x;
+                yy[i] = loc.y;
+                boolean currIsFinite = loc.isFinite();
+                boolean lastIter = i == (unitIndexesLen - 1);
+                if (currIsFinite && !oldIsFinite) {
+                    start = i; // start a new series
+                } else if (oldIsFinite && (!currIsFinite || lastIter)) {
+                    // draw the previous points series because
+                    // (1) current is invalid point. Note: in (one of) the next iteration(s), the
+                    // oldIsFinite will be false and we will update the start and start a new series
+                    // (2) we are in the last iteration
+                    if (lastIter || i - start > 1) {
+                        // we draw only if the previous series of points was at least of length 3 or
+                        // it's last iteration. This seems slightly weird, but that's how GnuR seems
+                        // to work
+                        drawPolylines(dev, drawingCtx, yy, xx, start, (i - start) + 1);
+                        if (arrow != null) {
+                            // Can draw an arrow at the start if the points include the first point.
+                            // Draw an arrow at the end only if this is the last series
+                            drawArrowsNode.drawArrows(xx, yy, start, (i - start) + 1, unitIndex, arrow, start == 0, lastIter, conversionCtx);
+                        }
+                    }
+                }
+                oldIsFinite = currIsFinite;
+            }
+        }
+    }
+
+    abstract void drawPolylines(GridDevice dev, DrawingContext drawingCtx, double[] yy, double[] xx, int start, int length);
+
+    private static final class GridLinesImpl extends GridLinesNode {
+        @Override
+        void drawPolylines(GridDevice dev, DrawingContext drawingCtx, double[] yy, double[] xx, int start, int length) {
+            dev.drawPolyLines(drawingCtx, xx, yy, start, length);
+        }
+    }
+
+    private static final class GridLinesPolygon extends GridLinesNode {
+        @Override
+        void drawPolylines(GridDevice dev, DrawingContext drawingCtx, double[] yy, double[] xx, int start, int length) {
+            xx[start + length] = xx[start];
+            yy[start + length] = yy[start];
+            dev.drawPolyLines(drawingCtx, xx, yy, start, length + 1);
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java
new file mode 100644
index 0000000000000000000000000000000000000000..5d840cbf68fcdea0a1a4b761ee232408bc0d8824
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java
@@ -0,0 +1,95 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+
+public final class GridState {
+    private RList gpar;
+    private RList viewPort;
+    private REnvironment gridEnv;
+    private double scale = 1;
+    private boolean deviceInitialized;
+
+    /**
+     * Current grob being drawn (for determining the list of grobs to search when evaluating a
+     * grobwidth/height unit via gPath). May be RNull or RList.
+     */
+    private Object currentGrob;
+
+    GridState() {
+    }
+
+    public void init(REnvironment gridEnv, GridDevice currentDevice) {
+        this.gridEnv = gridEnv;
+        this.currentGrob = RNull.instance;
+        initGPar(currentDevice);
+    }
+
+    void initGPar(GridDevice currentDevice) {
+        gpar = GPar.createNew();
+        currentDevice.initDrawingContext(GPar.asDrawingContext(gpar));
+    }
+
+    public static DrawingContext getInitialGPar(GridDevice device) {
+        DrawingContext result = GPar.asDrawingContext(GPar.createNew());
+        device.initDrawingContext(result);
+        return result;
+    }
+
+    public RList getGpar() {
+        assert gridEnv != null : "GridState not initialized";
+        return gpar;
+    }
+
+    public void setGpar(RList gpar) {
+        assert gridEnv != null : "GridState not initialized";
+        this.gpar = gpar;
+    }
+
+    public boolean isDeviceInitialized() {
+        return deviceInitialized;
+    }
+
+    public void setDeviceInitialized() {
+        this.deviceInitialized = true;
+    }
+
+    public RList getViewPort() {
+        return viewPort;
+    }
+
+    public void setViewPort(RList viewPort) {
+        this.viewPort = viewPort;
+    }
+
+    public REnvironment getGridEnv() {
+        return gridEnv;
+    }
+
+    public Object getCurrentGrob() {
+        return currentGrob;
+    }
+
+    public void setCurrentGrob(Object currentGrob) {
+        this.currentGrob = currentGrob;
+    }
+
+    public double getScale() {
+        return scale;
+    }
+
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridStateGetNode.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridStateGetNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..9006431d6095d25d4a3a97ee0bb2e808565c1eed
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridStateGetNode.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import java.util.function.Function;
+
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+
+/**
+ * Gets a specified attribute of current {@link GridState}.
+ */
+class GridStateGetNode extends RExternalBuiltinNode.Arg0 {
+    private final Function<GridState, Object> getter;
+
+    static {
+        Casts.noCasts(GridStateGetNode.class);
+    }
+
+    GridStateGetNode(Function<GridState, Object> getter) {
+        this.getter = getter;
+    }
+
+    @Override
+    public Object execute() {
+        Object result = getter.apply(GridContext.getContext().getGridState());
+        assert result != null;
+        return result;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridStateSetNode.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridStateSetNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..4b07fdcc38dcea0935898ab4c99f38c0a793df1a
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridStateSetNode.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import java.util.function.BiConsumer;
+
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RNull;
+
+/**
+ * Sets a specified attribute of current {@link GridState}.
+ */
+public final class GridStateSetNode extends RExternalBuiltinNode.Arg1 {
+    private final BiConsumer<GridState, Object> setter;
+
+    static {
+        Casts.noCasts(GridStateSetNode.class);
+    }
+
+    public static GridStateSetNode create(BiConsumer<GridState, Object> setter) {
+        return new GridStateSetNode(setter);
+    }
+
+    private GridStateSetNode(BiConsumer<GridState, Object> setter) {
+        this.setter = setter;
+    }
+
+    @Override
+    public Object execute(Object arg) {
+        setter.accept(GridContext.getContext().getGridState(), arg);
+        return RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridTextNode.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridTextNode.java
new file mode 100644
index 0000000000000000000000000000000000000000..b9d862b0fb7964f21d3a5771acafb996586db78c
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridTextNode.java
@@ -0,0 +1,254 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (c) 2001-2015, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.fmax;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.fmin;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.getDataAtMod;
+import static com.oracle.truffle.r.library.fastrGrid.TransformMatrix.multiply;
+import static com.oracle.truffle.r.library.fastrGrid.TransformMatrix.transLocation;
+import static com.oracle.truffle.r.library.fastrGrid.TransformMatrix.translation;
+import static com.oracle.truffle.r.library.fastrGrid.device.DrawingContext.INCH_TO_POINTS_FACTOR;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.library.fastrGrid.EdgeDetection.Rectangle;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.ViewPortTransform.GetViewPortTransformNode;
+import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.nodes.builtin.NodeWithArgumentCasts.Casts;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
+
+/**
+ * Implements what is in the original grid code implemented by {@code gridText} function.
+ *
+ * Note: the third parameter contains sequences {@code 1:max(length(x),length(y))}, where the
+ * 'length' dispatches to S3 method giving us unit length like {@link Unit.UnitLengthNode}.
+ */
+public final class GridTextNode extends RBaseNode {
+    @Child private Unit.UnitToInchesNode unitToInches = Unit.createToInchesNode();
+    @Child private Unit.UnitLengthNode unitLength = Unit.createLengthNode();
+    @Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode();
+
+    private final ConditionProfile checkOverlapProfile = ConditionProfile.createBinaryProfile();
+    private final boolean draw;
+
+    static void addGridTextCasts(Casts casts) {
+        casts.arg(0).asStringVector();
+        casts.arg(1).mustBe(abstractVectorValue());
+        casts.arg(2).mustBe(abstractVectorValue());
+        casts.arg(3).mustBe(numericValue()).asDoubleVector();
+        casts.arg(4).mustBe(numericValue()).asDoubleVector();
+        casts.arg(5).mustBe(numericValue()).asDoubleVector();
+    }
+
+    private GridTextNode(boolean draw) {
+        this.draw = draw;
+    }
+
+    public static GridTextNode createDraw() {
+        return new GridTextNode(true);
+    }
+
+    public static GridTextNode createCalculateBounds() {
+        return new GridTextNode(false);
+    }
+
+    @TruffleBoundary
+    public Object gridText(RAbstractStringVector textVec, RAbstractVector x, RAbstractVector y, RAbstractDoubleVector hjustVec, RAbstractDoubleVector vjustVec, RAbstractDoubleVector rotationVec,
+                    boolean checkOverlapIn, double theta) {
+        if (textVec.getLength() == 0) {
+            return RNull.instance;
+        }
+
+        boolean checkOverlap = checkOverlapProfile.profile(checkOverlapIn);
+        GridContext ctx = GridContext.getContext();
+        GridDevice dev = ctx.getCurrentDevice();
+
+        RList currentVP = ctx.getGridState().getViewPort();
+        DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
+        ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, dev);
+        ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
+
+        int length = GridUtils.maxLength(unitLength, x, y);
+
+        // following variables will hold the (intermediate) results of bounds checking
+        int boundsCount = 0;
+        Point edge = null;
+        double xmin = Double.MAX_VALUE;
+        double xmax = Double.MIN_VALUE;
+        double ymin = Double.MAX_VALUE;
+        double ymax = Double.MIN_VALUE;
+        int ntxt = 0;   // number of texts that were actually used for bounds computation
+        EdgeDetection.Rectangle[] bounds = null;
+        if (checkOverlap || !draw) {
+            bounds = new EdgeDetection.Rectangle[length];
+        }
+
+        for (int i = 0; i < length; i++) {
+            Point loc = Point.fromUnits(unitToInches, x, y, i, conversionCtx);
+            if (draw) {
+                // transformation not necessary for bounds calculation
+                loc = transLocation(loc, vpTransform.transform);
+            }
+
+            String text = textVec.getDataAt(i % textVec.getLength());
+            double hjust = getDataAtMod(hjustVec, i);
+            double vjust = getDataAtMod(vjustVec, i);
+            double rotation = getDataAtMod(rotationVec, i);
+
+            // update bounds if necessary
+            boolean doDraw = true;
+            Rectangle trect = null;
+            if (checkOverlap || !draw) {
+                trect = textRect(loc, hjust, vjust, rotation, text, drawingCtx, dev);
+                for (int j = 0; j < boundsCount; j++) {
+                    if (trect.intersects(bounds[j])) {
+                        doDraw = false;
+                        break;
+                    }
+                }
+                if (doDraw) {
+                    bounds[boundsCount++] = trect;
+                }
+            }
+
+            // actual drawing
+            if (draw && doDraw) {
+                text(loc.x, loc.y, text, hjust, vjust, rotation, drawingCtx, dev);
+            }
+
+            // or bounds checking
+            if (!draw) {
+                if (Double.isFinite(loc.x) && Double.isFinite(loc.y)) {
+                    xmin = fmin(xmin, trect.x);
+                    xmax = fmax(xmax, trect.x);
+                    ymin = fmin(ymin, trect.y);
+                    ymax = fmax(ymax, trect.y);
+                    double[] xxx = new double[4];
+                    double[] yyy = new double[4];
+                    for (int j = 0; j < 4; j++) {
+                        xxx[j] = trect.x[3 - j];
+                        yyy[j] = trect.y[3 - j];
+                    }
+                    // Calculate edgex and edgey for case where this is the only rect
+                    edge = EdgeDetection.polygonEdge(xxx, yyy, 4, theta);
+                    ntxt++;
+                }
+            }
+        }
+
+        if (!draw && ntxt > 0) {
+            // 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
+                edge = EdgeDetection.rectEdge(xmin, ymin, xmax, ymax, theta);
+            }
+
+            double scale = GridContext.getContext().getGridState().getScale();
+            double[] result = new double[4];
+            result[0] = edge.x / scale;
+            result[1] = edge.y / scale;
+            result[2] = (xmax - xmin) / scale;
+            result[3] = (ymax - ymin) / scale;
+            return RDataFactory.createDoubleVector(result, RDataFactory.COMPLETE_VECTOR);
+        }
+
+        // NULL is OK result even for bound checking if there was no text actually "drawn", the R
+        // wrapper deals with NULL in such case. For actual drawing case, we should always return
+        // NULL
+        return RNull.instance;
+    }
+
+    // transcribed from utils.c
+
+    private EdgeDetection.Rectangle textRect(Point loc, double xadj, double yadj, double rotation, String text, DrawingContext drawingCtx, GridDevice device) {
+        // TODO: for expressions the h and w are calculated differently
+        double h = device.getStringHeight(drawingCtx, text);
+        double w = device.getStringWidth(drawingCtx, text);
+
+        double[][] thisJustification = translation(-xadj * w, -yadj * h);
+        double[][] thisRotation = TransformMatrix.rotation(rotation);
+        double[][] transform = multiply(multiply(thisJustification, thisRotation), translation(loc.x, loc.y));
+
+        Point bl = transLocation(new Point(0, 0), transform);
+        Point br = transLocation(new Point(w, 0), transform);
+        Point tr = transLocation(new Point(w, h), transform);
+        Point tl = transLocation(new Point(0, h), transform);
+        return new Rectangle(bl, br, tr, tl);
+    }
+
+    // transcribed from engine.c
+
+    private void text(double x, double y, String text, double xadjIn, double yadj, double rotation, DrawingContext drawingCtx, GridDevice device) {
+        if (!Double.isFinite(yadj)) {
+            throw RInternalError.unimplemented("'exact' vertical centering, see engine.c:1700");
+        }
+        double xadj = Double.isFinite(xadjIn) ? xadjIn : 0.5;
+
+        double radRotation = Math.toRadians(rotation);
+        double cosRot = Math.cos(radRotation);
+        double sinRot = Math.sin(radRotation);
+        String[] lines = text.split("\n");
+        for (int lineIdx = 0; lineIdx < lines.length; lineIdx++) {
+            double xoff;
+            double yoff;
+            if (lines.length == 1) {
+                // simplification for single line
+                xoff = x;
+                yoff = y;
+            } else {
+                yoff = (1 - yadj) * (lines.length - 1) - lineIdx;
+                // TODO: in the original the following formula uses "dd->dev->cra[1]"
+                yoff *= (drawingCtx.getFontSize() * drawingCtx.getLineHeight()) / INCH_TO_POINTS_FACTOR;
+                xoff = -yoff * sinRot;
+                yoff = yoff * cosRot;
+                xoff = x + xoff;
+                yoff = y + yoff;
+            }
+
+            double xleft = xoff;
+            double ybottom = yoff;
+            // now determine bottom-left for THIS line
+            if (xadj != 0.0 || yadj != 0.0) {
+                // otherwise simply the initial values for xleft and ybottom are OK
+                double width = device.getStringWidth(drawingCtx, lines[lineIdx]);
+                double height = device.getStringHeight(drawingCtx, lines[lineIdx]);
+                xleft = xoff - (xadj) * width * cosRot + yadj * height * sinRot;
+                ybottom = yoff - (xadj) * width * sinRot - yadj * height * cosRot;
+            }
+
+            device.drawString(drawingCtx, xleft, ybottom, radRotation, lines[lineIdx]);
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridUtils.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..b57eaaa9942a29cb6a70e1ffdad36095a6c524cd
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridUtils.java
@@ -0,0 +1,204 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.runtime.nmath.RMath.fmax2;
+import static com.oracle.truffle.r.runtime.nmath.RMath.fmin2;
+
+import com.oracle.truffle.api.nodes.ExplodeLoop;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitLengthNode;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.data.RAttributable;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.RStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+final class GridUtils {
+    private GridUtils() {
+        // only static members
+    }
+
+    static double justify(double coord, double size, double justification) {
+        // justification is supposed to be either between 0 and 1
+        return coord - size * justification;
+    }
+
+    /**
+     * Returns the amount of justification required. I.e. transforms the justification from value
+     * between 0 and 1 to the value within size.
+     */
+    static double justification(double size, double justification) {
+        return -size * justification;
+    }
+
+    static double getDataAtMod(RAbstractDoubleVector vec, int idx) {
+        return vec.getDataAt(idx % vec.getLength());
+    }
+
+    static int getDataAtMod(RAbstractIntVector vec, int idx) {
+        return vec.getDataAt(idx % vec.getLength());
+    }
+
+    @ExplodeLoop
+    static int maxLength(UnitLengthNode unitLength, RAbstractVector... units) {
+        int result = 0;
+        for (RAbstractVector unit : units) {
+            result = Math.max(result, unitLength.execute(unit));
+        }
+        return result;
+    }
+
+    @ExplodeLoop
+    static double fmax(double firstVal, double... vals) {
+        double result = firstVal;
+        for (double val : vals) {
+            result = fmax2(result, val);
+        }
+        return result;
+    }
+
+    @ExplodeLoop
+    static double fmin(double firstVal, double... vals) {
+        double result = firstVal;
+        for (double val : vals) {
+            result = fmin2(result, val);
+        }
+        return result;
+    }
+
+    static boolean hasRClass(RAttributable obj, String clazz) {
+        RStringVector classAttr = obj.getClassAttr();
+        if (classAttr == null) {
+            return false;
+        }
+        for (int i = 0; i < classAttr.getLength(); i++) {
+            if (classAttr.getDataAt(i).equals(clazz)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    static RList asListOrNull(Object value) {
+        if (value == null || value == RNull.instance) {
+            return null;
+        }
+        if (!(value instanceof RList)) {
+            throw RError.error(RError.NO_CALLER, Message.GENERIC, "Expected list");
+        }
+        return (RList) value;
+    }
+
+    static RList asList(Object value) {
+        if (!(value instanceof RList)) {
+            throw RError.error(RError.NO_CALLER, Message.GENERIC, "Expected list");
+        }
+        return (RList) value;
+    }
+
+    static double getDoubleAt(RAbstractVector vector, int index) {
+        if (vector instanceof RAbstractDoubleVector) {
+            return ((RAbstractDoubleVector) vector).getDataAt(index);
+        } else if (vector instanceof RAbstractIntVector) {
+            return ((RAbstractIntVector) vector).getDataAt(index);
+        }
+        throw RError.error(RError.NO_CALLER, Message.GENERIC, "Unexpected non double/integer value");
+    }
+
+    static double asDouble(Object val) {
+        if (val instanceof Double) {
+            return (double) val;
+        } else if (val instanceof RAbstractDoubleVector) {
+            if (((RAbstractDoubleVector) val).getLength() > 0) {
+                return ((RAbstractDoubleVector) val).getDataAt(0);
+            }
+        } else if (val instanceof Integer) {
+            return (int) val;
+        } else if (val instanceof RAbstractIntVector) {
+            if (((RAbstractIntVector) val).getLength() > 0) {
+                return ((RAbstractIntVector) val).getDataAt(0);
+            }
+        }
+        throw RError.error(RError.NO_CALLER, Message.GENERIC, "Unexpected non double/integer value " + val.getClass().getSimpleName());
+    }
+
+    static double asDouble(Object val, int cyclicIndex) {
+        if (val instanceof Double) {
+            return (int) val;
+        } else if (val instanceof RAbstractDoubleVector) {
+            RAbstractDoubleVector vec = (RAbstractDoubleVector) val;
+            if (vec.getLength() > 0) {
+                return vec.getDataAt(cyclicIndex % vec.getLength());
+            }
+        }
+        throw RError.error(RError.NO_CALLER, Message.GENERIC, "Unexpected non double value " + val.getClass().getSimpleName());
+    }
+
+    static int asInt(Object val, int cyclicIndex) {
+        if (val instanceof Integer) {
+            return (int) val;
+        } else if (val instanceof RAbstractIntVector) {
+            RAbstractIntVector vec = (RAbstractIntVector) val;
+            if (vec.getLength() > 0) {
+                return vec.getDataAt(cyclicIndex % vec.getLength());
+            }
+        }
+        throw RError.error(RError.NO_CALLER, Message.GENERIC, "Unexpected non integer value " + val.getClass().getSimpleName());
+    }
+
+    static RAbstractIntVector asIntVector(Object value) {
+        if (value instanceof Integer) {
+            return RDataFactory.createIntVectorFromScalar((Integer) value);
+        } else if (value instanceof RAbstractIntVector) {
+            return (RAbstractIntVector) value;
+        }
+        throw RError.error(RError.NO_CALLER, Message.GENERIC, "Unexpected non integer value " + value.getClass().getSimpleName());
+    }
+
+    public static RAbstractDoubleVector asDoubleVector(Object obj) {
+        if (obj instanceof Double) {
+            return RDataFactory.createDoubleVectorFromScalar((Double) obj);
+        } else if (obj instanceof RAbstractDoubleVector) {
+            return (RAbstractDoubleVector) obj;
+        }
+        throw RError.error(RError.NO_CALLER, Message.GENERIC, "Unexpected non double value " + obj.getClass().getSimpleName());
+    }
+
+    static RAbstractContainer asAbstractContainer(Object value) {
+        if (value instanceof Integer) {
+            return RDataFactory.createIntVectorFromScalar((Integer) value);
+        } else if (value instanceof Double) {
+            return RDataFactory.createDoubleVectorFromScalar((Double) value);
+        } else if (value instanceof RAbstractContainer) {
+            return (RAbstractContainer) value;
+        }
+        throw RError.error(RError.NO_CALLER, Message.GENERIC, "Unexpected non abstract container type " + value.getClass().getSimpleName());
+    }
+
+    static double sum(double[] values) {
+        return sum(values, 0, values.length);
+    }
+
+    static double sum(double[] values, int from, int length) {
+        double result = 0;
+        for (int i = 0; i < length; i++) {
+            result += values[from + i];
+        }
+        return result;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/IgnoredGridExternal.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/IgnoredGridExternal.java
new file mode 100644
index 0000000000000000000000000000000000000000..9166dac774a24138f1e6b6180d5f7713198ce9f0
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/IgnoredGridExternal.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+
+/**
+ * A node for externals that we ignore, becuase we do not need to implement them or because they
+ * support functionallity we do not implement yet, especially record/replay.
+ */
+class IgnoredGridExternal extends RExternalBuiltinNode {
+    private final Object result;
+
+    static {
+        Casts.noCasts(IgnoredGridExternal.class);
+    }
+
+    IgnoredGridExternal(Object result) {
+        this.result = result;
+    }
+
+    @Override
+    protected Object call(RArgsValuesAndNames args) {
+        return result;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircle.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircle.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d0183bf8f336f6a5c09ea215f8dca8686f8d445
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircle.java
@@ -0,0 +1,66 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.ViewPortTransform.GetViewPortTransformNode;
+import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.nmath.RMath;
+
+public abstract class LCircle extends RExternalBuiltinNode.Arg3 {
+    @Child private Unit.UnitToInchesNode unitToInches = Unit.createToInchesNode();
+    @Child private Unit.UnitLengthNode unitLength = Unit.createLengthNode();
+    @Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode();
+
+    static {
+        Casts casts = new Casts(LCircle.class);
+        casts.arg(0).mustBe(abstractVectorValue());
+        casts.arg(1).mustBe(abstractVectorValue());
+        casts.arg(2).mustBe(abstractVectorValue());
+    }
+
+    public static LCircle create() {
+        return LCircleNodeGen.create();
+    }
+
+    @Specialization
+    @TruffleBoundary
+    Object doCircle(RAbstractVector xVec, RAbstractVector yVec, RAbstractVector radiusVec) {
+        GridContext ctx = GridContext.getContext();
+        GridDevice dev = ctx.getCurrentDevice();
+
+        RList currentVP = ctx.getGridState().getViewPort();
+        DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
+        ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, dev);
+        ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
+
+        int length = GridUtils.maxLength(unitLength, xVec, yVec, radiusVec);
+        for (int i = 0; i < length; i++) {
+            Size radiusSizes = Size.fromUnits(unitToInches, radiusVec, radiusVec, i, conversionCtx);
+            double radius = RMath.fmin2(radiusSizes.getWidth(), radiusSizes.getHeight());
+            Point origLoc = Point.fromUnits(unitToInches, xVec, yVec, i, conversionCtx);
+            Point loc = TransformMatrix.transLocation(origLoc, vpTransform.transform);
+            dev.drawCircle(drawingCtx, loc.x, loc.y, radius);
+        }
+        return RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LConvert.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LConvert.java
new file mode 100644
index 0000000000000000000000000000000000000000..b2300de6ab6313762a0eb3b1d9296566b7af1655
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LConvert.java
@@ -0,0 +1,157 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.Unit.NATIVE;
+import static com.oracle.truffle.r.library.fastrGrid.Unit.NPC;
+import static com.oracle.truffle.r.library.fastrGrid.Unit.isArithmeticUnit;
+import static com.oracle.truffle.r.library.fastrGrid.Unit.isListUnit;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.library.fastrGrid.Unit.AxisOrDimension;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.ViewPortTransform.GetViewPortTransformNode;
+import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+public abstract class LConvert extends RExternalBuiltinNode.Arg4 {
+    @Child private Unit.UnitLengthNode unitLength = Unit.createLengthNode();
+    @Child private Unit.UnitToInchesNode unitToInches = Unit.createToInchesNode();
+    @Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode();
+
+    static {
+        Casts casts = new Casts(LConvert.class);
+        casts.arg(0).mustBe(abstractVectorValue());
+        casts.arg(1).mustBe(numericValue()).asIntegerVector();
+        casts.arg(2).mustBe(numericValue()).asIntegerVector();
+        casts.arg(3).mustBe(numericValue()).asIntegerVector();
+    }
+
+    public static LConvert create() {
+        return LConvertNodeGen.create();
+    }
+
+    @Specialization
+    @TruffleBoundary
+    Object doConvert(RAbstractVector units, RAbstractIntVector axisFromVec, RAbstractIntVector axisToVec, RAbstractIntVector unitToVec) {
+
+        GridContext ctx = GridContext.getContext();
+        GridDevice dev = ctx.getCurrentDevice();
+
+        RList currentVP = ctx.getGridState().getViewPort();
+        DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
+        ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, dev);
+        ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
+
+        int length = unitLength.execute(units);
+        double[] result = new double[length];
+
+        RAbstractIntVector unitIds = GridUtils.asIntVector(units.getAttr(Unit.VALID_UNIT_ATTR));
+        boolean fromUnitIsSimple = !isArithmeticUnit(units) && !isListUnit(units);
+
+        for (int i = 0; i < length; i++) {
+            // scalar values used in current iteration
+            AxisOrDimension axisFrom = AxisOrDimension.fromInt(axisFromVec.getDataAt(i % axisFromVec.getLength()));
+            AxisOrDimension axisTo = AxisOrDimension.fromInt(axisToVec.getDataAt(i % axisToVec.getLength()));
+            boolean compatibleAxes = axisFrom.isHorizontal() == axisTo.isHorizontal();
+            double vpToSize = axisTo.isHorizontal() ? vpTransform.size.getWidth() : vpTransform.size.getHeight();
+            double vpFromSize = axisFrom.isHorizontal() ? vpTransform.size.getWidth() : vpTransform.size.getHeight();
+            int unitTo = unitToVec.getDataAt(i % unitToVec.getLength());
+            int fromUnitId = unitIds.getDataAt(i % unitIds.getLength());
+
+            // actual conversion:
+            // if the units are both relative, we are converting compatible axes and the vpSize for
+            // 'from' axis is small, we will not convert through inches, but directly to avoid
+            // divide by zero, but still do something useful
+            boolean bothRelative = isRelative(unitTo) && isRelative(fromUnitId);
+            boolean realativeConversion = bothRelative && fromUnitIsSimple && compatibleAxes && vpFromSize < 1e-6;
+            if (realativeConversion) {
+                // if the unit is not "unit.arithmetic" or "unit.list", it must be double vector
+                RAbstractDoubleVector simpleUnits = (RAbstractDoubleVector) units;
+                double fromValue = simpleUnits.getDataAt(i % simpleUnits.getLength());
+                result[i] = transformFromNPC(tranfromToNPC(fromValue, fromUnitId, axisFrom, vpContext), unitTo, axisTo, vpContext);
+            } else {
+                double inches = toInches(units, i, axisFrom, conversionCtx);
+                boolean isX = axisTo.isHorizontal();
+                double scalemin = isX ? vpContext.xscalemin : vpContext.yscalemin;
+                double scalemax = isX ? vpContext.xscalemax : vpContext.yscalemax;
+                result[i] = Unit.convertFromInches(inches, unitTo, vpToSize, scalemin, scalemax, axisTo.isDimension(), drawingCtx);
+            }
+        }
+
+        return RDataFactory.createDoubleVector(result, RDataFactory.COMPLETE_VECTOR);
+    }
+
+    private double toInches(RAbstractVector units, int index, AxisOrDimension axisFrom, UnitConversionContext conversionCtx) {
+        double inches;
+        if (axisFrom.isHorizontal()) {
+            if (axisFrom.isDimension()) {
+                inches = unitToInches.convertWidth(units, index, conversionCtx);
+            } else {
+                inches = unitToInches.convertX(units, index, conversionCtx);
+            }
+        } else {
+            if (axisFrom.isDimension()) {
+                inches = unitToInches.convertHeight(units, index, conversionCtx);
+            } else {
+                inches = unitToInches.convertY(units, index, conversionCtx);
+            }
+        }
+        return inches;
+    }
+
+    private static double tranfromToNPC(double value, int fromUnitId, AxisOrDimension axisFrom, ViewPortContext vpContext) {
+        if (fromUnitId == Unit.NPC) {
+            return value;
+        }
+        assert fromUnitId == Unit.NATIVE : "relative conversion should only happen when units are NPC or NATIVE";
+        boolean isX = axisFrom.isHorizontal();
+        double min = isX ? vpContext.xscalemin : vpContext.yscalemin;
+        double max = isX ? vpContext.xscalemax : vpContext.yscalemax;
+        if (axisFrom.isDimension()) {
+            return value / (max - min);
+        } else {
+            return (value - min) / (max - min);
+        }
+    }
+
+    private static double transformFromNPC(double value, int unitTo, AxisOrDimension axisTo, ViewPortContext vpContext) {
+        if (unitTo == Unit.NPC) {
+            return value;
+        }
+        assert unitTo == Unit.NATIVE : "relative conversion should only happen when units are NPC or NATIVE";
+        boolean isX = axisTo.isHorizontal();
+        double min = isX ? vpContext.xscalemin : vpContext.yscalemin;
+        double max = isX ? vpContext.xscalemax : vpContext.yscalemax;
+        if (axisTo.isDimension()) {
+            return value * (max - min);
+        } else {
+            return min + value * (max - min);
+        }
+    }
+
+    // Note: this is not relative in the same sense as IsRelativeUnitNode. The later checks for
+    // special NULL unit used only in layouting.
+    private static boolean isRelative(int unitId) {
+        return unitId == NPC || unitId == NATIVE;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LGridDirty.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LGridDirty.java
new file mode 100644
index 0000000000000000000000000000000000000000..b385a6091ed9592cb32f2a445ad15b14c9617ed2
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LGridDirty.java
@@ -0,0 +1,51 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.profiles.ConditionProfile;
+import com.oracle.truffle.r.library.fastrGrid.ViewPort.InitViewPortNode;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.data.RNull;
+
+class LGridDirty extends RExternalBuiltinNode {
+    @Child private InitViewPortNode initViewPort = new InitViewPortNode();
+    private final ConditionProfile initViewPortProfile = ConditionProfile.createCountingProfile();
+
+    static {
+        Casts.noCasts(LGridDirty.class);
+    }
+
+    @Override
+    public Object call(VirtualFrame frame, RArgsValuesAndNames args) {
+        GridState gridState = GridContext.getContext().getGridState();
+        if (!gridState.isDeviceInitialized()) {
+            CompilerDirectives.transferToInterpreter();
+            GridContext.getContext().getCurrentDevice().openNewPage();
+            gridState.setDeviceInitialized();
+        }
+        if (initViewPortProfile.profile(gridState.getViewPort() == null)) {
+            // this rarely happens, but we do not have a slow-path implementation (yet)
+            CompilerDirectives.transferToInterpreter();
+            gridState.setViewPort(initViewPort.execute(frame));
+        }
+        return RNull.instance;
+    }
+
+    @Override
+    protected Object call(RArgsValuesAndNames args) {
+        // shadowed by the VirtualFrame overload
+        return null;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitGPar.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitGPar.java
new file mode 100644
index 0000000000000000000000000000000000000000..152aac26cd945fa35c96e2d2b567e96e78786c25
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitGPar.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.data.RNull;
+
+class LInitGPar extends RExternalBuiltinNode {
+    static {
+        Casts.noCasts(LInitGPar.class);
+    }
+
+    @Override
+    @TruffleBoundary
+    protected Object call(RArgsValuesAndNames args) {
+        GridContext.getContext().getGridState().initGPar(GridContext.getContext().getCurrentDevice());
+        return RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitGrid.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitGrid.java
new file mode 100644
index 0000000000000000000000000000000000000000..885ace9762156ae4fba296ebb0e32a710ef8591d
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitGrid.java
@@ -0,0 +1,37 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+
+public abstract class LInitGrid extends RExternalBuiltinNode.Arg1 {
+    static {
+        Casts casts = new Casts(LInitGrid.class);
+        casts.arg(0).mustBe(REnvironment.class);
+    }
+
+    public static LInitGrid create() {
+        return LInitGridNodeGen.create();
+    }
+
+    @Specialization
+    @TruffleBoundary
+    public Object doEnv(REnvironment gridEnv) {
+        GridContext context = GridContext.getContext();
+        context.getGridState().init(gridEnv, context.getCurrentDevice());
+        return RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitViewPortStack.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitViewPortStack.java
new file mode 100644
index 0000000000000000000000000000000000000000..82c23f06ae2e1d4bb9f76eff3c7c52e6aedac120
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitViewPortStack.java
@@ -0,0 +1,38 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.r.library.fastrGrid.ViewPort.InitViewPortNode;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.data.RNull;
+
+class LInitViewPortStack extends RExternalBuiltinNode {
+    @Child private InitViewPortNode initViewPortNode = new InitViewPortNode();
+    static {
+        Casts.noCasts(LInitViewPortStack.class);
+    }
+
+    @Override
+    public Object call(VirtualFrame frame, @SuppressWarnings("unused") RArgsValuesAndNames args) {
+        initViewPortNode.execute(frame);
+        return RNull.instance;
+    }
+
+    @Override
+    protected Object call(RArgsValuesAndNames args) {
+        throw RInternalError.shouldNotReachHere("shadowed by the overload with VirtualFrame");
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LLines.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LLines.java
new file mode 100644
index 0000000000000000000000000000000000000000..e38149b8f1da4283dc5986c7e42032678fcc7b84
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LLines.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue;
+
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+public abstract class LLines extends RExternalBuiltinNode.Arg4 {
+    @Child private GridLinesNode gridLinesNode = GridLinesNode.createLines();
+
+    static {
+        Casts casts = new Casts(LLines.class);
+        casts.arg(0).mustBe(abstractVectorValue());
+        casts.arg(1).mustBe(abstractVectorValue());
+        casts.arg(2).mustBe(RList.class);
+        casts.arg(2).allowNull().mustBe(RList.class);
+    }
+
+    public static LLines create() {
+        return LLinesNodeGen.create();
+    }
+
+    @Specialization
+    Object doLines(RAbstractVector x, RAbstractVector y, RList lengths, RNull arrowIgnored) {
+        gridLinesNode.execute(x, y, lengths, null);
+        return RNull.instance;
+    }
+
+    @Specialization
+    Object doLines(RAbstractVector x, RAbstractVector y, RList lengths, @SuppressWarnings("unused") RList arrow) {
+        gridLinesNode.execute(x, y, lengths, arrow);
+        return RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LNewPage.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LNewPage.java
new file mode 100644
index 0000000000000000000000000000000000000000..19c7bc6bf6000f67e76e0599cf473fb6999f7c7f
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LNewPage.java
@@ -0,0 +1,29 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RNull;
+
+class LNewPage extends RExternalBuiltinNode.Arg0 {
+    static {
+        Casts.noCasts(LNewPage.class);
+    }
+
+    @Override
+    @TruffleBoundary
+    public Object execute() {
+        GridContext.getContext().getCurrentDevice().openNewPage();
+        return RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPoints.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPoints.java
new file mode 100644
index 0000000000000000000000000000000000000000..fb8977b780450c1967a5dc81b0de4272ea14640a
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPoints.java
@@ -0,0 +1,142 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2015, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitLengthNode;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitToInchesNode;
+import com.oracle.truffle.r.library.fastrGrid.ViewPortTransform.GetViewPortTransformNode;
+import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
+import com.oracle.truffle.r.library.fastrGrid.device.GridColor;
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+public abstract class LPoints extends RExternalBuiltinNode.Arg4 {
+    private static final double SMALL = 0.25;
+    private static final double RADIUS = 0.375;
+    private static final double SQRC = 0.88622692545275801364; /* sqrt(pi / 4) */
+    private static final double DMDC = 1.25331413731550025119; /* sqrt(pi / 4) * sqrt(2) */
+    private static final double TRC0 = 1.55512030155621416073; /* sqrt(4 * pi/(3 * sqrt(3))) */
+    private static final double TRC1 = 1.34677368708859836060; /* TRC0 * sqrt(3) / 2 */
+    private static final double TRC2 = 0.77756015077810708036; /* TRC0 / 2 */
+
+    @Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode();
+
+    @Child private UnitLengthNode unitLength = Unit.createLengthNode();
+    @Child private UnitToInchesNode unitToInches = Unit.createToInchesNode();
+
+    static {
+        Casts casts = new Casts(LPoints.class);
+        casts.arg(0).mustBe(abstractVectorValue());
+        casts.arg(1).mustBe(abstractVectorValue());
+        casts.arg(2).mustBe(numericValue(), Message.GENERIC, "grid.points: pch argument not implemented for characters yet").asIntegerVector();
+        casts.arg(3).mustBe(abstractVectorValue());
+    }
+
+    public static LPoints create() {
+        return LPointsNodeGen.create();
+    }
+
+    @Specialization
+    @TruffleBoundary
+    public Object doPoints(RAbstractVector xVec, RAbstractVector yVec, RAbstractIntVector pchVec, RAbstractVector sizeVec) {
+        GridContext ctx = GridContext.getContext();
+        GridDevice dev = ctx.getCurrentDevice();
+
+        RList currentVP = ctx.getGridState().getViewPort();
+        RList gpar = ctx.getGridState().getGpar();
+        DrawingContext drawingCtx = GPar.asDrawingContext(gpar);
+        double cex = GPar.getCex(gpar);
+        ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, dev);
+        ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
+
+        // Note: unlike in other drawing primitives, we only consider length of x
+        int length = unitLength.execute(xVec);
+        for (int i = 0; i < length; i++) {
+            Point loc = TransformMatrix.transLocation(Point.fromUnits(unitToInches, xVec, yVec, i, conversionCtx), vpTransform.transform);
+            double size = unitToInches.convertWidth(sizeVec, i, conversionCtx);
+            if (loc.isFinite() && Double.isFinite(size)) {
+                drawSymbol(drawingCtx, dev, cex, pchVec.getDataAt(i % pchVec.getLength()), size, loc.x, loc.y);
+            }
+        }
+        return RNull.instance;
+    }
+
+    // transcribed from engine.c function GESymbol
+
+    private void drawSymbol(DrawingContext drawingCtx, GridDevice dev, double cex, int pch, double size, double x, double y) {
+        // pch 0 - 25 are interpreted as geometrical shapes, pch from ascii code of ' ' are
+        // interpreted as corresponding ascii character, which should be drawn
+        switch (pch) {
+            case 46:
+                drawDot(drawingCtx, dev, cex, x, y);
+                break;
+            case 1:
+                drawOctahedron(drawingCtx, dev, GridColor.TRANSPARENT, size, x, y);
+                break;
+            case 16:
+                drawOctahedron(drawingCtx, dev, drawingCtx.getColor(), size, x, y);
+                break;
+            default:
+                throw RInternalError.unimplemented("grid.points unimplemented symbol " + pch);
+        }
+    }
+
+    private static void drawOctahedron(DrawingContext drawingCtx, GridDevice dev, GridColor fill, double size, double x, double y) {
+        GridColor originalFill = drawingCtx.getFillColor();
+        drawingCtx.setFillColor(fill);
+        dev.drawCircle(drawingCtx, x, y, RADIUS * size);
+        drawingCtx.setFillColor(originalFill);
+    }
+
+    private static void drawDot(DrawingContext drawingCtx, GridDevice dev, double cex, double x, double y) {
+        // NOTE: we are *filling* a rect with the current colour (we are not drawing the border AND
+        // we are not using the current fill colour)
+        GridColor originalFill = drawingCtx.getFillColor();
+        drawingCtx.setFillColor(drawingCtx.getColor());
+        drawingCtx.setColor(GridColor.TRANSPARENT);
+
+        /*
+         * The idea here is to use a 0.01" square, but to be of at least one device unit in each
+         * direction, assuming that corresponds to pixels. That may be odd if pixels are not square,
+         * but only on low resolution devices where we can do nothing better.
+         *
+         * For this symbol only, size is cex (see engine.c).
+         *
+         * Prior to 2.1.0 the offsets were always 0.5.
+         */
+        double xc = cex * 0.005;
+        double yc = cex * 0.005;
+        if (cex > 0 && xc < 0.5) {
+            xc = 0.5;
+        }
+        if (cex > 0 && yc < 0.5) {
+            yc = 0.5;
+        }
+        dev.drawRect(drawingCtx, x - xc, y - yc, x + xc, y + yc);
+
+        drawingCtx.setColor(drawingCtx.getFillColor());
+        drawingCtx.setFillColor(originalFill);
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPolygon.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPolygon.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4d8a917bd2bc2387d1faa7cb8b5026ee5fb37c3
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPolygon.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue;
+
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+public abstract class LPolygon extends RExternalBuiltinNode.Arg3 {
+    @Child private GridLinesNode gridLinesNode = GridLinesNode.createPolygon();
+
+    static {
+        Casts casts = new Casts(LPolygon.class);
+        casts.arg(0).mustBe(abstractVectorValue());
+        casts.arg(1).mustBe(abstractVectorValue());
+        casts.arg(2).mustBe(RList.class);
+    }
+
+    public static LPolygon create() {
+        return LPolygonNodeGen.create();
+    }
+
+    @Specialization
+    Object doLines(RAbstractVector x, RAbstractVector y, RList lengths) {
+        gridLinesNode.execute(x, y, lengths, null);
+        return RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRect.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRect.java
new file mode 100644
index 0000000000000000000000000000000000000000..a666b07edeab7b1d149f15aef7d7e945bdabf69e
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRect.java
@@ -0,0 +1,73 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.getDataAtMod;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.ViewPortTransform.GetViewPortTransformNode;
+import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+public abstract class LRect extends RExternalBuiltinNode.Arg6 {
+    @Child private Unit.UnitToInchesNode unitToInches = Unit.createToInchesNode();
+    @Child private Unit.UnitLengthNode unitLength = Unit.createLengthNode();
+    @Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode();
+
+    static {
+        Casts casts = new Casts(LRect.class);
+        casts.arg(0).mustBe(abstractVectorValue());
+        casts.arg(1).mustBe(abstractVectorValue());
+        casts.arg(2).mustBe(abstractVectorValue());
+        casts.arg(3).mustBe(abstractVectorValue());
+        casts.arg(4).mustBe(numericValue()).asDoubleVector();
+        casts.arg(5).mustBe(numericValue()).asDoubleVector();
+    }
+
+    public static LRect create() {
+        return LRectNodeGen.create();
+    }
+
+    @Specialization
+    @TruffleBoundary
+    public Object execute(RAbstractVector xVec, RAbstractVector yVec, RAbstractVector wVec, RAbstractVector hVec, RAbstractDoubleVector hjust, RAbstractDoubleVector vjust) {
+        GridContext ctx = GridContext.getContext();
+        GridDevice dev = ctx.getCurrentDevice();
+
+        RList currentVP = ctx.getGridState().getViewPort();
+        DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
+        ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, dev);
+        ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
+
+        int length = GridUtils.maxLength(unitLength, xVec, yVec, wVec, hVec);
+        for (int i = 0; i < length; i++) {
+            Size size = Size.fromUnits(unitToInches, wVec, hVec, i, conversionCtx);
+            // Note: once this is factored to drawing/recording: this transformation is necessary
+            // only for drawing
+            Point origLoc = Point.fromUnits(unitToInches, xVec, yVec, i, conversionCtx);
+            Point transLoc = TransformMatrix.transLocation(origLoc, vpTransform.transform);
+            Point loc = transLoc.justify(size, getDataAtMod(hjust, i), getDataAtMod(vjust, i));
+            dev.drawRect(drawingCtx, loc.x, loc.y, size.getWidth(), size.getHeight());
+        }
+        return RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LSegments.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LSegments.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a3dda410ed2fef75fcde352584f3f42e18df404
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LSegments.java
@@ -0,0 +1,87 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.ViewPortTransform.GetViewPortTransformNode;
+import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+/**
+ * Gets (vectors of) 4 coordinates (two points) and draws a line between them, unlike {@link LLines}
+ * which gets a vector of points and connects them all.
+ */
+public abstract class LSegments extends RExternalBuiltinNode.Arg5 {
+    @Child private Unit.UnitToInchesNode unitToInches = Unit.createToInchesNode();
+    @Child private Unit.UnitLengthNode unitLength = Unit.createLengthNode();
+    @Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode();
+    @Child private DrawArrowsNode drawArrowsNode = new DrawArrowsNode();
+
+    static {
+        Casts casts = new Casts(LSegments.class);
+        casts.arg(0).mustBe(abstractVectorValue());
+        casts.arg(1).mustBe(abstractVectorValue());
+        casts.arg(2).mustBe(abstractVectorValue());
+        casts.arg(3).mustBe(abstractVectorValue());
+        casts.arg(4).allowNull().mustBe(RList.class);
+    }
+
+    public static LSegments create() {
+        return LSegmentsNodeGen.create();
+    }
+
+    @Specialization
+    Object doSegments(RAbstractVector x0, RAbstractVector y0, RAbstractVector x1, RAbstractVector y1, RNull arrow) {
+        return doSegments(x0, y0, x1, y1, (RList) null);
+    }
+
+    @Specialization
+    @TruffleBoundary
+    Object doSegments(RAbstractVector x0, RAbstractVector y0, RAbstractVector x1, RAbstractVector y1, RList arrow) {
+        GridContext ctx = GridContext.getContext();
+        GridDevice dev = ctx.getCurrentDevice();
+
+        RList currentVP = ctx.getGridState().getViewPort();
+        DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
+        ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, dev);
+        ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
+
+        int length = GridUtils.maxLength(unitLength, x0, y0, x1, y1);
+        double[] xx = new double[2];
+        double[] yy = new double[2];
+        for (int i = 0; i < length; i++) {
+            Point loc1 = TransformMatrix.transLocation(Point.fromUnits(unitToInches, x0, y0, i, conversionCtx), vpTransform.transform);
+            Point loc2 = TransformMatrix.transLocation(Point.fromUnits(unitToInches, x1, y1, i, conversionCtx), vpTransform.transform);
+            if (!loc1.isFinite() || !loc2.isFinite()) {
+                continue;
+            }
+            xx[0] = loc1.x;
+            xx[1] = loc2.x;
+            yy[0] = loc1.y;
+            yy[1] = loc2.y;
+            dev.drawPolyLines(drawingCtx, xx, yy, 0, 2);
+            if (arrow != null) {
+                drawArrowsNode.drawArrows(xx, yy, 0, 2, i, arrow, true, true, conversionCtx);
+            }
+        }
+        return RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LText.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LText.java
new file mode 100644
index 0000000000000000000000000000000000000000..c22cf068da8963e2ec89eaa5ba5c1778029b4705
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LText.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.GridTextNode.addGridTextCasts;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.nodes.NodeCost;
+import com.oracle.truffle.api.nodes.NodeInfo;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+@NodeInfo(cost = NodeCost.NONE)
+public abstract class LText extends RExternalBuiltinNode.Arg7 {
+    static {
+        Casts casts = new Casts(LText.class);
+        addGridTextCasts(casts);
+        casts.arg(6).mustBe(logicalValue()).asLogicalVector().findFirst().map(toBoolean());
+    }
+
+    public static LText create() {
+        return LTextNodeGen.create();
+    }
+
+    @Specialization
+    Object drawText(RAbstractStringVector text, RAbstractVector x, RAbstractVector y, RAbstractDoubleVector hjust, RAbstractDoubleVector vjust, RAbstractDoubleVector rotation, boolean checkOverlap,
+                    @Cached("createDraw()") GridTextNode gridText) {
+        return gridText.gridText(text, x, y, hjust, vjust, rotation, checkOverlap, 0);
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LTextBounds.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LTextBounds.java
new file mode 100644
index 0000000000000000000000000000000000000000..fa1ec97d723de0ecc9ce446b956b9307558f32a5
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LTextBounds.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.GridTextNode.addGridTextCasts;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.constant;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.missingValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue;
+
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.nodes.NodeCost;
+import com.oracle.truffle.api.nodes.NodeInfo;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+@NodeInfo(cost = NodeCost.NONE)
+public abstract class LTextBounds extends RExternalBuiltinNode.Arg7 {
+    static {
+        Casts casts = new Casts(LTextBounds.class);
+        addGridTextCasts(casts);
+        casts.arg(6).returnIf(missingValue().or(nullValue()), constant(0)).asDoubleVector().findFirst();
+    }
+
+    public static LTextBounds create() {
+        return LTextBoundsNodeGen.create();
+    }
+
+    @Specialization
+    public Object textBounds(RAbstractStringVector text, RAbstractVector x, RAbstractVector y, RAbstractDoubleVector hjust, RAbstractDoubleVector vjust, RAbstractDoubleVector rotation, double theta,
+                    @Cached("createCalculateBounds()") GridTextNode gridText) {
+        return gridText.gridText(text, x, y, hjust, vjust, rotation, false, theta);
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LUnsetViewPort.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LUnsetViewPort.java
new file mode 100644
index 0000000000000000000000000000000000000000..0416b1f6746b01de0853d2a0d1c465683f893660
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LUnsetViewPort.java
@@ -0,0 +1,90 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asList;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asListOrNull;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
+
+public abstract class LUnsetViewPort extends RExternalBuiltinNode.Arg1 {
+    @Child private DoSetViewPort doSetViewPort = new DoSetViewPort();
+
+    static {
+        Casts casts = new Casts(LUnsetViewPort.class);
+        casts.arg(0).mustBe(numericValue()).asIntegerVector().findFirst();
+    }
+
+    public static LUnsetViewPort create() {
+        return LUnsetViewPortNodeGen.create();
+    }
+
+    @Specialization
+    @TruffleBoundary
+    Object unsetViewPort(int n) {
+        GridContext ctx = GridContext.getContext();
+        GridState gridState = ctx.getGridState();
+
+        // go n-steps up the view-port tree
+        RList gvp = gridState.getViewPort();
+        RList newVp = gvp;
+        for (int i = 0; i < n; i++) {
+            gvp = newVp;
+            newVp = asListOrNull(gvp.getDataAt(ViewPort.PVP_PARENT));
+            if (newVp == null) {
+                throw error(Message.GENERIC, "cannot pop the top-level viewport ('grid' and 'graphics' output mixed?)");
+            }
+        }
+
+        // gvp will be removed, newVp will be the new view-port
+        // first update children of newVp -> remove gvp
+        REnvironment children = (REnvironment) newVp.getDataAt(ViewPort.PVP_CHILDREN);
+        String gvpName = RRuntime.asString(gvp.getDataAt(ViewPort.VP_NAME));
+        safeRemoveFromEnv(children, gvpName);
+
+        // update newVp transform etc. because it will be the current vp, it has to be up to date
+        GridDevice device = ctx.getCurrentDevice();
+        if (ViewPort.updateDeviceSizeInVP(newVp, device)) {
+            // Note: like in other places calling this, why incremental == true, given that the
+            // device has changed? Don't we want to recalculate the whole tree?
+            doSetViewPort.calcViewportTransform(newVp, newVp.getDataAt(ViewPort.PVP_PARENT), true, device, GridState.getInitialGPar(device));
+        }
+
+        gridState.setGpar(asList(newVp.getDataAt(ViewPort.PVP_GPAR)));
+
+        // TODO: clipping
+        gridState.setViewPort(newVp);
+
+        // remove the parent link from the old viewport
+        gvp.setDataAt(gvp.getInternalStore(), ViewPort.PVP_PARENT, null);
+        return RNull.instance;
+    }
+
+    private void safeRemoveFromEnv(REnvironment children, String gvpName) {
+        try {
+            children.rm(gvpName);
+        } catch (PutException e) {
+            throw RInternalError.shouldNotReachHere("Cannot update view-port children environment");
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LUpViewPort.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LUpViewPort.java
new file mode 100644
index 0000000000000000000000000000000000000000..6e51c4b4d8704de111da056926b350af43c9c544
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LUpViewPort.java
@@ -0,0 +1,50 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.gte;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
+import static com.oracle.truffle.r.nodes.builtin.casts.fluent.CastNodeBuilder.newCastBuilder;
+
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.nodes.unary.CastNode;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+
+public abstract class LUpViewPort extends RExternalBuiltinNode.Arg1 {
+    @Child private CastNode castParentToViewPort = newCastBuilder().mustBe(RList.class, Message.GENERIC, "cannot pop the top-level viewport ('grid' and 'graphics' output mixed?)").buildCastNode();
+
+    static {
+        Casts casts = new Casts(LUpViewPort.class);
+        casts.arg(0).mustBe(numericValue()).asIntegerVector().findFirst(1).mustBe(gte(1));
+    }
+
+    public static LUpViewPort create() {
+        return LUpViewPortNodeGen.create();
+    }
+
+    @Specialization
+    Object upViewPort(int n) {
+        GridState gridState = GridContext.getContext().getGridState();
+        RList newViewPort = gridState.getViewPort();
+        for (int i = 0; i < n; i++) {
+            newViewPort = (RList) castParentToViewPort.execute(newViewPort.getDataAt(ViewPort.PVP_PARENT));
+        }
+        gridState.setViewPort(newViewPort);
+
+        // TODO: device changed? => calcViewportTransform for newViewPort
+        // TODO: update the clipping region
+        return RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Point.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Point.java
new file mode 100644
index 0000000000000000000000000000000000000000..ba2c5135203ec29e6c547b259fdfa90c2a97de14
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Point.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitToInchesNode;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+public final class Point {
+    final double x;
+    final double y;
+
+    public Point(double x, double y) {
+        this.x = x;
+        this.y = y;
+    }
+
+    public static Point fromUnits(UnitToInchesNode unitToInches, RAbstractVector x, RAbstractVector y, int index, UnitConversionContext ctx) {
+        double newX = unitToInches.convertX(x, index, ctx);
+        double newY = unitToInches.convertY(y, index, ctx);
+        return new Point(newX, newY);
+    }
+
+    public Point justify(Size size, double hjust, double vjust) {
+        return justify(size.getWidth(), size.getHeight(), hjust, vjust);
+    }
+
+    private Point justify(double width, double height, double hjust, double vjust) {
+        return new Point(GridUtils.justify(x, width, hjust), GridUtils.justify(y, height, vjust));
+    }
+
+    public boolean isFinite() {
+        return Double.isFinite(x) && Double.isFinite(y);
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/RGridCodeCall.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/RGridCodeCall.java
new file mode 100644
index 0000000000000000000000000000000000000000..7f6b3f18ea44819d4211a01a8db831b8213bd8db
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/RGridCodeCall.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.frame.FrameDescriptor;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.nodes.builtin.RInternalCodeBuiltinNode;
+import com.oracle.truffle.r.runtime.ArgumentsSignature;
+import com.oracle.truffle.r.runtime.RArguments;
+import com.oracle.truffle.r.runtime.RInternalCode;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
+
+/**
+ * Allows to call arbitrary R function from {@code fastrGrid.R} source.
+ */
+class RGridCodeCall extends Node {
+    private static final FrameDescriptor emptyFrameDescriptor = new FrameDescriptor("<fastrGrid.R tmp frame>");
+    @Child private RInternalCodeBuiltinNode child;
+
+    RGridCodeCall(String functionName) {
+        child = new RInternalCodeBuiltinNode(RContext.getInstance(), "grid", RInternalCode.loadSourceRelativeTo(GPar.class, "fastrGrid.R"), functionName);
+    }
+
+    static {
+        FrameSlotChangeMonitor.initializeFunctionFrameDescriptor("<fastrGrid.R tmp frame>", emptyFrameDescriptor);
+    }
+
+    public Object call(Object arg1) {
+        return execute(new RArgsValuesAndNames(new Object[]{arg1}, ArgumentsSignature.empty(1)));
+    }
+
+    public Object execute(RArgsValuesAndNames args) {
+        Object[] dummyFrameArgs = RArguments.createUnitialized();
+        VirtualFrame dummyFrame = Truffle.getRuntime().createVirtualFrame(dummyFrameArgs, emptyFrameDescriptor);
+        return child.call(dummyFrame, args);
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Size.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Size.java
new file mode 100644
index 0000000000000000000000000000000000000000..c428af3df6956bf7813712e41955f796e7d2e21f
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Size.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitToInchesNode;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+public final class Size {
+    private final double width;
+    private final double height;
+
+    public Size(double width, double height) {
+        this.width = width;
+        this.height = height;
+    }
+
+    public static Size fromUnits(UnitToInchesNode unitToInches, RAbstractVector wVec, RAbstractVector hVec, int index, UnitConversionContext conversionCtx) {
+        double w = unitToInches.convertWidth(wVec, index, conversionCtx);
+        double h = unitToInches.convertHeight(hVec, index, conversionCtx);
+        return new Size(w, h);
+    }
+
+    public double getWidth() {
+        return width;
+    }
+
+    public double getHeight() {
+        return height;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/TransformMatrix.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/TransformMatrix.java
new file mode 100644
index 0000000000000000000000000000000000000000..e1051c98a46937db800261a3ea626a87c04061f9
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/TransformMatrix.java
@@ -0,0 +1,118 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.runtime.nmath.MathConstants.M_PI;
+
+/**
+ * Operations on transformation (3x3) matrices.
+ */
+final class TransformMatrix {
+    private TransformMatrix() {
+        // only static members
+    }
+
+    static double[][] translation(double tx, double ty) {
+        double[][] m = identity();
+        m[2][0] = tx;
+        m[2][1] = ty;
+        return m;
+    }
+
+    static double[][] multiply(double[][] m1, double[][] m2) {
+        double[][] m = new double[3][3];
+        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];
+        return m;
+    }
+
+    static double[][] rotation(double theta) {
+        double thetarad = theta / 180 * M_PI;
+        double costheta = Math.cos(thetarad);
+        double sintheta = Math.sin(thetarad);
+        double[][] result = identity();
+        if (theta == 0) {
+            return result;
+        }
+        result[0][0] = costheta;
+        result[0][1] = sintheta;
+        result[1][0] = -sintheta;
+        result[1][1] = costheta;
+        return result;
+    }
+
+    static double[][] identity() {
+        double[][] result = new double[3][3];
+        result[0][0] = 1;
+        result[1][1] = 1;
+        result[2][2] = 1;
+        return result;
+    }
+
+    private static double[] location(double x, double y) {
+        return new double[]{x, y, 1};
+    }
+
+    private static double[] transLocation(double[] location, double[][] m) {
+        double[] res = new double[3];
+        res[0] = location[0] * m[0][0] + location[1] * m[1][0] + location[2] * m[2][0];
+        res[1] = location[0] * m[0][1] + location[1] * m[1][1] + location[2] * m[2][1];
+        res[2] = location[0] * m[0][2] + location[1] * m[1][2] + location[2] * m[2][2];
+        return res;
+    }
+
+    static Point transLocation(Point loc, double[][] m) {
+        double[] newLoc = transLocation(location(loc.x, loc.y), m);
+        return new Point(locationX(newLoc), locationY(newLoc));
+    }
+
+    private static double locationX(double[] loc) {
+        return loc[0];
+    }
+
+    private static double locationY(double[] loc) {
+        return loc[1];
+    }
+
+    /**
+     * Transforms the internal double matrix to R matrix flat array.
+     */
+    static double[] flatten(double[][] m) {
+        double[] res = new double[9];
+        for (int i = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++) {
+                res[i + j * 3] = m[i][j];
+            }
+        }
+        return res;
+    }
+
+    /**
+     * Reverse operation to {@link #flatten(double[][])}.
+     */
+    static double[][] fromFlat(double[] flat) {
+        double[][] res = new double[3][3];
+        for (int i = 0; i < 3; i++) {
+            for (int j = 0; j < 3; j++) {
+                res[i][j] = flat[i + j * 3];
+            }
+        }
+        return res;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Unit.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Unit.java
new file mode 100644
index 0000000000000000000000000000000000000000..4cb343dfbff085c8f8d78422d5d458984d28a2da
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Unit.java
@@ -0,0 +1,657 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asAbstractContainer;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asDouble;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asDoubleVector;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asIntVector;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asList;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asListOrNull;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.fmax;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.fmin;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.getDataAtMod;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.getDoubleAt;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.hasRClass;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.sum;
+import static com.oracle.truffle.r.library.fastrGrid.device.DrawingContext.INCH_TO_POINTS_FACTOR;
+
+import java.util.function.BiFunction;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.dsl.Cached;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.library.fastrGrid.UnitFactory.UnitLengthNodeGen;
+import com.oracle.truffle.r.library.fastrGrid.UnitFactory.UnitToInchesNodeGen;
+import com.oracle.truffle.r.library.fastrGrid.ViewPortTransform.GetViewPortTransformNode;
+import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext;
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode;
+import com.oracle.truffle.r.runtime.ArgumentsSignature;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.RInternalError;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.data.RAttributable;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RDoubleVector;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.RNull;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
+
+/**
+ * Note: internally in FastR Grid everything is in inches. However, some lists that are exposed to
+ * the R code should contain values in centimeters, we convert such values immediatelly once they
+ * enter our system.
+ */
+public class Unit {
+    static final String VALID_UNIT_ATTR = "valid.unit";
+
+    public static final int NPC = 0;
+    private static final int CM = 1;
+    public static final int INCHES = 2;
+    private static final int LINES = 3;
+    public static final int NATIVE = 4;
+    private static final int NULL = 5; /* only used in layout specifications */
+    private static final int SNPC = 6;
+    private static final int MM = 7;
+    /*
+     * Some units based on TeX's definition thereof
+     */
+    private static final int POINTS = 8; /* 72.27 pt = 1 in */
+    public static final int PICAS = 9; /* 1 pc = 12 pt */
+    public static final int BIGPOINTS = 10; /* 72 bp = 1 in */
+    public static final int DIDA = 11; /* 1157 dd = 1238 pt */
+    public static final int CICERO = 12; /* 1 cc = 12 dd */
+    public static final int SCALEDPOINTS = 13; /* 65536 sp = 1pt */
+    /*
+     * Some units which require an object to query for a value.
+     */
+    private static final int STRINGWIDTH = 14;
+    private static final int STRINGHEIGHT = 15;
+    public static final int STRINGASCENT = 16;
+    public static final int STRINGDESCENT = 17;
+    /*
+     * LINES now means multiples of the line height. This is multiples of the font size.
+     */
+    private static final int CHAR = 18;
+    private static final int GROBX = 19;
+    private static final int GROBY = 20;
+    private static final int GROBWIDTH = 21;
+    private static final int GROBHEIGHT = 22;
+    public static final int GROBASCENT = 23;
+    private static final int GROBDESCENT = 24;
+    private static final int LAST_NORMAL_UNIT = GROBDESCENT;
+    /*
+     * No longer used
+     */
+    private static final int MYLINES = 103;
+    private static final int MYCHAR = 104;
+    private static final int MYSTRINGWIDTH = 105;
+    private static final int MYSTRINGHEIGHT = 106;
+
+    // null layout arithmetic mode
+    private static final int L_adding = 1;
+    private static final int L_subtracting = 2;
+    private static final int L_summing = 3;
+    private static final int L_plain = 4;
+    private static final int L_maximising = 5;
+    private static final int L_minimising = 6;
+    private static final int L_multiplying = 7;
+
+    // attributes in the unit object and unit classes
+    private static final String UNIT_ATTR_DATA = "data";
+    private static final String UNIT_ATTR_UNIT_ID = "valid.unit";
+    private static final String UNIT_CLASS = "unit";
+    private static final String UNIT_ARITHMETIC_CLASS = "unit.arithmetic";
+    private static final String UNIT_LIST_CLASS = "unit.list";
+
+    private static final double CM_IN_INCH = 2.54;
+
+    public static double inchesToCm(double inches) {
+        return inches * CM_IN_INCH;
+    }
+
+    public static double cmToInches(double cm) {
+        return cm / CM_IN_INCH;
+    }
+
+    public static RAbstractDoubleVector newUnit(double value, int unitId) {
+        assert unitId > 0 && unitId <= LAST_NORMAL_UNIT;
+        RDoubleVector result = RDataFactory.createDoubleVector(new double[]{value}, RDataFactory.COMPLETE_VECTOR);
+        result.setClassAttr(RDataFactory.createStringVectorFromScalar(UNIT_CLASS));
+        result.setAttr(UNIT_ATTR_UNIT_ID, unitId);
+        result.setAttr(UNIT_ATTR_DATA, RNull.instance);
+        return result;
+    }
+
+    public static UnitLengthNode createLengthNode() {
+        return UnitLengthNode.create();
+    }
+
+    public static UnitToInchesNode createToInchesNode() {
+        return UnitToInchesNode.create();
+    }
+
+    static double convertFromInches(double value, int unitId, double vpSize, double scalemin, double scalemax, boolean isDimension, DrawingContext drawingCtx) {
+        switch (unitId) {
+            case NATIVE:
+                double addition = isDimension ? 0 : scalemin;
+                return addition + (value / vpSize) * (scalemax - scalemin);
+            case NPC:
+                return value / vpSize;
+            case CM:
+                return value * CM_IN_INCH;
+            case MM:
+                return value * CM_IN_INCH * 10;
+            case INCHES:
+                return value;
+            case POINTS:
+                return value * INCH_TO_POINTS_FACTOR;
+            case LINES:
+                return (value * INCH_TO_POINTS_FACTOR) / (drawingCtx.getFontSize() * drawingCtx.getLineHeight());
+            // following units are not supported even by original grid
+            case SNPC:
+            case MYCHAR:
+            case MYLINES:
+            case STRINGWIDTH:
+            case MYSTRINGWIDTH:
+            case STRINGHEIGHT:
+            case MYSTRINGHEIGHT:
+            case GROBX:
+            case GROBY:
+            case GROBWIDTH:
+            case GROBHEIGHT:
+            case NULL:
+            default:
+                throw RInternalError.unimplemented("unit type " + unitId + " in convertFromInches");
+        }
+    }
+
+    private static double convertToInches(double value, int unitId, RList data, UnitConversionContext ctx, AxisOrDimension axisOrDim) {
+        double vpSize = ctx.getViewPortSize(axisOrDim);
+        switch (unitId) {
+            case INCHES:
+                return value;
+            case NATIVE:
+                double tmp = axisOrDim.isDimension() ? value : (value - ctx.getScaleMin(axisOrDim));
+                return (tmp / (ctx.getScaleMax(axisOrDim) - ctx.getScaleMin(axisOrDim))) * vpSize;
+            case NPC:
+                return value * vpSize;
+            case POINTS:
+                return value / INCH_TO_POINTS_FACTOR;
+            case CM:
+                return value / CM_IN_INCH;
+            case MM:
+                return value / (CM_IN_INCH * 10);
+            case CHAR:
+            case MYCHAR:
+                return (value * ctx.drawingContext.getFontSize()) / INCH_TO_POINTS_FACTOR;
+            case LINES:
+            case MYLINES:
+                return (value * ctx.drawingContext.getFontSize() * ctx.drawingContext.getLineHeight()) / INCH_TO_POINTS_FACTOR;
+            case STRINGWIDTH:
+            case MYSTRINGWIDTH:
+                return ctx.device.getStringWidth(ctx.drawingContext, RRuntime.asString(data.getDataAt(0)));
+            case STRINGHEIGHT:
+            case MYSTRINGHEIGHT:
+                return ctx.device.getStringHeight(ctx.drawingContext, RRuntime.asString(data.getDataAt(0)));
+            case NULL:
+                return evaluateNullUnit(value, vpSize, ctx.nullLayoutMode, ctx.nullArithmeticMode);
+            default:
+                throw RInternalError.unimplemented("unit type " + unitId + " in convertToInches");
+        }
+    }
+
+    private static double evaluateNullUnit(double value, double vpSize, int nullLMode, int nullAMode) {
+        if (nullLMode > 0) {
+            return value;
+        }
+        switch (nullAMode) {
+            case L_plain:
+            case L_adding:
+            case L_subtracting:
+            case L_summing:
+            case L_multiplying:
+            case L_maximising:
+                return 0;
+            case L_minimising:
+                return vpSize;  // Note: GnuR has vpSize in "cm", so this will be in "cm"
+                                // too, does it matter?
+        }
+        return value;
+    }
+
+    static boolean isListUnit(Object unit) {
+        return unit instanceof RList && hasRClass((RAttributable) unit, UNIT_LIST_CLASS);
+    }
+
+    static boolean isArithmeticUnit(Object unit) {
+        return unit instanceof RList && hasRClass((RAttributable) unit, UNIT_ARITHMETIC_CLASS);
+    }
+
+    private static boolean isGrobUnit(int unitId) {
+        return unitId >= GROBX && unitId <= GROBDESCENT;
+    }
+
+    static double pureNullUnitValue(RAbstractContainer unit, int index) {
+        // TODO: convert to unit visitor
+        if (unit instanceof RAbstractDoubleVector) {
+            RAbstractDoubleVector simpleUnit = (RAbstractDoubleVector) unit;
+            return simpleUnit.getDataAt(index % simpleUnit.getLength());
+        } else if (isListUnit(unit)) {
+            return pureNullUnitValue((RAbstractContainer) ((RList) unit).getDataAt(index % unit.getLength()), 0);
+        } else if (isArithmeticUnit(unit)) {
+            ArithmeticUnit expr = ArithmeticUnit.asArithmeticUnit((RList) unit);
+            switch (expr.op) {
+                case "+":
+                    return pureNullUnitValue(expr.arg1, index) + pureNullUnitValue(expr.arg2, index);
+                case "-":
+                    return pureNullUnitValue(expr.arg1, index) - pureNullUnitValue(expr.arg2, index);
+                case "*":
+                    return asDouble(expr.arg1) * pureNullUnitValue(expr.arg2, index);
+                case "min":
+                case "max":
+                case "sum":
+                    double[] values = new double[expr.arg1.getLength()];
+                    for (int i = 0; i < values.length; i++) {
+                        values[i] = pureNullUnitValue(expr.arg1, i);
+                    }
+                    switch (expr.op) {
+                        case "min":
+                            return fmin(Double.MAX_VALUE, values);
+                        case "max":
+                            return fmax(Double.MIN_VALUE, values);
+                        case "sum":
+                            return sum(values);
+                        default:
+                            throw RInternalError.shouldNotReachHere("unexpected arithmetic unit operation");
+                    }
+            }
+
+        }
+        throw RInternalError.shouldNotReachHere("unexpected arithmetic unit type");
+    }
+
+    private static final class ArithmeticUnit {
+        public final String op;
+        public final RAbstractContainer arg1;
+        public final RAbstractContainer arg2;
+
+        ArithmeticUnit(String op, RAbstractContainer arg1, RAbstractContainer arg2) {
+            this.op = op;
+            this.arg1 = arg1;
+            this.arg2 = arg2;
+        }
+
+        static ArithmeticUnit asArithmeticUnit(RList unit) {
+            if (unit.getLength() <= 1) {
+                throw RError.error(RError.NO_CALLER, Message.GENERIC, "Invalid arithmetic unit (length <= 1).");
+            }
+            // Note: the operator is usually of type character, however in the R code, grid compares
+            // it to symbols, e.g. `x`. Our implementation here should work with symbols too thanks
+            // to the asStringVector() conversion.
+            String op = RRuntime.asString(unit.getDataAt(0));
+            RAbstractContainer arg1 = asAbstractContainer(unit.getDataAt(1));
+            if (op.equals("+") || op.equals("-") || op.equals("*")) {
+                if (unit.getLength() != 3) {
+                    throw RError.error(RError.NO_CALLER, Message.GENERIC, "Invalid arithmetic unit with binary operator and missing operand.");
+                }
+                return new ArithmeticUnit(op, arg1, asAbstractContainer(unit.getDataAt(2)));
+            }
+            if (op.equals("max") || op.equals("min") || op.equals("sum")) {
+                return new ArithmeticUnit(op, arg1, null);
+            }
+            throw RError.error(RError.NO_CALLER, Message.GENERIC, "Unexpected unit operator " + op);
+        }
+
+        public boolean isBinary() {
+            return arg2 != null;
+        }
+    }
+
+    abstract static class UnitNodeBase extends RBaseNode {
+        @Child private InheritsCheckNode inheritsArithmeticCheckNode = new InheritsCheckNode(UNIT_ARITHMETIC_CLASS);
+        @Child private InheritsCheckNode inheritsUnitListCheckNode = new InheritsCheckNode(UNIT_LIST_CLASS);
+
+        boolean isSimple(Object obj) {
+            return !inheritsArithmeticCheckNode.execute(obj) && !inheritsUnitListCheckNode.execute(obj);
+        }
+
+        boolean isArithmetic(Object obj) {
+            return inheritsArithmeticCheckNode.execute(obj);
+        }
+
+        boolean isUnitList(Object obj) {
+            return inheritsUnitListCheckNode.execute(obj);
+        }
+    }
+
+    public static final class IsRelativeUnitNode extends UnitNodeBase {
+        @Child private RGridCodeCall isPureNullCall = new RGridCodeCall("isPureNullUnit");
+
+        public boolean execute(Object unit, int index) {
+            // Note: converting 0-based java index to 1-based R index
+            Object result = isPureNullCall.execute(new RArgsValuesAndNames(new Object[]{unit, index + 1}, ArgumentsSignature.empty(2)));
+            byte resultByte;
+            if (result instanceof Byte) {
+                resultByte = (Byte) result;
+            } else if (result instanceof RAbstractLogicalVector) {
+                resultByte = ((RAbstractLogicalVector) result).getDataAt(0);
+            } else {
+                throw RInternalError.shouldNotReachHere("unexpected result type form isPuteNullUnit");
+            }
+            return RRuntime.fromLogical(resultByte);
+        }
+    }
+
+    /**
+     * Arithmetic unit objects can represent 'vectorized' expressions, in such case the 'length' is
+     * not simply the length of the underlying vector/list.
+     */
+    public abstract static class UnitLengthNode extends UnitNodeBase {
+        public static UnitLengthNode create() {
+            return UnitLengthNodeGen.create();
+        }
+
+        public abstract int execute(RAbstractContainer vector);
+
+        @Specialization(guards = "!isArithmetic(value)")
+        int doNormal(RAbstractContainer value) {
+            return value.getLength();
+        }
+
+        @Specialization(guards = "isArithmetic(list)")
+        int doArithmetic(RList list,
+                        @Cached("create()") UnitLengthNode recursiveLen) {
+            ArithmeticUnit arithmeticUnit = ArithmeticUnit.asArithmeticUnit(list);
+            if (arithmeticUnit.isBinary()) {
+                return Math.max(recursiveLen.execute(arithmeticUnit.arg1), recursiveLen.execute(arithmeticUnit.arg2));
+            }
+            return 1;   // op is max, min, sum
+        }
+    }
+
+    /**
+     * Used to discriminate between x axis, y axis, width, and height when doing unit conversions.
+     * The order should be the same as used in e.g. {@code L_convert}, which is 0 means x, 1 means
+     * y, 2 means width, 3 means height.
+     */
+    public enum AxisOrDimension {
+        X,
+        Y,
+        WIDTH,
+        HEIGHT;
+
+        private static final AxisOrDimension[] enumValues = values();
+
+        static AxisOrDimension fromInt(int value) {
+            assert value >= 0 && value < 4;
+            return enumValues[value];
+        }
+
+        public boolean isHorizontal() {
+            return this == X || this == WIDTH;
+        }
+
+        public boolean isDimension() {
+            return this == WIDTH || this == HEIGHT;
+        }
+    }
+
+    /**
+     * Wraps the data necessary for converting a unit to another unit. Note: {@code nullLMode} and
+     * {@code nullAMode} is only used for converting 'NULL' units and is only explicitly set when
+     * calculating layout. When e.g. drawing or calculating bounds, both should have default zero
+     * value.
+     */
+    public static final class UnitConversionContext {
+        public final Size viewPortSize;
+        public final ViewPortContext viewPortContext;
+        public final DrawingContext drawingContext;
+        public final GridDevice device;
+        public final int nullLayoutMode;
+        public final int nullArithmeticMode;
+
+        public UnitConversionContext(Size viewPortSize, ViewPortContext viewPortContext, GridDevice device, DrawingContext drawingContext) {
+            this(viewPortSize, viewPortContext, device, drawingContext, 0, 0);
+        }
+
+        public UnitConversionContext(Size viewPortSize, ViewPortContext viewPortContext, GridDevice device, DrawingContext drawingContext, int nullLMode, int nullAMode) {
+            this.viewPortSize = viewPortSize;
+            this.viewPortContext = viewPortContext;
+            this.device = device;
+            this.drawingContext = drawingContext;
+            this.nullLayoutMode = nullLMode;
+            this.nullArithmeticMode = nullAMode;
+        }
+
+        public double getViewPortSize(AxisOrDimension forAxisOrDim) {
+            return forAxisOrDim.isHorizontal() ? viewPortSize.getWidth() : viewPortSize.getHeight();
+        }
+
+        public double getScaleMin(AxisOrDimension forAxisOrDim) {
+            return forAxisOrDim.isHorizontal() ? viewPortContext.xscalemin : viewPortContext.yscalemin;
+        }
+
+        public double getScaleMax(AxisOrDimension forAxisOrDim) {
+            return forAxisOrDim.isHorizontal() ? viewPortContext.xscalemax : viewPortContext.yscalemax;
+        }
+    }
+
+    /**
+     * Normalizes grid unit object to a double value in inches. For convenience the index is
+     * interpreted as cyclic.
+     */
+    public abstract static class UnitToInchesNode extends UnitNodeBase {
+        @Child GrobUnitToInches grobUnitToInches;
+
+        public static UnitToInchesNode create() {
+            return UnitToInchesNodeGen.create();
+        }
+
+        public double convertX(RAbstractContainer vector, int index, UnitConversionContext ctx) {
+            return execute(vector, index, ctx, AxisOrDimension.X);
+        }
+
+        public double convertY(RAbstractContainer vector, int index, UnitConversionContext ctx) {
+            return execute(vector, index, ctx, AxisOrDimension.Y);
+        }
+
+        public double convertWidth(RAbstractContainer vector, int index, UnitConversionContext ctx) {
+            return execute(vector, index, ctx, AxisOrDimension.WIDTH);
+        }
+
+        public double convertHeight(RAbstractContainer vector, int index, UnitConversionContext ctx) {
+            return execute(vector, index, ctx, AxisOrDimension.HEIGHT);
+        }
+
+        public double convertDimension(RAbstractContainer vector, int index, UnitConversionContext ctx, boolean isWidth) {
+            return isWidth ? convertWidth(vector, index, ctx) : convertHeight(vector, index, ctx);
+        }
+
+        public abstract double execute(RAbstractContainer vector, int index, UnitConversionContext ctx, AxisOrDimension axisOrDim);
+
+        @Specialization(guards = "isSimple(value)")
+        double doNormal(RAbstractVector value, int index, UnitConversionContext ctx, AxisOrDimension axisOrDim) {
+            int unitId = getDataAtMod(asIntVector(value.getAttr(UNIT_ATTR_UNIT_ID)), index);
+            double scalarValue = getDoubleAt(value, index % value.getLength());
+            if (isGrobUnit(unitId)) {
+                RList grobList = asList(value.getAttr(UNIT_ATTR_DATA));
+                return getGrobUnitToInchesNode().execute(scalarValue, unitId, grobList.getDataAt(index % grobList.getLength()), ctx);
+            }
+            return convertToInches(scalarValue, unitId, asListOrNull(value.getAttr(UNIT_ATTR_DATA)), ctx, axisOrDim);
+        }
+
+        @Specialization(guards = "isUnitList(value)")
+        double doList(RList value, int index, UnitConversionContext ctx, AxisOrDimension axisOrDim,
+                        @Cached("create()") UnitToInchesNode recursiveNode) {
+            Object unwrapped = value.getDataAt(index % value.getLength());
+            if (unwrapped instanceof RAbstractVector) {
+                return recursiveNode.execute((RAbstractContainer) unwrapped, 0, ctx, axisOrDim);
+            }
+            throw error(Message.GENERIC, "Unexpected unit list with non-vector like element at index " + index);
+        }
+
+        @Specialization(guards = "isArithmetic(list)")
+        double doArithmetic(RList list, int index, UnitConversionContext ctx, AxisOrDimension axisOrDim,
+                        @Cached("create()") UnitLengthNode unitLengthNode,
+                        @Cached("create()") UnitToInchesNode recursiveNode) {
+            ArithmeticUnit expr = ArithmeticUnit.asArithmeticUnit(list);
+            BiFunction<RAbstractContainer, Integer, Double> recursive = (x, newNullAMode) -> recursiveNode.execute(x, index, getNewCtx(ctx, axisOrDim, newNullAMode), axisOrDim);
+            switch (expr.op) {
+                case "+":
+                    return recursive.apply(expr.arg1, L_adding) + recursive.apply(expr.arg2, L_adding);
+                case "-":
+                    return recursive.apply(expr.arg1, L_subtracting) - recursive.apply(expr.arg2, L_subtracting);
+                case "*":
+                    RAbstractDoubleVector left = asDoubleVector(expr.arg1);
+                    return left.getDataAt(index % left.getLength()) * recursive.apply(expr.arg2, L_multiplying);
+                default:
+                    break;
+            }
+
+            // must be aggregate operation
+            UnitConversionContext newCtx = getNewCtx(ctx, axisOrDim, getNullAMode(expr.op));
+            int len = unitLengthNode.execute(expr.arg1);
+            double[] values = new double[len];
+            for (int i = 0; i < len; i++) {
+                values[i] = recursiveNode.execute(expr.arg1, i, newCtx, axisOrDim);
+            }
+
+            switch (expr.op) {
+                case "min":
+                    return GridUtils.fmin(Double.MAX_VALUE, values);
+                case "max":
+                    return GridUtils.fmax(Double.MAX_VALUE, values);
+                case "sum":
+                    return GridUtils.sum(values);
+                default:
+                    throw RInternalError.shouldNotReachHere("The operation should have been validated in asArithmeticUnit method.");
+            }
+        }
+
+        // Note the catch: newNullAMode is applied only if the axisOrDim is dimension
+        private static UnitConversionContext getNewCtx(UnitConversionContext ctx, AxisOrDimension axisOrDim, int newNullAMode) {
+            return new UnitConversionContext(ctx.viewPortSize, ctx.viewPortContext, ctx.device, ctx.drawingContext, ctx.nullLayoutMode,
+                            axisOrDim.isDimension() ? newNullAMode : ctx.nullArithmeticMode);
+        }
+
+        private static int getNullAMode(String op) {
+            switch (op) {
+                case "min":
+                    return L_minimising;
+                case "max":
+                    return L_maximising;
+                case "sum":
+                    return L_summing;
+            }
+            return L_plain;
+        }
+
+        private GrobUnitToInches getGrobUnitToInchesNode() {
+            if (grobUnitToInches == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                grobUnitToInches = insert(new GrobUnitToInches());
+            }
+            return grobUnitToInches;
+        }
+    }
+
+    public static final class GrobUnitToInches extends Node {
+        @Child private RGridCodeCall preDrawCode = new RGridCodeCall("grobConversionPreDraw");
+        @Child private RGridCodeCall getUnitXY = new RGridCodeCall("grobConversionGetUnitXY");
+        @Child private RGridCodeCall postDrawCode = new RGridCodeCall("grobConversionPostDraw");
+        @Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode();
+
+        @Child private IsRelativeUnitNode isRelativeUnit = new IsRelativeUnitNode();
+        @Child private UnitToInchesNode unitToInchesNode;
+
+        // transcribed from unit.c function evaluateGrobUnit
+
+        public double execute(double value, int unitId, Object grob, UnitConversionContext conversionCtx) {
+            GridContext ctx = GridContext.getContext();
+            RList currentVP = ctx.getGridState().getViewPort();
+            getViewPortTransform.execute(currentVP, conversionCtx.device);
+
+            RList savedGPar = ctx.getGridState().getGpar();
+            Object savedGrob = ctx.getGridState().getCurrentGrob();
+
+            Object updatedGrob = preDrawCode.call(grob);
+
+            /*
+             * 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.
+             */
+            currentVP = ctx.getGridState().getViewPort();
+            RList currentGP = ctx.getGridState().getGpar();
+            ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, conversionCtx.device);
+            ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
+
+            // getUnitXY returns a list with either one or two items
+            RList unitxy = (RList) getUnitXY.execute(new RArgsValuesAndNames(new Object[]{updatedGrob, unitId}, ArgumentsSignature.empty(2)));
+            double result;
+            switch (unitId) {
+                case GROBX:
+                case GROBY:
+                    if (unitId == GROBY && isRelativeUnit.execute(unitxy.getDataAt(1), 0)) {
+                        double nullUnitValue = pureNullUnitValue((RAbstractContainer) unitxy.getDataAt(1), 0);
+                        result = evaluateNullUnit(nullUnitValue, vpTransform.size.getHeight(), conversionCtx.nullLayoutMode, conversionCtx.nullArithmeticMode);
+                    } else if (isRelativeUnit.execute(unitxy.getDataAt(0), 0)) {
+                        double nullUnitValue = pureNullUnitValue((RAbstractContainer) unitxy.getDataAt(0), 0);
+                        result = evaluateNullUnit(nullUnitValue, vpTransform.size.getWidth(), conversionCtx.nullLayoutMode, conversionCtx.nullArithmeticMode);
+                    } else {
+                        throw RInternalError.unimplemented("GrobUnitToInches from unit.c: 610");
+                    }
+                    break;
+                default:
+                    // should still be GROB_SOMETHING unit
+                    if (isRelativeUnit.execute(unitxy.getDataAt(0), 0)) {
+                        // Note: GnuR uses equivalent of vpTransform.size.getWidth() even for
+                        // GROBHEIGHT, bug?
+                        double nullUnitValue = pureNullUnitValue((RAbstractContainer) unitxy.getDataAt(0), 0);
+                        result = evaluateNullUnit(nullUnitValue, vpTransform.size.getWidth(), conversionCtx.nullLayoutMode, conversionCtx.nullArithmeticMode);
+                    } else {
+                        UnitConversionContext newConversionCtx = new UnitConversionContext(vpTransform.size, vpContext, conversionCtx.device, GPar.asDrawingContext(currentGP));
+                        initUnitToInchesNode();
+                        if (unitId == GROBWIDTH) {
+                            result = unitToInchesNode.convertWidth((RAbstractContainer) unitxy.getDataAt(0), 0, newConversionCtx);
+                        } else {
+                            // Note: GnuR uses height transform for both grobascent, grobdescent and
+                            // for height
+                            result = unitToInchesNode.convertHeight((RAbstractContainer) unitxy.getDataAt(0), 0, newConversionCtx);
+                        }
+                    }
+                    break;
+            }
+
+            postDrawCode.call(updatedGrob);
+            ctx.getGridState().setGpar(savedGPar);
+            ctx.getGridState().setCurrentGrob(savedGrob);
+            return value * result;
+        }
+
+        private void initUnitToInchesNode() {
+            if (unitToInchesNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                unitToInchesNode = UnitToInchesNode.create();
+            }
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java
new file mode 100644
index 0000000000000000000000000000000000000000..99fdd428d3e57bfff7a6cff318801655d1b3fdb2
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java
@@ -0,0 +1,166 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asDouble;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asList;
+import static com.oracle.truffle.r.library.fastrGrid.Unit.inchesToCm;
+
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
+import com.oracle.truffle.r.nodes.function.call.RExplicitCallNode;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RFunction;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+
+/**
+ * There is a notion of a view point, which is an ordinary list that user creates to define a view
+ * port. One such list is pushed using {@code pushViewpoint} it is transformed to a 'pushed
+ * viewpoint', which is a copy of the original view point and it has some additional attributes.
+ */
+class ViewPort {
+    /*
+     * Structure of a viewport
+     */
+    public static final int VP_X = 0;
+    public static final int VP_Y = 1;
+    public static final int VP_WIDTH = 2;
+    public static final int VP_HEIGHT = 3;
+    public static final int VP_JUST = 4;
+    public static final int VP_GP = 5;
+    public static final int VP_CLIP = 6;
+    public static final int VP_XSCALE = 7;
+    public static final int VP_YSCALE = 8;
+    public static final int VP_ANGLE = 9;
+    public static final int VP_LAYOUT = 10;
+    public static final int VP_LPOSROW = 11;
+    public static final int VP_LPOSCOL = 12;
+    public static final int VP_VALIDJUST = 13;
+    public static final int VP_VALIDLPOSROW = 14;
+    public static final int VP_VALIDLPOSCOL = 15;
+    public static final int VP_NAME = 16;
+    /*
+     * Additional structure of a pushedvp
+     */
+    public static final int PVP_PARENTGPAR = 17;
+    public static final int PVP_GPAR = 18;
+    public static final int PVP_TRANS = 19;
+    public static final int PVP_WIDTHS = 20;
+    public static final int PVP_HEIGHTS = 21;
+    public static final int PVP_WIDTHCM = 22;
+    public static final int PVP_HEIGHTCM = 23;
+    public static final int PVP_ROTATION = 24;
+    public static final int PVP_CLIPRECT = 25;
+    public static final int PVP_PARENT = 26;
+    public static final int PVP_CHILDREN = 27;
+    public static final int PVP_DEVWIDTHCM = 28;
+    public static final int PVP_DEVHEIGHTCM = 29;
+    /*
+     * Structure of a layout
+     */
+    private static final int LAYOUT_NROW = 0;
+    private static final int LAYOUT_NCOL = 1;
+    public static final int LAYOUT_WIDTHS = 2;
+    public static final int LAYOUT_HEIGHTS = 3;
+    public static final int LAYOUT_RESPECT = 4;
+    public static final int LAYOUT_VRESPECT = 5;
+    public static final int LAYOUT_MRESPECT = 6;
+    public static final int LAYOUT_JUST = 7;
+    private static final int LAYOUT_VJUST = 8;
+
+    /**
+     * Updates the device size in the viewport and returns {@code true} if the size has changed.
+     */
+    public static boolean updateDeviceSizeInVP(RList viewPort, GridDevice device) {
+        double devWidthCm = inchesToCm(device.getWidth());
+        boolean result = false;
+        if (Math.abs(devWidthCm - asDouble(viewPort.getDataAt(PVP_DEVWIDTHCM))) >= 1e-6) {
+            viewPort.setDataAt(viewPort.getInternalStore(), PVP_DEVWIDTHCM, devWidthCm);
+            result = true;
+        }
+        double devHeightCm = inchesToCm(device.getHeight());
+        if (Math.abs(devHeightCm - asDouble(viewPort.getDataAt(PVP_DEVHEIGHTCM))) >= 1e-6) {
+            viewPort.setDataAt(viewPort.getInternalStore(), PVP_DEVHEIGHTCM, devHeightCm);
+        }
+        return result;
+    }
+
+    /**
+     * Represents the integer values extracted from {@link #LAYOUT_NCOL} and {@link #LAYOUT_NROW}.
+     * In the R world, RNulls are valid values for those, we convert them to -1 to keep type safety.
+     * The values should be interpreted as 0-based indexes.
+     */
+    public static final class LayoutPos {
+        public final int colMin;
+        public final int colMax;
+        public final int rowMin;
+        public final int rowMax;
+        public final LayoutSize layoutSize;
+
+        LayoutPos(int colMin, int colMax, int rowMin, int rowMax, LayoutSize layoutSize) {
+            this.colMin = colMin;
+            this.colMax = colMax;
+            this.rowMin = rowMin;
+            this.rowMax = rowMax;
+            this.layoutSize = layoutSize;
+        }
+    }
+
+    /**
+     * The values should be interpreted as 0-based indexes.
+     */
+    public static final class LayoutSize {
+        public final int ncol;
+        public final int nrow;
+        public final double hjust;
+        public final double vjust;
+
+        private LayoutSize(int nrow, int ncol, double hjust, double vjust) {
+            this.ncol = ncol;
+            this.nrow = nrow;
+            this.hjust = hjust;
+            this.vjust = vjust;
+        }
+
+        public static LayoutSize fromViewPort(RList vp) {
+            RList layout = asList(vp.getDataAt(ViewPort.VP_LAYOUT));
+            int ncol = RRuntime.asInteger(layout.getDataAt(ViewPort.LAYOUT_NCOL));
+            int nrow = RRuntime.asInteger(layout.getDataAt(ViewPort.LAYOUT_NROW));
+            RAbstractDoubleVector just = (RAbstractDoubleVector) layout.getDataAt(ViewPort.LAYOUT_VJUST);
+            return new LayoutSize(nrow, ncol, just.getDataAt(0), just.getDataAt(1));
+        }
+    }
+
+    public static final class InitViewPortNode extends Node {
+        @Child private ReadVariableNode readGridTopLevel = ReadVariableNode.create("grid.top.level.vp");
+        @Child private RExplicitCallNode callNode = RExplicitCallNode.create();
+        @Child private DoSetViewPort doSetViewPort = new DoSetViewPort();
+
+        public RList execute(VirtualFrame frame) {
+            RFunction gridTopLevel = (RFunction) readGridTopLevel.execute(frame);
+            RList topVP = (RList) callNode.execute(frame, gridTopLevel, RArgsValuesAndNames.EMPTY);
+
+            GridDevice device = GridContext.getContext().getCurrentDevice();
+            // TODO: properly set the scale according to the current device
+            Object[] data = topVP.getDataWithoutCopying();
+            data[ViewPort.VP_XSCALE] = RDataFactory.createDoubleVector(new double[]{0, device.getWidth()}, RDataFactory.COMPLETE_VECTOR);
+            data[ViewPort.VP_YSCALE] = RDataFactory.createDoubleVector(new double[]{0, device.getHeight()}, RDataFactory.COMPLETE_VECTOR);
+            data[ViewPort.PVP_GPAR] = GridContext.getContext().getGridState().getGpar();
+            return doSetViewPort.doSetViewPort(topVP, false, true);
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortContext.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..0eb93b84f92ae34ba54ddf944a0e867bb9bfde62
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortContext.java
@@ -0,0 +1,55 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asDoubleVector;
+
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+
+public final class ViewPortContext {
+    public double xscalemin;
+    public double yscalemin;
+    public double xscalemax;
+    public double yscalemax;
+
+    private ViewPortContext() {
+    }
+
+    public static ViewPortContext createDefault() {
+        ViewPortContext result = new ViewPortContext();
+        result.xscalemin = 0;
+        result.yscalemin = 0;
+        result.xscalemax = 1;
+        result.yscalemax = 1;
+        return result;
+    }
+
+    public static ViewPortContext fromViewPort(RList viewPort) {
+        ViewPortContext result = new ViewPortContext();
+        RAbstractDoubleVector x = asDoubleVector(viewPort.getDataAt(ViewPort.VP_XSCALE));
+        if (x.getLength() != 2) {
+            throw RError.error(RError.NO_CALLER, Message.GENERIC, "view-port xscale must be vector of size 2");
+        }
+        result.xscalemin = x.getDataAt(0);
+        result.xscalemax = x.getDataAt(1);
+        RAbstractDoubleVector y = asDoubleVector(viewPort.getDataAt(ViewPort.VP_YSCALE));
+        if (y.getLength() != 2) {
+            throw RError.error(RError.NO_CALLER, Message.GENERIC, "view-port yscale must be vector of size 2");
+        }
+        result.yscalemin = y.getDataAt(0);
+        result.yscalemax = y.getDataAt(1);
+        return result;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortLocation.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortLocation.java
new file mode 100644
index 0000000000000000000000000000000000000000..2ea1a4d776efaa252721e8bcf1c58f6f81e81103
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortLocation.java
@@ -0,0 +1,49 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asAbstractContainer;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asDoubleVector;
+
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.model.RAbstractContainer;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+
+/**
+ * The vectors in this class represent a unit objects, therefore we cannot just have a double value
+ * for them. However, the unit object should contain only single value.
+ */
+public class ViewPortLocation {
+    public RAbstractContainer x;
+    public RAbstractContainer y;
+    public RAbstractContainer width;
+    public RAbstractContainer height;
+    public double hjust;
+    public double vjust;
+
+    public static ViewPortLocation fromViewPort(RList viewPort) {
+        ViewPortLocation r = new ViewPortLocation();
+        r.x = asAbstractContainer(viewPort.getDataAt(ViewPort.VP_X));
+        r.y = asAbstractContainer(viewPort.getDataAt(ViewPort.VP_Y));
+        r.width = asAbstractContainer(viewPort.getDataAt(ViewPort.VP_WIDTH));
+        r.height = asAbstractContainer(viewPort.getDataAt(ViewPort.VP_HEIGHT));
+        RAbstractDoubleVector just = asDoubleVector(viewPort.getDataAt(ViewPort.VP_VALIDJUST));
+        if (just.getLength() != 2) {
+            throw RError.error(RError.NO_CALLER, Message.GENERIC, "Unexpected size of layout justification vector.");
+        }
+        r.hjust = just.getDataAt(0);
+        r.vjust = just.getDataAt(1);
+        return r;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortTransform.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortTransform.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e8ea8aa082fd627a3857caa998e923d758989fc
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortTransform.java
@@ -0,0 +1,69 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 2001-3 Paul Murrell
+ * Copyright (c) 1998-2013, The R Core Team
+ * Copyright (c) 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
+import static com.oracle.truffle.r.nodes.builtin.casts.fluent.CastNodeBuilder.newCastBuilder;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.nodes.unary.CastNode;
+import com.oracle.truffle.r.runtime.data.RList;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+
+/**
+ * Holds the data of a viewport needed to perform transformations.
+ */
+public final class ViewPortTransform {
+    private final double rotationAngle;
+    public final double[][] transform;
+    public final Size size;
+
+    private ViewPortTransform(double width, double height, double rotationAngle, double[][] transform) {
+        this.size = new Size(width, height);
+        this.rotationAngle = rotationAngle;
+        this.transform = transform;
+    }
+
+    public static final class GetViewPortTransformNode extends Node {
+        @Child private CastNode castDoubleVector = newCastBuilder().mustBe(numericValue()).asDoubleVector().buildCastNode();
+        @Child private CastNode castScalarDouble = newCastBuilder().mustBe(numericValue()).asDoubleVector().findFirst().buildCastNode();
+        @Child private DoSetViewPort doSetViewPort;
+
+        public ViewPortTransform execute(RList viewPort, GridDevice device) {
+            if (ViewPort.updateDeviceSizeInVP(viewPort, device)) {
+                // Note: GnuR sets incremental parameter to true, but don't we need to recalculate
+                // the parent(s) as well?
+                initDoSetViewportNode();
+                doSetViewPort.calcViewportTransform(viewPort, viewPort.getDataAt(ViewPort.PVP_PARENT), true, device, GridState.getInitialGPar(device));
+            }
+            double width = Unit.cmToInches(getScalar(viewPort.getDataAt(ViewPort.PVP_WIDTHCM)));
+            double height = Unit.cmToInches(getScalar(viewPort.getDataAt(ViewPort.PVP_HEIGHTCM)));
+            double rotationAngle = getScalar(viewPort.getDataAt(ViewPort.VP_ANGLE));
+            RAbstractDoubleVector trans = (RAbstractDoubleVector) castDoubleVector.execute(viewPort.getDataAt(ViewPort.PVP_TRANS));
+            double[][] transform = TransformMatrix.fromFlat(trans.materialize().getDataWithoutCopying());
+            return new ViewPortTransform(width, height, rotationAngle, transform);
+        }
+
+        private void initDoSetViewportNode() {
+            if (doSetViewPort == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                doSetViewPort = new DoSetViewPort();
+            }
+        }
+
+        private double getScalar(Object value) {
+            return (double) castScalarDouble.execute(value);
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/DrawingContext.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/DrawingContext.java
new file mode 100644
index 0000000000000000000000000000000000000000..f3d42fa703d922981c033f164f3970438fc96131
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/DrawingContext.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid.device;
+
+/**
+ * Defines parameters for drawing, like color, line style etc.
+ */
+public interface DrawingContext {
+    double INCH_TO_POINTS_FACTOR = 72.27;
+
+    enum GridLineType {
+        // The order is important!
+        BLANK,
+        SOLID,
+        DASHED,
+        DOTTED,
+        DOTDASHED,
+        LONGDASH,
+        TWODASH;
+
+        private static final int LINE_TYPES_COUNT = 7;
+        private static final GridLineType[] allValues = values();
+
+        public static GridLineType fromInt(int num) {
+            if (num == -1) {
+                return BLANK;
+            }
+            assert num >= 1;
+            return allValues[(num - 1) % LINE_TYPES_COUNT + 1];
+        }
+    }
+
+    GridLineType getLineType();
+
+    GridColor getColor();
+
+    /**
+     * Alows to set the color drawing color of shape borders, lines and text.
+     */
+    void setColor(GridColor color);
+
+    /**
+     * Gets the font size in points.
+     *
+     * @see #INCH_TO_POINTS_FACTOR
+     */
+    double getFontSize();
+
+    /**
+     * Gets the height of a line in multiplies of the base line height.
+     */
+    double getLineHeight();
+
+    GridColor getFillColor();
+
+    /**
+     * Alows to set the fill color of shapes.
+     */
+    void setFillColor(GridColor color);
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridColor.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridColor.java
new file mode 100644
index 0000000000000000000000000000000000000000..e4cdd7517aca69d5ef477e040ab81279f046e6b4
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridColor.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid.device;
+
+/**
+ * Simple color representation, so that the device interface does not have to depend on a specific
+ * GUI framework.
+ */
+public class GridColor {
+    public static final int OPAQUE_ALPHA = 0xff;
+    private static final int TRANSPARENT_ALPHA = 0;
+    public static final GridColor TRANSPARENT = new GridColor(0, 0, 0, TRANSPARENT_ALPHA);
+
+    private final int value;
+
+    public GridColor(int red, int green, int blue, int alpha) {
+        value = ((alpha & 0xFF) << 24) |
+                        ((red & 0xFF) << 16) |
+                        ((green & 0xFF) << 8) |
+                        (blue & 0xFF);
+    }
+
+    public int getRed() {
+        return (value >> 16) & 0xFF;
+    }
+
+    public int getGreen() {
+        return (value >> 8) & 0xFF;
+    }
+
+    public int getBlue() {
+        return value & 0xFF;
+    }
+
+    public int getAlpha() {
+        return (value >> 24) & 0xff;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridDevice.java
new file mode 100644
index 0000000000000000000000000000000000000000..9e37a873bb46214d7035d2fefb13467cd7df174e
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/GridDevice.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid.device;
+
+import static com.oracle.truffle.r.library.fastrGrid.device.DrawingContext.INCH_TO_POINTS_FACTOR;
+
+/**
+ * Abstract device that can draw primitive shapes and text. All sizes and coordinates are specified
+ * in inches and angles in radians unless stated otherwise.
+ */
+public interface GridDevice {
+    void openNewPage();
+
+    void drawRect(DrawingContext ctx, double leftX, double topY, double width, double height);
+
+    /**
+     * Connects given points with a line, there has to be at least two points in order to actually
+     * draw somethig.
+     */
+    void drawPolyLines(DrawingContext ctx, double[] x, double[] y, int startIndex, int length);
+
+    void drawCircle(DrawingContext ctx, double centerX, double centerY, double radius);
+
+    /**
+     * Prints a string with left bottom corner at given position rotates by given angle anti clock
+     * wise, the centre of the rotation should be the bottom left corer.
+     */
+    void drawString(DrawingContext ctx, double leftX, double bottomY, double rotationAnticlockWise, String text);
+
+    /**
+     * @return The width of the device in inches.
+     */
+    double getWidth();
+
+    /**
+     * @return The height of the device in inches.
+     */
+    double getHeight();
+
+    /**
+     * May change the default values the of the initial drawing context instance.
+     * 
+     * @param ctx instance of drawing context to be altered.
+     */
+    default void initDrawingContext(DrawingContext ctx) {
+        // nop
+    }
+
+    double getStringWidth(DrawingContext ctx, String text);
+
+    /**
+     * Gets the height of a line of text in inches, the default implementation uses only the
+     * parameters from the drawing context, but we allow the device to override this calculation
+     * with something more precise.
+     */
+    default double getStringHeight(DrawingContext ctx, @SuppressWarnings("unused") String text) {
+        return (ctx.getLineHeight() * ctx.getFontSize()) / INCH_TO_POINTS_FACTOR;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/JFrameDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/JFrameDevice.java
new file mode 100644
index 0000000000000000000000000000000000000000..8821b8dd0f0d05d46e9d860e594b480aebe22065
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/device/JFrameDevice.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid.device;
+
+import static com.oracle.truffle.r.library.fastrGrid.device.DrawingContext.INCH_TO_POINTS_FACTOR;
+
+import java.awt.BasicStroke;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.HeadlessException;
+import java.awt.Paint;
+import java.awt.RenderingHints;
+import java.awt.Shape;
+import java.awt.Toolkit;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Rectangle2D;
+import java.util.function.Supplier;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+
+import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext.GridLineType;
+import com.oracle.truffle.r.runtime.RInternalError;
+
+public class JFrameDevice implements GridDevice {
+    // Grid's coordinate system has origin in left bottom corner and y axis grows from bottom to
+    // top. Moreover, the grid system uses inches as units. We use transformation to adjust the java
+    // coordinate system to the grid one. However, in the case of text rendering, we cannot simply
+    // turn upside down the y-axis, because the text would be upside down too, so for text rendering
+    // only, we reset the transformation completely and transform the coordinates ourselves
+    private static final double POINTS_IN_INCH = 72.;
+
+    private static BasicStroke solidStroke;
+    private static BasicStroke blankStroke;
+    private static BasicStroke dashedStroke;
+    private static BasicStroke longdashedStroke;
+    private static BasicStroke twodashedStroke;
+    private static BasicStroke dotdashedStroke;
+    private static BasicStroke dottedStroke;
+
+    private FastRFrame currentFrame;
+    private Graphics2D graphics;
+
+    @Override
+    public void openNewPage() {
+        initStrokes();
+        if (currentFrame == null) {
+            currentFrame = new FastRFrame();
+            currentFrame.setVisible(true);
+            graphics = (Graphics2D) currentFrame.getGraphics();
+            graphics.translate(0, currentFrame.getHeight());
+            graphics.scale(POINTS_IN_INCH, -POINTS_IN_INCH);
+            graphics.setStroke(new BasicStroke((float) (1d / POINTS_IN_INCH)));
+            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+            graphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
+        } else {
+            noTranform(() -> {
+                graphics.clearRect(0, 0, currentFrame.getWidth(), currentFrame.getHeight());
+                return null;
+            });
+        }
+    }
+
+    @Override
+    public void drawRect(DrawingContext ctx, double leftX, double topY, double width, double height) {
+        setContext(ctx);
+        drawShape(ctx, new Rectangle2D.Double(leftX, topY, width, height));
+    }
+
+    @Override
+    public void drawPolyLines(DrawingContext ctx, double[] x, double[] y, int startIndex, int length) {
+        assert startIndex >= 0 && startIndex < x.length && startIndex < y.length : "startIndex out of bounds";
+        assert length > 0 && (startIndex + length) <= Math.min(x.length, y.length) : "length out of bounds";
+        setContext(ctx);
+        Path2D.Double path = new Path2D.Double();
+        path.moveTo(x[startIndex], y[startIndex]);
+        for (int i = startIndex + 1; i < length; i++) {
+            path.lineTo(x[i], y[i]);
+        }
+        graphics.draw(path);
+    }
+
+    @Override
+    public void drawCircle(DrawingContext ctx, double centerX, double centerY, double radius) {
+        setContext(ctx);
+        drawShape(ctx, new Ellipse2D.Double(centerX - radius, centerY - radius, radius * 2d, radius * 2d));
+    }
+
+    @Override
+    public void drawString(DrawingContext ctx, double leftX, double bottomY, double rotationAnticlockWise, String text) {
+        setContext(ctx);
+        noTranform(() -> {
+            AffineTransform tr = graphics.getTransform();
+            tr.translate((float) (leftX * POINTS_IN_INCH), (float) (currentFrame.getContentPane().getHeight() - bottomY * POINTS_IN_INCH));
+            tr.rotate(-rotationAnticlockWise);
+            graphics.setTransform(tr);
+            setFontSize(ctx);
+            graphics.drawString(text, 0, 0);
+            return null;
+        });
+    }
+
+    @Override
+    public double getWidth() {
+        return currentFrame.getContentPane().getWidth() / POINTS_IN_INCH;
+    }
+
+    @Override
+    public double getHeight() {
+        return currentFrame.getContentPane().getHeight() / POINTS_IN_INCH;
+    }
+
+    @Override
+    public double getStringWidth(DrawingContext ctx, String text) {
+        setContext(ctx);
+        return noTranform(() -> {
+            setFontSize(ctx);
+            int swingUnits = graphics.getFontMetrics(graphics.getFont()).stringWidth(text);
+            return swingUnits / POINTS_IN_INCH;
+        });
+    }
+
+    @Override
+    public double getStringHeight(DrawingContext ctx, String text) {
+        setContext(ctx);
+        return noTranform(() -> {
+            setFontSize(ctx);
+            int swingUnits = graphics.getFont().getSize();
+            return swingUnits / POINTS_IN_INCH;
+        });
+    }
+
+    private void drawShape(DrawingContext drawingCtx, Shape shape) {
+        Paint paint = graphics.getPaint();
+        graphics.setPaint(fromGridColor(drawingCtx.getFillColor()));
+        graphics.fill(shape);
+        graphics.setPaint(paint);
+        graphics.draw(shape);
+    }
+
+    private void setContext(DrawingContext ctx) {
+        graphics.setColor(fromGridColor(ctx.getColor()));
+        graphics.setStroke(fromGridLineType(ctx.getLineType()));
+    }
+
+    private void setFontSize(DrawingContext ctx) {
+        float fontSize = (float) ((ctx.getFontSize() / INCH_TO_POINTS_FACTOR) * POINTS_IN_INCH);
+        graphics.setFont(graphics.getFont().deriveFont(fontSize));
+    }
+
+    private <T> T noTranform(Supplier<T> action) {
+        AffineTransform transform = graphics.getTransform();
+        graphics.setTransform(new AffineTransform());
+        T result = action.get();
+        graphics.setTransform(transform);
+        return result;
+    }
+
+    private static Color fromGridColor(GridColor color) {
+        return new Color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
+    }
+
+    private static BasicStroke fromGridLineType(GridLineType type) {
+        switch (type) {
+            case SOLID:
+                return solidStroke;
+            case BLANK:
+                return blankStroke;
+            case DASHED:
+                return dashedStroke;
+            case DOTDASHED:
+                return dotdashedStroke;
+            case DOTTED:
+                return dottedStroke;
+            case TWODASH:
+                return twodashedStroke;
+            case LONGDASH:
+                return longdashedStroke;
+            default:
+                throw RInternalError.shouldNotReachHere("unexpected value of GridLineType enum");
+        }
+    }
+
+    private static void initStrokes() {
+        if (solidStroke != null) {
+            return;
+        }
+        float defaultWidth = (float) (1. / POINTS_IN_INCH);
+        float dashSize = (float) (10. / POINTS_IN_INCH);
+        float dotSize = (float) (2. / POINTS_IN_INCH);
+        solidStroke = new BasicStroke((float) (1f / POINTS_IN_INCH));
+        blankStroke = new BasicStroke(0f);
+        dashedStroke = new BasicStroke(defaultWidth, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10f, new float[]{dashSize}, 0f);
+        dottedStroke = new BasicStroke(defaultWidth, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10f, new float[]{dotSize}, 0f);
+        dotdashedStroke = new BasicStroke(defaultWidth, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10f, new float[]{dotSize, dashSize}, 0f);
+        twodashedStroke = new BasicStroke(defaultWidth, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10f, new float[]{dashSize / 2f, dashSize}, 0f);
+        longdashedStroke = new BasicStroke(defaultWidth, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10f, new float[]{2f * dashSize}, 0f);
+    }
+
+    private static class FastRFrame extends JFrame {
+        private static final long serialVersionUID = 1L;
+        private final Dimension framePreferredSize = new Dimension(720, 720);
+        private final JPanel fastRComponent = new JPanel();
+
+        FastRFrame() throws HeadlessException {
+            super("FastR");
+            addCloseListener();
+            createUI();
+            center();
+        }
+
+        private void createUI() {
+            setLayout(new BorderLayout());
+            setSize(framePreferredSize);
+            add(fastRComponent, BorderLayout.CENTER);
+            fastRComponent.setPreferredSize(getSize());
+        }
+
+        private void addCloseListener() {
+            addWindowFocusListener(new WindowAdapter() {
+                @Override
+                public void windowClosing(WindowEvent e) {
+                    dispose();
+                }
+            });
+        }
+
+        private void center() {
+            Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+            Dimension frameSize = getSize();
+            int x = (screenSize.width - frameSize.width) / 2;
+            int y = (screenSize.height - frameSize.height) / 2;
+            setLocation(x, y);
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/fastrGrid.R b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/fastrGrid.R
new file mode 100644
index 0000000000000000000000000000000000000000..30a16598eaf5a19953b64fe03b5bf148e992072b
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/fastrGrid.R
@@ -0,0 +1,176 @@
+
+# Returns list with elements [[1]] - depth, zero if not found, [[2]] - the viewport, NULL if not found
+find.viewport <- function(name, strict, pvp, depth) {
+    if (length(ls(env=pvp$children)) == 0) {
+        return(list(FALSE, NULL))
+    } else if (exists(name, env=pvp$children, inherits=FALSE)) {
+        return(list(depth, get(name, env=pvp$children, inherits=FALSE)))
+    } else if (strict) {
+        return(list(FALSE, NULL))
+    } else {
+        return(find.in.children(name, pvp$children, depth + 1L))
+    }
+}
+
+# Note: in GnuR this takes "strict" from find.viewport and forwards it to recursive calls to find.viewport,
+# however, strict must be constant FALSE if find.in.children is called, so we leave it out.
+find.in.children <- function(name, children, depth) {
+  cpvps <- ls(env=children)
+  ncpvp <- length(cpvps)
+  count <- 0L
+  found <- FALSE
+  while (count < ncpvp && !found) {
+    result <- find.viewport(name, FALSE, get(cpvps[count + 1L], env=children), depth)
+    if (result[[1L]]) {
+        return(result);
+    }
+    count <- count + 1L
+  }
+  list(FALSE, NULL) # not found
+}
+
+L_downviewport <- function(name, strict) {
+    currVp <- .Call(grid:::L_currentViewport)
+    result <- find.viewport(name, strict, currVp, 1L);
+    if (result[[1]]) {
+        .Internal(.fastr.grid.doSetViewPort(result[[2L]], FALSE, FALSE));
+        return(result[[1L]])
+    } else {
+        stop(paste0("Viewport '", name, "' was not found"));
+    }
+}
+
+L_setviewport <- function(vp, hasParent) {
+    pushedVP <- grid:::pushedvp(vp);
+    .Internal(.fastr.grid.doSetViewPort(pushedVP, hasParent, TRUE));
+}
+
+L_unsetviewport <- function(n) {
+    gvp <- .Call(grid:::L_currentViewport)
+    newVp <- gvp;
+    for (i in 1:n) {
+        gvp <- newVp;
+        newVp <- gvp$parent;
+        if (is.null(newVp)) {
+            error("cannot pop the top-level viewport ('grid' and 'graphics' output mixed?)")
+        }
+    }
+    # remove
+}
+
+###################################################
+# Helper functions to deal with null and grob units
+# these functions are invoked from Java directly
+
+# Should be in sync with constants in Unit java class
+L_GROBX <- 19
+L_GROBY <- 20
+L_GROBWIDTH <- 21
+L_GROBHEIGHT <- 22
+L_GROBASCENT <- 23
+L_GROBDESCENT <- 24
+
+indexMod <- function(i, mod) ((i - 1) %% mod) + 1
+
+# if the grob is gPath, use it to find an actual grob
+# savedgrob - the grob from grid context
+findGrob <- function(grob, savedgrob) {
+    if (inherits(grob, "gPath")) {
+        if (is.null(savedgrob)) {
+            return(grid:::findGrobinDL(grob$name))
+        } else {
+            return(grid:::findGrobinChildren(grob$name, savedgrob$children))
+        }
+    }
+    grob
+}
+
+# this is called from FastR, it is simpler to implement this whole function in R.
+# GnuR uses series of install -> lang2 -> eval calls to achieve this from C.
+isPureNullUnit <- function(unit, index) {
+    if (inherits(unit, "unit.arithmetic")) {
+        return(isPureNullUnitArithmetic(unit, index));
+    } else if (inherits(unit, "unit.list")) {
+        return(isPureNullUnit(unit[[indexMod(index, length(unit))]], 1))
+    }
+    unitIdVec <- attr(unit, "valid.unit")
+    unitId <- unitIdVec[[indexMod(index, length(unitIdVec))]]
+    if (unitId == L_GROBWIDTH) {
+        return(isPureNullUnitGrobDim(unit, index, grid:::width))
+    } else if (unitId == L_GROBHEIGHT) {
+        return(isPureNullUnitGrobDim(unit, index, grid:::height))
+    }
+    unitId == 5 # L_NULL
+}
+
+getUnitData <- function(unit, index) {
+    result <- attr(unit, "data")
+    if (!is.list(result)) {
+        return(result)
+    }
+    result[[indexMod(index, length(result))]]
+}
+
+isPureNullUnitGrobDim <- function(unit, index, dimFunction) {
+    # Can a grob have "null" width/height?
+    # to be sure we cover everything, we keep the check here (like in GnuR)
+    savedgpar <- .Call(grid:::L_getGPar)
+    savedgrob <- .Call(grid:::L_getCurrentGrob)
+
+    grob <- findGrob(getUnitData(unit, index), savedgrob)
+
+    updatedgrob <- grid:::preDraw(grob)
+    result <- isPureNullUnit(dimFunction(updatedgrob), 1)
+    grid:::postDraw(updatedgrob)
+
+    .Call(grid:::L_setGPar, savedgpar)
+    .Call(grid:::L_setCurrentGrob, savedgrob)
+    result
+}
+
+isPureNullUnitArithmetic <- function(x, index) {
+    if (x$fname %in% c('+', '-')) {
+        # can this ever happen when Ops.unit raises error for two null units added/subtracted?
+        # to be sure we cover everything, we keep the check here (like in GnuR)
+        return(isPureNullUnit(x$arg1, index) && isPureNullUnit(x$arg2, index))
+    } else if (x$fname == '*') {
+        return(isPureNullUnit(x$arg2, index))
+    } else if (x$fname %in% c('min', 'max', 'sum')) {
+        return(all(sapply(seq_along(x$arg1), function(i) isPureNullUnit(x$arg1, i))))
+    } else {
+        error("unimplemented unit function");
+    }
+}
+
+# tests:
+# isPureNullUnit(grid:::unit.list(unit(c(1,2,3),c('mm', 'cm', 'null'))), 1) == FALSE
+# isPureNullUnit(grid:::unit.list(unit(c(1,2,3),c('mm', 'cm', 'null'))), 3) == TRUE
+# isPureNullUnit(3*unit(1,'mm'), 2) == FALSE
+# isPureNullUnit(3*unit(1,'null'), 2) == TRUE
+# isPureNullUnit(min(unit(1,'null')), 1) == TRUE
+# { gt <- grid.text("Hi there"); isPureNullUnit(unit(1, "grobwidth", gt), 1) } == FALSE
+# { gt <- grid.text("Hi there"); isPureNullUnit(unit(1, "grobheight", gt), 1) } == FALSE
+
+grobConversionPreDraw <- function(grobIn) {
+    grob <- findGrob(grobIn, .Call(grid:::L_getCurrentGrob))
+    grid:::preDraw(grob)
+}
+
+grobConversionGetUnitXY <- function(grob, unitId) {
+    if (unitId == L_GROBX || unitId == L_GROBY) {
+        return(list(grid:::xDetails(grob), grid:::yDetails(grob)))
+    } else if (unitId == L_GROBWIDTH) {
+        return(list(grid:::width(grob)))
+    } else if (unitId == L_GROBHEIGHT) {
+        return(list(grid:::height(grob)))
+    } else if (unitId == L_GROBDESCENT) {
+        return(list(grid:::descentDetails(grob)))
+    } else if (unitId == L_GROBASCENT) {
+        return(list(grid:::ascentDetails(grob)))
+    }
+    error("grobConversionGetUnitXY: not a grob unit.")
+}
+
+grobConversionPostDraw <- function(grob) {
+    grid:::postDraw(grob)
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/InitWindowedDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/InitWindowedDevice.java
new file mode 100644
index 0000000000000000000000000000000000000000..d6314346b22dd1fc81e28524a31f56f0b1a27bc6
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/InitWindowedDevice.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid.grDevices;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.r.library.fastrGrid.GridContext;
+import com.oracle.truffle.r.library.fastrGrid.GridState;
+import com.oracle.truffle.r.library.fastrGrid.graphics.RGridGraphicsAdapter;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.data.RNull;
+
+/**
+ * Node that handles the {@code C_X11} external calls. Those calls may be initiated from either the
+ * {@code X11} function or FastR specific {@code awt} function. In either case the result is that
+ * the AWT window is opened and ready for drawing.
+ */
+public final class InitWindowedDevice extends RExternalBuiltinNode {
+    static {
+        Casts.noCasts(InitWindowedDevice.class);
+    }
+
+    @Override
+    @TruffleBoundary
+    protected Object call(RArgsValuesAndNames args) {
+        GridState gridState = GridContext.getContext().getGridState();
+        if (!gridState.isDeviceInitialized()) {
+            GridContext.getContext().getCurrentDevice().openNewPage();
+            gridState.setDeviceInitialized();
+        }
+        RGridGraphicsAdapter.setCurrentDevice(args.getLength() == 0 ? "awt" : "X11cairo");
+        return RNull.instance;
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/R/fastrGridDevices.R b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/R/fastrGridDevices.R
new file mode 100644
index 0000000000000000000000000000000000000000..16a15ce9b2ba2313874a6d1e22eca5c237f15dfc
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/R/fastrGridDevices.R
@@ -0,0 +1,30 @@
+# Copyright (c) 2017, 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.
+
+eval(expression({
+    # This should be preffered way of starting the FastR java device.
+    # For compatibility reasons, both X11 and awt end up calling C_X11.
+    # In the future, this function may support extra parameters like a
+    # reference to java 2D graphics object, which will be used for the drawing.
+    awt <- function(...) {
+        .External2(grDevices:::C_X11)
+    }
+}), asNamespace("grDevices"))
\ No newline at end of file
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/package-info.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..f8363aacb3318093101e3a1d9ea3b3e1509a9a5e
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/grDevices/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+/**
+ * Compatibility layer for grDevices packages.
+ *
+ * With FastR grid we use {@link com.oracle.truffle.r.library.fastrGrid.device.GridDevice} instead
+ * of the abstraction used by GnuR. This is compatibility layer that provides implementation of some
+ * of the externals that manipulate the device and forwards them to corresponding methods on FastR
+ * grid side.
+ */
+package com.oracle.truffle.r.library.fastrGrid.grDevices;
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/CPar.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/CPar.java
new file mode 100644
index 0000000000000000000000000000000000000000..e4b6fd0f7e2e4f7a777aceb713ceaef3941a37fd
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/CPar.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.library.fastrGrid.graphics;
+
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.r.library.fastrGrid.GridContext;
+import com.oracle.truffle.r.library.fastrGrid.device.GridDevice;
+import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.RRuntime;
+import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RList;
+
+public class CPar extends RExternalBuiltinNode {
+    static {
+        Casts.noCasts(CPar.class);
+    }
+
+    @Override
+    @TruffleBoundary
+    protected Object call(RArgsValuesAndNames args) {
+        if (args.getSignature().getNonNullCount() > 0) {
+            throw error(Message.GENERIC, "Using par for setting device parameters is not supported in FastR grid emulation mode.");
+        }
+
+        GridDevice device = GridContext.getContext().getCurrentDevice();
+        Object[] names = args.getArguments();
+        // unwrap list if it is the first argument
+        if (names.length == 1) {
+            Object first = args.getArgument(0);
+            if (first instanceof RList) {
+                names = ((RList) first).getDataWithoutCopying();
+            }
+        }
+
+        Object[] result = new Object[names.length];
+        String[] resultNames = new String[names.length];
+        for (int i = 0; i < names.length; i++) {
+            resultNames[i] = RRuntime.asString(names[i]);
+            result[i] = getParam(resultNames[i], device);
+        }
+        return RDataFactory.createList(result, RDataFactory.createStringVector(resultNames, RDataFactory.COMPLETE_VECTOR));
+    }
+
+    private Object getParam(String name, GridDevice device) {
+        switch (name) {
+            case "din":
+                return RDataFactory.createDoubleVector(new double[]{device.getWidth(), device.getHeight()}, RDataFactory.COMPLETE_VECTOR);
+            default:
+                throw RError.nyi(RError.NO_CALLER, "C_Par paramter '" + name + "'");
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/RGridGraphicsAdapter.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/RGridGraphicsAdapter.java
new file mode 100644
index 0000000000000000000000000000000000000000..4d4629bc7f1e29d3c33ec63f6b7ad9a7ad8324b3
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/RGridGraphicsAdapter.java
@@ -0,0 +1,73 @@
+/*
+ * This material is distributed under the GNU General Public License
+ * Version 2. You may review the terms of this license at
+ * http://www.gnu.org/licenses/gpl-2.0.html
+ *
+ * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
+ * Copyright (C) 1998 Ross Ihaka
+ * Copyright (c) 1998--2014, The R Core Team
+ * Copyright (c) 2002--2010, The R Foundation
+ * Copyright (C) 2005--2006, Morten Welinder
+ * Copyright (c) 2014, 2017, Oracle and/or its affiliates
+ *
+ * All rights reserved.
+ */
+package com.oracle.truffle.r.library.fastrGrid.graphics;
+
+import com.oracle.truffle.r.library.fastrGrid.FastRGridExternalLookup;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
+import com.oracle.truffle.r.runtime.ROptions;
+import com.oracle.truffle.r.runtime.ROptions.OptionsException;
+import com.oracle.truffle.r.runtime.context.RContext;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RPairList;
+import com.oracle.truffle.r.runtime.env.REnvironment;
+
+/**
+ * Initialization of graphics package emulation for the purposes of FastR grid package
+ * implementation.
+ *
+ * FastR exposes two devices: the null device and 'awt' device, and adds function 'awt' to the
+ * grDevices package. The 'awt' function ends up calling 'C_X11' (the same as the 'X11' function
+ * from grDevices), we capture that call in {@link FastRGridExternalLookup} and replace it with our
+ * own logic. This way we also "implement" 'X11' device with java awt should anyone try to activate
+ * it.
+ *
+ * Moreover, we change the value of option "device" to our "awt" function so that when e.g. lattice
+ * tries to open the default device it uses 'awt'. If the future this should be either 'awt' for
+ * interactive sessions, or some image format device for batch sessions. We should also honor the
+ * R_INTERACTIVE_DEVICE and R_DEFAULT_DEVICE environment variables.
+ */
+public class RGridGraphicsAdapter {
+    private static final String NULL_DEVICE = "null device";
+    /**
+     * The graphics devices system maintains two variables .Device and .Devices in the base
+     * environment both are always set: .Devices gives a list of character vectors of the names of
+     * open devices, .Device is the element corresponding to the currently active device. The null
+     * device will always be open.
+     */
+    private static final String DOT_DEVICE = ".Device";
+    private static final String DOT_DEVICES = ".Devices";
+
+    public static void initialize() {
+        setCurrentDevice(NULL_DEVICE);
+        ROptions.ContextStateImpl options = RContext.getInstance().stateROptions;
+        try {
+            options.setValue("device", "awt");
+        } catch (OptionsException e) {
+            RError.warning(RError.NO_CALLER, Message.GENERIC, "FastR could not set the 'device' options to awt.");
+        }
+    }
+
+    public static void setCurrentDevice(String name) {
+        REnvironment baseEnv = REnvironment.baseEnv();
+        baseEnv.safePut(DOT_DEVICE, name);
+        Object devices = baseEnv.get(DOT_DEVICES);
+        if (devices instanceof RPairList) {
+            ((RPairList) devices).appendToEnd(RDataFactory.createPairList(name));
+        } else {
+            baseEnv.safePut(DOT_DEVICES, RDataFactory.createPairList(name));
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/package-info.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e19a7b99e7e0e5242d1b4783c80c3083f754354
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/package-info.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+/**
+ * Compatibility layer for graphics package.
+ *
+ * Some R code that uses mainly grid for drawing resorts to several graphics package functions,
+ * especially in order to control or query information about device(s).
+ */
+package com.oracle.truffle.r.library.fastrGrid.graphics;
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grDevices/DevicesCCalls.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grDevices/DevicesCCalls.java
deleted file mode 100644
index d7ba80d07c943bd2112a373a122927c0bb1da8dd..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grDevices/DevicesCCalls.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.grDevices;
-
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.emptyStringVector;
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
-import static com.oracle.truffle.r.nodes.builtin.casts.fluent.CastNodeBuilder.newCastBuilder;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.r.library.grDevices.DevicesCCallsFactory.C_DevOffNodeGen;
-import com.oracle.truffle.r.library.grDevices.pdf.PdfGraphicsDevice;
-import com.oracle.truffle.r.library.graphics.core.GraphicsEngineImpl;
-import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
-import com.oracle.truffle.r.nodes.unary.CastNode;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RNull;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
-
-public class DevicesCCalls {
-    public abstract static class C_DevOff extends RExternalBuiltinNode.Arg1 {
-        public static C_DevOff create() {
-            return C_DevOffNodeGen.create();
-        }
-
-        static {
-            Casts casts = new Casts(C_DevOff.class);
-            casts.arg(0).asIntegerVector().findFirst();
-        }
-
-        @Specialization
-        public Object doCall(int deviceIndex) {
-            GraphicsEngineImpl.getInstance().killGraphicsDeviceByIndex(deviceIndex);
-            return RNull.instance;
-        }
-    }
-
-    public static final class C_DevCur extends RExternalBuiltinNode.Arg0 {
-
-        @Override
-        @TruffleBoundary
-        public Object execute() {
-            return GraphicsEngineImpl.getInstance().getCurrentGraphicsDeviceIndex();
-        }
-    }
-
-    public static final class C_PDF extends RExternalBuiltinNode {
-
-        @Child private CastNode extractFontsNode = newCastBuilder().mapNull(emptyStringVector()).mustBe(stringValue()).asStringVector().buildCastNode();
-        @Child private CastNode asStringNode = newCastBuilder().asStringVector().findFirst().buildCastNode();
-        @Child private CastNode asDoubleNode = newCastBuilder().asDoubleVector().findFirst().buildCastNode();
-        @Child private CastNode asLogicalNode = newCastBuilder().asLogicalVector().findFirst().buildCastNode();
-        @Child private CastNode asIntNode = newCastBuilder().asIntegerVector().findFirst().buildCastNode();
-
-        static {
-            Casts.noCasts(C_PDF.class);
-        }
-
-        @SuppressWarnings("unused")
-        @Override
-        @TruffleBoundary
-        public Object call(RArgsValuesAndNames args) {
-            new PdfGraphicsDevice(extractParametersFrom(args));
-            // todo implement devices addition
-            return RNull.instance;
-        }
-
-        private PdfGraphicsDevice.Parameters extractParametersFrom(RArgsValuesAndNames args) {
-            PdfGraphicsDevice.Parameters result = new PdfGraphicsDevice.Parameters();
-            result.filePath = asString(args.getArgument(0));
-            result.paperSize = asString(args.getArgument(1));
-            result.fontFamily = asString(args.getArgument(2));
-            result.encoding = asString(args.getArgument(3));
-            result.bg = asString(args.getArgument(4));
-            result.fg = asString(args.getArgument(5));
-            result.width = asDouble(castVector(args.getArgument(6)));
-            result.height = asDouble(castVector(args.getArgument(7)));
-            result.pointSize = asDouble(castVector(args.getArgument(8)));
-            result.oneFile = asLogical(castVector(args.getArgument(9)));
-            result.pageCenter = asLogical(castVector(args.getArgument(10)));
-            result.title = asString(args.getArgument(11));
-            result.fonts = extractFontsFrom(args.getArgument(12));
-
-            result.majorVersion = asInt(castVector(args.getArgument(13)));
-            result.minorVersion = asInt(castVector(args.getArgument(14)));
-            result.colormodel = asString(args.getArgument(15));
-            result.useDingbats = asLogical(castVector(args.getArgument(16)));
-            result.useKerning = asLogical(castVector(args.getArgument(17)));
-            result.fillOddEven = asLogical(castVector(args.getArgument(18)));
-            result.compress = asLogical(castVector(args.getArgument(19)));
-            return result;
-        }
-
-        private String asString(Object value) {
-            return (String) asStringNode.execute(value);
-        }
-
-        private int asInt(Object value) {
-            return (Integer) asIntNode.execute(value);
-        }
-
-        private double asDouble(Object value) {
-            return (Double) asDoubleNode.execute(value);
-        }
-
-        private byte asLogical(Object value) {
-            return (Byte) asLogicalNode.execute(value);
-        }
-
-        private String[] extractFontsFrom(Object inputArgument) {
-            return ((RAbstractStringVector) extractFontsNode.execute(inputArgument)).materialize().getDataCopy();
-        }
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grDevices/NullGraphicsDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grDevices/NullGraphicsDevice.java
deleted file mode 100644
index 0ec439c90287e887c4869771a65ef7bad33ac17f..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grDevices/NullGraphicsDevice.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.grDevices;
-
-import com.oracle.truffle.r.library.graphics.core.DrawingParameters;
-import com.oracle.truffle.r.library.graphics.core.GraphicsDevice;
-import com.oracle.truffle.r.library.graphics.core.geometry.Coordinates;
-
-public final class NullGraphicsDevice implements GraphicsDevice {
-    private static final NullGraphicsDevice instance = new NullGraphicsDevice();
-
-    public static NullGraphicsDevice getInstance() {
-        return instance;
-    }
-
-    @Override
-    public void deactivate() {
-        throw createExceptionForMethod("deactivate");
-    }
-
-    @Override
-    public void activate() {
-        throw createExceptionForMethod("activate");
-    }
-
-    @Override
-    public void close() {
-        throw createExceptionForMethod("close");
-    }
-
-    @Override
-    public DrawingParameters getDrawingParameters() {
-        throw createExceptionForMethod("getDrawingParameters");
-    }
-
-    @Override
-    public void setMode(Mode newMode) {
-        throw createExceptionForMethod("setMode");
-    }
-
-    @Override
-    public Mode getMode() {
-        throw createExceptionForMethod("getMode");
-    }
-
-    @Override
-    public void setClipRect(double x1, double y1, double x2, double y2) {
-        throw createExceptionForMethod("setClipRect");
-    }
-
-    @Override
-    public void drawPolyline(Coordinates coordinates, DrawingParameters drawingParameters) {
-        throw createExceptionForMethod("drawPolyline");
-    }
-
-    private static RuntimeException createExceptionForMethod(String methodName) {
-        return new IllegalStateException("Call to " + methodName + " of Null-device");
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grDevices/fastrgd/FastRGraphicsDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grDevices/fastrgd/FastRGraphicsDevice.java
deleted file mode 100644
index ca8976b7907f25bacb5b9ac22c8ee65720fc7f19..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grDevices/fastrgd/FastRGraphicsDevice.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.grDevices.fastrgd;
-
-import static com.oracle.truffle.r.library.graphics.core.geometry.AxisDirection.EAST;
-import static com.oracle.truffle.r.library.graphics.core.geometry.AxisDirection.NORTH;
-
-import java.util.Arrays;
-import java.util.function.Function;
-
-import com.oracle.truffle.r.library.graphics.FastRFrame;
-import com.oracle.truffle.r.library.graphics.core.DrawingParameters;
-import com.oracle.truffle.r.library.graphics.core.GraphicsDevice;
-import com.oracle.truffle.r.library.graphics.core.drawables.DrawableObject;
-import com.oracle.truffle.r.library.graphics.core.drawables.PolylineDrawableObject;
-import com.oracle.truffle.r.library.graphics.core.drawables.StringDrawableObject;
-import com.oracle.truffle.r.library.graphics.core.geometry.Axis;
-import com.oracle.truffle.r.library.graphics.core.geometry.CoordinateSystem;
-import com.oracle.truffle.r.library.graphics.core.geometry.Coordinates;
-import com.oracle.truffle.r.library.graphics.core.geometry.CoordinatesFactory;
-import com.oracle.truffle.r.library.graphics.core.geometry.DoubleCoordinates;
-
-/**
- * Default interactive FastR graphics device.
- */
-public class FastRGraphicsDevice implements GraphicsDevice {
-    private static final double GNUR_DEFAULT_MAX_X = 1;
-    private static final Axis GNUR_DEFAULT_X_AXIS = new Axis(0, GNUR_DEFAULT_MAX_X, EAST);
-    private static final Axis GNUR_DEFAULT_Y_AXIS = new Axis(0, 1, NORTH);
-    private static final double MARGIN = GNUR_DEFAULT_MAX_X * 0.1; // the margin for each side of
-    // 10% of a screen
-    // compress resulting image to have a small margin on all sides
-    private static final double COMPRESS_RATION = 1. - MARGIN * 1.8;
-
-    private Mode mode = Mode.GRAPHICS_OFF;
-    private FastRFrame fastRFrame;
-    private CoordinateSystem currentCoordinateSystem = new CoordinateSystem(GNUR_DEFAULT_X_AXIS, GNUR_DEFAULT_Y_AXIS);
-
-    @Override
-    public void deactivate() {
-        // todo impl
-    }
-
-    @Override
-    public void activate() {
-        // todo impl
-    }
-
-    @Override
-    public void close() {
-        // todo impl
-    }
-
-    @Override
-    public DrawingParameters getDrawingParameters() {
-        return null;
-    }
-
-    @Override
-    public void setMode(Mode newMode) {
-        mode = newMode;
-    }
-
-    @Override
-    public Mode getMode() {
-        return mode;
-    }
-
-    @Override
-    public void setClipRect(double x1, double y1, double x2, double y2) {
-        // todo impl
-    }
-
-    @Override
-    public void drawPolyline(Coordinates coordinates, DrawingParameters drawingParameters) {
-        // todo continue from GEPolyline() of engine.c
-        Coordinates convertedCoords = CoordinatesFactory.withRatioAndShift(coordinates, COMPRESS_RATION, MARGIN);
-        addDrawableObject(new PolylineDrawableObject(currentCoordinateSystem, convertedCoords));
-        drawBounds();
-        drawXYLabelsFor(coordinates);
-    }
-
-    private void drawBounds() {
-        // x,y in range [0,1]
-        double[] boundsXYPairs = {0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0};
-        Coordinates bounds = CoordinatesFactory.createByXYPairs(boundsXYPairs);
-        Coordinates compressedBounds = CoordinatesFactory.withRatioAndShift(bounds, COMPRESS_RATION, MARGIN);
-        addDrawableObject(new PolylineDrawableObject(currentCoordinateSystem, compressedBounds));
-    }
-
-    private void drawXYLabelsFor(Coordinates coordinates) {
-        drawLabelsForCoordinates(coordinates.getXCoordinatesAsDoubles(), MARGIN, 0.01, // just small
-                        // shift
-                        d -> CoordinatesFactory.createWithSameY(d, 0));
-        drawLabelsForCoordinates(coordinates.getYCoordinatesAsDoubles(), 0, MARGIN, d -> CoordinatesFactory.createWithSameX(0, d));
-    }
-
-    private void drawLabelsForCoordinates(double[] coordinates, double xShift, double yShift, Function<double[], DoubleCoordinates> xYConverter) {
-        int length = coordinates.length;
-        double[] sortedCoords = new double[length];
-        // copy to avoid side-effects on a caller side
-        System.arraycopy(coordinates, 0, sortedCoords, 0, length);
-        Arrays.sort(sortedCoords);
-        String[] labels = composeLabelsFor(sortedCoords);
-        DoubleCoordinates xYCoords = xYConverter.apply(sortedCoords);
-        Coordinates shiftedCoords = CoordinatesFactory.withRatioAndShift(xYCoords, COMPRESS_RATION, xShift, yShift);
-        addDrawableObject(new StringDrawableObject(currentCoordinateSystem, shiftedCoords, labels));
-    }
-
-    private static String[] composeLabelsFor(double[] doubles) {
-        return Arrays.stream(doubles).mapToObj(String::valueOf).toArray(String[]::new);
-    }
-
-    private FastRFrame getFastRFrame() {
-        if (fastRFrame == null || !fastRFrame.isVisible()) {
-            fastRFrame = new FastRFrame();
-            fastRFrame.setVisible(true);
-        }
-        return fastRFrame;
-    }
-
-    private void addDrawableObject(DrawableObject drawableObject) {
-        getFastRFrame().getFastRComponent().addDrawableObject(drawableObject);
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grDevices/pdf/PdfGraphicsDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grDevices/pdf/PdfGraphicsDevice.java
deleted file mode 100644
index 9b7302823dec1669faa446b3a71a2723d628cbe4..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grDevices/pdf/PdfGraphicsDevice.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.grDevices.pdf;
-
-import com.oracle.truffle.r.library.graphics.core.DrawingParameters;
-import com.oracle.truffle.r.library.graphics.core.GraphicsDevice;
-import com.oracle.truffle.r.library.graphics.core.geometry.Coordinates;
-import com.oracle.truffle.r.runtime.RRuntime;
-
-public class PdfGraphicsDevice implements GraphicsDevice {
-    @SuppressWarnings("unused") private final Parameters deviceParameters;
-
-    public PdfGraphicsDevice(Parameters deviceParameters) {
-        this.deviceParameters = deviceParameters;
-    }
-
-    @Override
-    public void deactivate() {
-
-    }
-
-    @Override
-    public void activate() {
-
-    }
-
-    @Override
-    public void close() {
-
-    }
-
-    @Override
-    public DrawingParameters getDrawingParameters() {
-        return null;
-    }
-
-    @Override
-    public void setMode(Mode newMode) {
-
-    }
-
-    @Override
-    public Mode getMode() {
-        return null;
-    }
-
-    @Override
-    public void setClipRect(double x1, double y1, double x2, double y2) {
-
-    }
-
-    @Override
-    public void drawPolyline(Coordinates coordinates, DrawingParameters drawingParameters) {
-
-    }
-
-    public static class Parameters {
-        public String filePath;
-        public String paperSize = "special";
-        public String fontFamily = "Helvetica";
-        public String encoding = "default";
-        public String bg = "transparent";
-        public String fg = "black";
-        public double width = 7.;
-        public double height = 7.;
-        public double pointSize = 12.;
-        public byte oneFile = RRuntime.LOGICAL_TRUE;
-        public byte pageCenter = RRuntime.LOGICAL_TRUE;
-        public String title = "R Graphics Output";
-        public String[] fonts;
-        public int majorVersion = 1;
-        public int minorVersion = 4;
-        public String colormodel = "srgb";
-        public byte useDingbats = RRuntime.LOGICAL_TRUE;
-        public byte useKerning = RRuntime.LOGICAL_TRUE;
-        public byte fillOddEven = RRuntime.LOGICAL_FALSE;
-        public byte compress = RRuntime.LOGICAL_TRUE;
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/BaseGraphicsSystem.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/BaseGraphicsSystem.java
deleted file mode 100644
index 77d572f534a7ef158c16829b4d7fa953fbaca328..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/BaseGraphicsSystem.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics;
-
-import com.oracle.truffle.r.library.graphics.core.AbstractGraphicsSystem;
-
-/**
- * Denotes to the 'base' in GNUR terms graphics system.
- */
-public class BaseGraphicsSystem extends AbstractGraphicsSystem {
-    private final GraphicsEventsListener graphicsEventsListener = (graphicsEvent, graphicsDevice) -> {
-    };
-
-    @Override
-    public GraphicsEventsListener getGraphicsEventsListener() {
-        return graphicsEventsListener;
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/FastRComponent.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/FastRComponent.java
deleted file mode 100644
index 5f395fecd1afe721a1f2f435c417169f7b88fcd0..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/FastRComponent.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics;
-
-import java.awt.Dimension;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import javax.swing.JComponent;
-
-import com.oracle.truffle.r.library.graphics.core.drawables.DrawableObject;
-import com.oracle.truffle.r.library.graphics.core.geometry.CoordinateSystem;
-
-public class FastRComponent extends JComponent {
-
-    private static final long serialVersionUID = 1L;
-
-    private final List<DrawableObject> displayList = Collections.synchronizedList(new ArrayList<>());
-
-    private boolean shouldDraw;
-    private CoordinateSystem coordinateSystem;
-
-    /**
-     * Note! Called from ED thread.
-     */
-    @Override
-    public void doLayout() {
-        super.doLayout();
-        Dimension size = getSize();
-        coordinateSystem = new CoordinateSystem(0, size.getWidth(), 0, size.getHeight());
-        shouldDraw = true;
-        recalculateDisplayList();
-    }
-
-    private void recalculateDisplayList() {
-        synchronized (displayList) {
-            displayList.stream().forEach(drawableObject -> drawableObject.recalculateForDrawingIn(coordinateSystem));
-        }
-    }
-
-    /**
-     * Note! Called from ED thread.
-     */
-    @Override
-    protected void paintComponent(Graphics g) {
-        super.paintComponent(g);
-        Graphics2D g2 = (Graphics2D) g;
-        if (shouldDraw) {
-            drawDisplayListOn(g2);
-        }
-    }
-
-    private void drawDisplayListOn(Graphics2D g2) {
-        synchronized (displayList) {
-            displayList.stream().forEach(drawableObject -> drawableObject.drawOn(g2));
-        }
-    }
-
-    public void addDrawableObject(DrawableObject drawableObject) {
-        synchronized (displayList) {
-            displayList.add(drawableObject);
-        }
-        shouldDraw = true;
-        if (coordinateSystem != null) {
-            drawableObject.recalculateForDrawingIn(coordinateSystem);
-            repaint();
-        }
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/FastRFrame.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/FastRFrame.java
deleted file mode 100644
index 9f1463e8549ecc02f5eca556a94f7e22426d590b..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/FastRFrame.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (c) 2014, 2016, 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.
- */
-package com.oracle.truffle.r.library.graphics;
-
-import java.awt.BorderLayout;
-import java.awt.Dimension;
-import java.awt.HeadlessException;
-import java.awt.Toolkit;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-
-import javax.swing.JFrame;
-
-public class FastRFrame extends JFrame {
-
-    private static final long serialVersionUID = 1L;
-
-    private final Dimension framePreferredSize = new Dimension(500, 500);
-    private final FastRComponent fastRComponent = new FastRComponent();
-
-    public FastRFrame() throws HeadlessException {
-        super("FastR");
-        addCloseListener();
-        createUI();
-        center();
-    }
-
-    private void createUI() {
-        setLayout(new BorderLayout());
-        setSize(framePreferredSize);
-        add(fastRComponent, BorderLayout.CENTER);
-        fastRComponent.setPreferredSize(getSize());
-    }
-
-    private void addCloseListener() {
-        addWindowFocusListener(new WindowAdapter() {
-            @Override
-            public void windowClosing(WindowEvent e) {
-                dispose();
-            }
-        });
-    }
-
-    private void center() {
-        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
-        Dimension frameSize = getSize();
-        int x = (screenSize.width - frameSize.width) / 2;
-        int y = (screenSize.height - frameSize.height) / 2;
-        setLocation(x, y);
-    }
-
-    public FastRComponent getFastRComponent() {
-        return fastRComponent;
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/GraphicsCCalls.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/GraphicsCCalls.java
deleted file mode 100644
index adee6f49eac5deef5d74256f2190bad20ace8108..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/GraphicsCCalls.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics;
-
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.doubleValue;
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.size;
-import static com.oracle.truffle.r.nodes.builtin.casts.fluent.CastNodeBuilder.newCastBuilder;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.r.library.graphics.core.DrawingParameters;
-import com.oracle.truffle.r.library.graphics.core.GraphicsDevice;
-import com.oracle.truffle.r.library.graphics.core.GraphicsEngine;
-import com.oracle.truffle.r.library.graphics.core.GraphicsEngineImpl;
-import com.oracle.truffle.r.library.graphics.core.geometry.Coordinates;
-import com.oracle.truffle.r.library.graphics.core.geometry.CoordinatesFactory;
-import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
-import com.oracle.truffle.r.nodes.unary.CastNode;
-import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RDoubleVector;
-import com.oracle.truffle.r.runtime.data.RNull;
-
-public class GraphicsCCalls {
-    public static final class C_PlotXY extends RExternalBuiltinNode {
-
-        @Child private CastNode castXYNode = newCastBuilder().mustBe(doubleValue().and(size(2))).asDoubleVector().buildCastNode();
-
-        static {
-            Casts.noCasts(C_PlotXY.class);
-        }
-
-        @Override
-        @TruffleBoundary
-        public RNull call(RArgsValuesAndNames args) {
-            RDoubleVector xyVector = (RDoubleVector) castXYNode.execute(args.getArgument(0));
-            getGraphicsEngine().setCurrentGraphicsDeviceMode(GraphicsDevice.Mode.GRAPHICS_ON);
-            drawWithLines(xyVector);
-            return RNull.instance;
-        }
-
-        private static void drawWithLines(RDoubleVector xyVector) {
-            // todo implement coordinate systems units conversion like in GConvert (graphics.c)
-            setClipRect();
-            DrawingParameters adoptedParameters = adoptCurrentDeviceDrawingParameters();
-            Coordinates coordinates = CoordinatesFactory.createByXYVector(xyVector);
-            getGraphicsEngine().drawPolyline(coordinates, adoptedParameters);
-        }
-
-        private static DrawingParameters adoptCurrentDeviceDrawingParameters() {
-            // todo Now adoption as for today. Transcribe from gcontextFromGM() (graphics.c)
-            return getCurrentGraphicsDevice().getDrawingParameters();
-        }
-
-        private static void setClipRect() {
-            // todo Transcrive from Gclip() (graphics.c)
-            getGraphicsEngine().setCurrentGraphicsDeviceClipRect(0, 0, 0, 0);
-        }
-
-        private static GraphicsDevice getCurrentGraphicsDevice() {
-            return getGraphicsEngine().getCurrentGraphicsDevice();
-        }
-
-        private static GraphicsEngine getGraphicsEngine() {
-            return GraphicsEngineImpl.getInstance();
-        }
-    }
-
-    public static final class C_Par extends RExternalBuiltinNode {
-
-        static {
-            Casts.noCasts(C_Par.class);
-        }
-
-        @Override
-        @TruffleBoundary
-        public Object call(RArgsValuesAndNames args) {
-            // pch
-            return RDataFactory.createIntVectorFromScalar(1);
-        }
-    }
-
-    @SuppressWarnings("unused")
-    public static final class C_mtext extends RExternalBuiltinNode {
-        private Object text;
-        private double side = 3.;
-        private double line = 0.;
-        private boolean outer = true;
-        private double adj = RRuntime.DOUBLE_NA;
-        private double at = RRuntime.DOUBLE_NA;
-        private double padj = RRuntime.DOUBLE_NA;
-        private double cex = RRuntime.DOUBLE_NA;
-        private double col = RRuntime.DOUBLE_NA;
-        private double font = RRuntime.DOUBLE_NA;
-
-        @Child private CastNode firstDoubleCast = newCastBuilder().asDoubleVector().findFirst().buildCastNode();
-
-        static {
-            Casts.noCasts(C_mtext.class);
-        }
-
-        @Override
-        @TruffleBoundary
-        public Object call(RArgsValuesAndNames args) {
-            extractArgumentsFrom(args);
-            return RNull.instance;
-        }
-
-        private void extractArgumentsFrom(RArgsValuesAndNames args) {
-            // text = args.getArgument(0); // postpone for now
-            side = extractFirstDoubleValueFrom(args.getArgument(1));
-            line = extractFirstDoubleValueFrom(args.getArgument(2));
-            // outer = extractFirstDoubleValueFrom(args.getArgument(3));
-            at = extractFirstDoubleValueFrom(args.getArgument(4));
-            adj = extractFirstDoubleValueFrom(args.getArgument(5));
-            padj = extractFirstDoubleValueFrom(args.getArgument(6));
-            cex = extractFirstDoubleValueFrom(args.getArgument(7));
-            // col = extractFirstDoubleValueFrom(args.getArgument(8));
-            font = extractFirstDoubleValueFrom(args.getArgument(9));
-        }
-
-        private double extractFirstDoubleValueFrom(Object arg) {
-            return (Double) firstDoubleCast.execute(arg);
-        }
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/RGraphics.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/RGraphics.java
index 11c4a71ce6b38c8afb385a9a5be2670ce5a16a14..7e9d763981801aa287fc67072cd539d53e96ce6e 100644
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/RGraphics.java
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/RGraphics.java
@@ -16,50 +16,24 @@ package com.oracle.truffle.r.library.graphics;
 
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import com.oracle.truffle.r.library.graphics.core.GraphicsEngine;
-import com.oracle.truffle.r.library.graphics.core.GraphicsEngineImpl;
+import com.oracle.truffle.r.library.fastrGrid.graphics.RGridGraphicsAdapter;
 import com.oracle.truffle.r.runtime.FastROptions;
-import com.oracle.truffle.r.runtime.context.ConsoleHandler;
-import com.oracle.truffle.r.runtime.context.RContext;
-import com.oracle.truffle.r.runtime.data.RDataFactory;
-import com.oracle.truffle.r.runtime.data.RPairList;
-import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.env.REnvironment;
-import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.ffi.CallRFFI;
+import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.ffi.NativeCallInfo;
 
 /**
- * A placeholder to keep {@code REngine} limited to calling the {@link #initialize} method. Two
- * possible implementations are available:
- * <ul>
- * <li>Native graphics from GnuR</li>
- * <li>Internal (Java) graphics, very incomplete implementation</li>
- * </ul>
- * The default is native graphics, selected by a startup option. Graphics is not virtualized, so
- * multiple contexts all share the same underlying implementation which is initialized exactly once.
- *
+ * A placeholder to keep {@code REngine} limited to calling the {@link #initialize} method. The code
+ * in R has a hard-coded invocation to InitGraphics in it. This initialization either invokes it
+ * too, or it runs a Java version of it if the internal grid package implementation is to be used.
  */
 public class RGraphics {
-    private static final RStringVector NULL_DEVICE = RDataFactory.createStringVectorFromScalar("null device");
-    /**
-     * The graphics devices system maintains two variables .Device and .Devices in the base
-     * environment both are always set: .Devices gives a list of character vectors of the names of
-     * open devices, .Device is the element corresponding to the currently active device. The null
-     * device will always be open.
-     */
-    private static final String DOT_DEVICE = ".Device";
-    private static final String DOT_DEVICES = ".Devices";
     private static final AtomicBoolean initialized = new AtomicBoolean();
 
     public static void initialize() {
         if (initialized.compareAndSet(false, true)) {
-            if (FastROptions.UseInternalGraphics.getBooleanValue()) {
-                REnvironment baseEnv = REnvironment.baseEnv();
-                baseEnv.safePut(DOT_DEVICE, NULL_DEVICE);
-                RPairList devices = RDataFactory.createPairList(NULL_DEVICE);
-                baseEnv.safePut(DOT_DEVICES, devices);
-                registerBaseGraphicsSystem();
+            if (FastROptions.UseInternalGridGraphics.getBooleanValue()) {
+                RGridGraphicsAdapter.initialize();
             } else {
                 DLL.DLLInfo dllInfo = DLL.findLibrary("graphics");
                 DLL.SymbolHandle symbolHandle = DLL.findSymbol("InitGraphics", dllInfo);
@@ -68,18 +42,4 @@ public class RGraphics {
             }
         }
     }
-
-    private static void registerBaseGraphicsSystem() {
-        try {
-            getGraphicsEngine().registerGraphicsSystem(new BaseGraphicsSystem());
-        } catch (Exception e) {
-            e.printStackTrace();
-            ConsoleHandler consoleHandler = RContext.getInstance().getConsoleHandler();
-            consoleHandler.println("Unable to register base graphics system");
-        }
-    }
-
-    private static GraphicsEngine getGraphicsEngine() {
-        return GraphicsEngineImpl.getInstance();
-    }
 }
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/AbstractGraphicsSystem.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/AbstractGraphicsSystem.java
deleted file mode 100644
index b401568ffa06f437c046cded9d3cbc363ed84de2..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/AbstractGraphicsSystem.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core;
-
-public abstract class AbstractGraphicsSystem implements GraphicsSystem {
-    private final GraphicsSystemParameters graphicsSystemParameters = new GraphicsSystemParameters();
-    private int id;
-
-    protected GraphicsSystemParameters getGraphicsSystemParameters() {
-        return graphicsSystemParameters;
-    }
-
-    @Override
-    public void setId(int id) {
-        this.id = id;
-    }
-
-    @Override
-    public int getId() {
-        return id;
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/DrawingParameters.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/DrawingParameters.java
deleted file mode 100644
index 69a1d7075210e9646fe22300db31d18f1678b7ab..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/DrawingParameters.java
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core;
-
-// todo implement GEContext data structure (GraphicsEngine.h)
-public final class DrawingParameters {
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsDevice.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsDevice.java
deleted file mode 100644
index 91e07584d7ea7e37edfe40fa83075849642823cb..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsDevice.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core;
-
-import com.oracle.truffle.r.library.graphics.core.geometry.Coordinates;
-
-public interface GraphicsDevice {
-    void deactivate();
-
-    void activate();
-
-    void close();
-
-    DrawingParameters getDrawingParameters();
-
-    void setMode(Mode newMode);
-
-    Mode getMode();
-
-    void setClipRect(double x1, double y1, double x2, double y2);
-
-    void drawPolyline(Coordinates coordinates, DrawingParameters drawingParameters);
-
-    enum Mode {
-        GRAPHICS_ON,    // allow graphics output
-        GRAPHICS_OFF    // disable graphics output
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsEngine.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsEngine.java
deleted file mode 100644
index 57a70b07e2e802fa89ba89a5af7e0702613c7b31..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsEngine.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core;
-
-import com.oracle.truffle.r.library.graphics.core.geometry.Coordinates;
-
-public interface GraphicsEngine {
-    void registerGraphicsSystem(GraphicsSystem newGraphicsSystem) throws Exception;
-
-    void unRegisterGraphicsSystem(GraphicsSystem graphicsSystem);
-
-    void registerGraphicsDevice(GraphicsDevice newGraphicsDevice) throws Exception;
-
-    void unRegisterGraphicsDevice(GraphicsDevice deviceToUnregister);
-
-    int getGraphicsDevicesAmount();
-
-    /**
-     * @return true if there is only Null graphics device registered
-     */
-    boolean noGraphicsDevices();
-
-    /**
-     * Tries to install one if there is no current device.
-     *
-     * @return current {@link GraphicsDevice}
-     */
-    GraphicsDevice getCurrentGraphicsDevice();
-
-    /**
-     * @return {@link com.oracle.truffle.r.library.grDevices.NullGraphicsDevice} if unable to find
-     *         other
-     */
-    GraphicsDevice getGraphicsDeviceNextTo(GraphicsDevice graphicsDevice);
-
-    /**
-     * @return {@link com.oracle.truffle.r.library.grDevices.NullGraphicsDevice} if unable to find
-     *         other
-     */
-    GraphicsDevice getGraphicsDevicePrevTo(GraphicsDevice graphicsDevice);
-
-    void setCurrentGraphicsDeviceMode(GraphicsDevice.Mode mode);
-
-    void setCurrentGraphicsDeviceClipRect(double x1, double y1, double x2, double y2);
-
-    void drawPolyline(Coordinates coordinates, DrawingParameters drawingParameters);
-
-    void killGraphicsDeviceByIndex(int graphicsDeviceIndex);
-
-    int getCurrentGraphicsDeviceIndex();
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsEngineImpl.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsEngineImpl.java
deleted file mode 100644
index 5efaee6e6765608e465e3d67d2b606db4dec8c0a..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsEngineImpl.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core;
-
-import static com.oracle.truffle.r.library.graphics.core.GraphicsEvent.GE_FINAL_STATE;
-import static com.oracle.truffle.r.library.graphics.core.GraphicsEvent.GE_INIT_STATE;
-
-import com.oracle.truffle.r.library.grDevices.NullGraphicsDevice;
-import com.oracle.truffle.r.library.grDevices.fastrgd.FastRGraphicsDevice;
-import com.oracle.truffle.r.library.graphics.core.geometry.Coordinates;
-import com.oracle.truffle.r.runtime.Utils;
-
-// todo implement 'active' devices array from devices.c
-public final class GraphicsEngineImpl implements GraphicsEngine {
-    // GNUR: GraphicsEngine.h (original value: 24)
-    private static final int MAX_GRAPHICS_SYSTEMS_AMOUNT = 48;
-    private static final int MAX_GRAPHICS_DEVICES_AMOUNT = 64;
-    private static final int NULL_GRAPHICS_DEVICE_INDEX = 0;
-    private static final int LAST_GRAPHICS_DEVICE_INDEX = MAX_GRAPHICS_DEVICES_AMOUNT - 1;
-    private static final int NOT_FOUND = -1;
-    private static final GraphicsEngine instance = new GraphicsEngineImpl();
-
-    /**
-     * According to GNUR devices.c: 0 - null device, 63 - empty.
-     */
-    private final GraphicsDevice[] graphicsDevices = new GraphicsDevice[MAX_GRAPHICS_DEVICES_AMOUNT];
-    private final GraphicsSystem[] graphicsSystems = new AbstractGraphicsSystem[MAX_GRAPHICS_SYSTEMS_AMOUNT];
-
-    private int graphicsSystemsAmount = 0;
-    private int devicesAmountWithoutNullDevice = 0;
-    private CurrentGraphicsDevice currentGraphicsDevice = new CurrentGraphicsDevice(NullGraphicsDevice.getInstance(), NULL_GRAPHICS_DEVICE_INDEX);
-
-    public static GraphicsEngine getInstance() {
-        return instance;
-    }
-
-    private GraphicsEngineImpl() {
-        initNullGraphicsDevice();
-    }
-
-    /**
-     * According to GNUR 0 index is for the Null graphics device.
-     */
-    private void initNullGraphicsDevice() {
-        graphicsDevices[NULL_GRAPHICS_DEVICE_INDEX] = NullGraphicsDevice.getInstance();
-    }
-
-    @Override
-    public void registerGraphicsSystem(GraphicsSystem newGraphicsSystem) throws Exception {
-        if (newGraphicsSystem == null) {
-            throw new NullPointerException("Graphics system to register is null");
-        }
-        int index = findElementIndexInArray(null, graphicsSystems); // find null in the
-        // graphicsSystems
-        if (NOT_FOUND == index) {
-            throw handleErrorAndMakeException("too many graphics systems registered");
-        }
-        newGraphicsSystem.setId(index);
-        graphicsSystems[index] = newGraphicsSystem;
-        callListenerForEachDevice(newGraphicsSystem.getGraphicsEventsListener(), GE_INIT_STATE);
-        graphicsSystemsAmount++;
-    }
-
-    private void callListenerForEachDevice(AbstractGraphicsSystem.GraphicsEventsListener listener, GraphicsEvent event) {
-        if (noGraphicsDevices()) {
-            return;
-        }
-        for (int i = NULL_GRAPHICS_DEVICE_INDEX + 1; i < LAST_GRAPHICS_DEVICE_INDEX; i++) {
-            GraphicsDevice graphicsDevice = graphicsDevices[i];
-            if (graphicsDevice != null) {
-                listener.onEvent(event, graphicsDevice);
-            }
-        }
-    }
-
-    // todo transcribe error(_(msg)) from errors.c
-    private static Exception handleErrorAndMakeException(String message) {
-        return new Exception(message);
-    }
-
-    // todo implement in GNUR way
-    private static void issueWarning(String warningMessage) {
-        Utils.warn(warningMessage);
-    }
-
-    @Override
-    public void unRegisterGraphicsSystem(GraphicsSystem graphicsSystem) {
-        int graphicsSystemId = graphicsSystem.getId();
-        checkGraphicsSystemIndex(graphicsSystemId);
-        if (graphicsSystemsAmount == 0) {
-            issueWarning("no graphics system to unregister");
-            return;
-        }
-        callListenerForEachDevice(graphicsSystem.getGraphicsEventsListener(), GE_FINAL_STATE);
-        graphicsSystems[graphicsSystemId] = null;
-        graphicsSystemsAmount--;
-    }
-
-    private void checkGraphicsSystemIndex(int graphicsSystemIndex) {
-        if (graphicsSystemIndex < 0 || graphicsSystemIndex >= graphicsSystems.length) {
-            throw new IllegalArgumentException("Wrong graphics system index: " + graphicsSystemIndex);
-        }
-    }
-
-    // todo implement '.Devices' list related logic from GEaddDevices (devices.c)
-    @Override
-    public void registerGraphicsDevice(GraphicsDevice newGraphicsDevice) throws Exception {
-        if (newGraphicsDevice == null) {
-            throw new NullPointerException("Graphics device to register is null");
-        }
-        if (!noGraphicsDevices()) {
-            getCurrentGraphicsDevice().deactivate();
-        }
-        int index = findElementIndexInArray(NULL_GRAPHICS_DEVICE_INDEX + 1, LAST_GRAPHICS_DEVICE_INDEX, null, graphicsDevices);
-        if (index == NOT_FOUND) {
-            throw handleErrorAndMakeException("too many open devices");
-        }
-        graphicsDevices[index] = newGraphicsDevice;
-        devicesAmountWithoutNullDevice++;
-        currentGraphicsDevice = new CurrentGraphicsDevice(newGraphicsDevice, index);
-        notifyEachGraphicsSystem(newGraphicsDevice, GE_INIT_STATE);
-        newGraphicsDevice.activate();
-    }
-
-    private void notifyEachGraphicsSystem(GraphicsDevice graphicsDevice, GraphicsEvent event) {
-        for (int i = 0; i < MAX_GRAPHICS_SYSTEMS_AMOUNT; i++) {
-            GraphicsSystem graphicsSystem = graphicsSystems[i];
-            if (graphicsSystem != null) {
-                graphicsSystem.getGraphicsEventsListener().onEvent(event, graphicsDevice);
-            }
-        }
-    }
-
-    @Override
-    public void unRegisterGraphicsDevice(GraphicsDevice deviceToUnregister) {
-        if (deviceToUnregister == null) {
-            throw new NullPointerException("Graphics device to unregister is null");
-        }
-        doUnregisterGraphicsDevice(deviceToUnregister);
-        GraphicsDevice nextGraphicsDevice = getGraphicsDeviceNextTo(deviceToUnregister);
-        int index = findElementIndexInArray(nextGraphicsDevice, graphicsDevices);
-        currentGraphicsDevice = new CurrentGraphicsDevice(nextGraphicsDevice, index);
-        nextGraphicsDevice.activate();
-        // todo Interesting that in GNUR a GraphicsSystem is not notified when a GraphicsDevice is
-        // killed
-    }
-
-    private void doUnregisterGraphicsDevice(GraphicsDevice deviceToUnregister) {
-        int index = findElementIndexInArray(deviceToUnregister, graphicsDevices);
-        if (index == NOT_FOUND) {
-            issueWarning("no graphics device to unregister");
-            return;
-        }
-        graphicsDevices[index] = null;
-        devicesAmountWithoutNullDevice--;
-        currentGraphicsDevice = new CurrentGraphicsDevice(getNullGraphicsDevice(), NULL_GRAPHICS_DEVICE_INDEX);
-        deviceToUnregister.close();
-    }
-
-    @Override
-    public int getGraphicsDevicesAmount() {
-        return devicesAmountWithoutNullDevice;
-    }
-
-    @Override
-    public boolean noGraphicsDevices() {
-        return devicesAmountWithoutNullDevice == 0;
-    }
-
-    @Override
-    public int getCurrentGraphicsDeviceIndex() {
-        return currentGraphicsDevice.graphicsDeviceIndex;
-    }
-
-    @Override
-    public GraphicsDevice getCurrentGraphicsDevice() {
-        if (isNullDeviceIsCurrent()) {
-            try {
-                // todo transcribe device installation from GNUR GEcurrentDevice (devices.c)
-                installCurrentGraphicsDevice();
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-        }
-        return currentGraphicsDevice.graphicsDevice;
-    }
-
-    private boolean isNullDeviceIsCurrent() {
-        return currentGraphicsDevice.graphicsDevice == getNullGraphicsDevice();
-    }
-
-    private void installCurrentGraphicsDevice() throws Exception {
-        registerGraphicsDevice(new FastRGraphicsDevice());
-    }
-
-    @Override
-    public GraphicsDevice getGraphicsDeviceNextTo(GraphicsDevice graphicsDevice) {
-        if (graphicsDevice == null) {
-            throw new NullPointerException("Graphics device is null");
-        }
-        int startIndex = findElementIndexInArray(graphicsDevice, graphicsDevices);
-        if (startIndex == NOT_FOUND) {
-            return getNullGraphicsDevice();
-        }
-        GraphicsDevice foundDevice = findNotNullGraphicsDevice(startIndex + 1, graphicsDevices.length, SearchDirection.FORWARD);
-        if (foundDevice == null) {
-            foundDevice = findNotNullGraphicsDevice(startIndex - 1, NULL_GRAPHICS_DEVICE_INDEX, SearchDirection.BACKWARD);
-        }
-        return foundDevice == null ? getNullGraphicsDevice() : foundDevice;
-    }
-
-    @Override
-    public void setCurrentGraphicsDeviceMode(GraphicsDevice.Mode newMode) {
-        GraphicsDevice currentDevice = getCurrentGraphicsDevice();
-        if (currentDevice.getMode() != newMode) {
-            currentDevice.setMode(newMode);
-        }
-    }
-
-    @Override
-    public GraphicsDevice getGraphicsDevicePrevTo(GraphicsDevice graphicsDevice) {
-        if (graphicsDevice == null) {
-            throw new NullPointerException("Graphics device is null");
-        }
-        int startIndex = findElementIndexInArray(graphicsDevice, graphicsDevices);
-        if (startIndex == NOT_FOUND) {
-            return getNullGraphicsDevice();
-        }
-        GraphicsDevice foundDevice = findNotNullGraphicsDevice(startIndex - 1, NULL_GRAPHICS_DEVICE_INDEX, SearchDirection.BACKWARD);
-        if (foundDevice == null) {
-            foundDevice = findNotNullGraphicsDevice(startIndex + 1, graphicsDevices.length, SearchDirection.FORWARD);
-        }
-        return foundDevice == null ? getNullGraphicsDevice() : foundDevice;
-    }
-
-    private static <T> int findElementIndexInArray(T element, T[] array) {
-        return findElementIndexInArray(0, array.length, element, array);
-    }
-
-    private static <T> int findElementIndexInArray(int startIndexInclusive, int endIndexNotInclusive, T element, T[] array) {
-        for (int i = startIndexInclusive; i < endIndexNotInclusive; i++) {
-            if (array[i] == element) {
-                return i;
-            }
-        }
-        return NOT_FOUND;
-    }
-
-    private GraphicsDevice findNotNullGraphicsDevice(int startIndexInclusive, int endIndexNotInclusive, SearchDirection direction) {
-        switch (direction) {
-            case FORWARD:
-                for (int i = startIndexInclusive; i < endIndexNotInclusive; i++) {
-                    GraphicsDevice graphicsDevice = graphicsDevices[i];
-                    if (graphicsDevice != null) {
-                        return graphicsDevice;
-                    }
-                }
-                break;
-            case BACKWARD:
-                for (int i = startIndexInclusive; i > endIndexNotInclusive; i--) {
-                    GraphicsDevice graphicsDevice = graphicsDevices[i];
-                    if (graphicsDevice != null) {
-                        return graphicsDevice;
-                    }
-                }
-        }
-        return getNullGraphicsDevice();
-    }
-
-    private GraphicsDevice getNullGraphicsDevice() {
-        return graphicsDevices[NULL_GRAPHICS_DEVICE_INDEX];
-    }
-
-    @Override
-    public void setCurrentGraphicsDeviceClipRect(double x1, double y1, double x2, double y2) {
-        // todo transcribe from GESetClip() (engine.c)
-        getCurrentGraphicsDevice().setClipRect(0, 0, 0, 0);
-    }
-
-    @Override
-    public void drawPolyline(Coordinates coordinates, DrawingParameters drawingParameters) {
-        getCurrentGraphicsDevice().drawPolyline(coordinates, drawingParameters);
-    }
-
-    @Override
-    public void killGraphicsDeviceByIndex(int graphicsDeviceIndex) {
-        // todo TBD
-    }
-
-    private final class CurrentGraphicsDevice {
-        private final GraphicsDevice graphicsDevice;
-        private final int graphicsDeviceIndex;
-
-        private CurrentGraphicsDevice(GraphicsDevice graphicsDevice, int graphicsDeviceIndex) {
-            this.graphicsDevice = graphicsDevice;
-            this.graphicsDeviceIndex = graphicsDeviceIndex;
-        }
-    }
-
-    private enum SearchDirection {
-        FORWARD,
-        BACKWARD
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsEvent.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsEvent.java
deleted file mode 100644
index 5ff9cf1c50ef0a89089439190ac045e43bac3521..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsEvent.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core;
-
-public enum GraphicsEvent {
-    GE_INIT_STATE,
-    GE_FINAL_STATE
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsSystem.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsSystem.java
deleted file mode 100644
index d0011a8c9a42eb6c9af5976901e6d20a283bea67..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsSystem.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core;
-
-public interface GraphicsSystem {
-    GraphicsEventsListener getGraphicsEventsListener();
-
-    void setId(int id);
-
-    int getId();
-
-    public interface GraphicsEventsListener {
-        void onEvent(GraphicsEvent graphicsEvent, GraphicsDevice graphicsDevice);
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsSystemParameters.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsSystemParameters.java
deleted file mode 100644
index 4d3ead19cad9736f8048aa38d04083add1bc47e4..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsSystemParameters.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core;
-
-import java.util.HashMap;
-import java.util.IdentityHashMap;
-import java.util.Map;
-
-class GraphicsSystemParameters {
-    private final Map<GraphicsDevice, HashMap<String, Object>> parametersByDevices = new IdentityHashMap<>();
-
-    void addParameterForDevice(GraphicsDevice graphicsDevice, String parameterName, Object parameterValue) {
-        HashMap<String, Object> parameters = parametersByDevices.get(graphicsDevice);
-        if (parameters == null) {
-            parameters = new HashMap<>();
-            parametersByDevices.put(graphicsDevice, parameters);
-        }
-        parameters.put(parameterName, parameterValue);
-    }
-
-    Object getParameterForDevice(GraphicsDevice graphicsDevice, int parameterName) {
-        HashMap<String, Object> parameters = parametersByDevices.get(graphicsDevice);
-        return parameters == null ? null : parameters.get(parameterName);
-    }
-
-    void removeAllParametersForDevice(GraphicsDevice graphicsDevice) {
-        parametersByDevices.remove(graphicsDevice);
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/drawables/CoordinatesDrawableObject.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/drawables/CoordinatesDrawableObject.java
deleted file mode 100644
index 4f641d7a79cf822ec0291cb4722a212e2d260587..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/drawables/CoordinatesDrawableObject.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core.drawables;
-
-import com.oracle.truffle.r.library.graphics.core.geometry.CoordinateSystem;
-import com.oracle.truffle.r.library.graphics.core.geometry.Coordinates;
-import com.oracle.truffle.r.library.graphics.core.geometry.IntCoordinates;
-
-/**
- * Denotes an object which drawing depends only from {@link Coordinates}. And automates conversion
- * from <code>srcCoordinates</code> to <code>dstCoordinates</code>.
- */
-public abstract class CoordinatesDrawableObject extends DrawableObject {
-    private final Coordinates srcCoordinates;
-
-    private Coordinates dstCoordinates;
-
-    protected CoordinatesDrawableObject(CoordinateSystem coordinateSystem, Coordinates coordinates) {
-        super(coordinateSystem);
-        this.srcCoordinates = coordinates;
-    }
-
-    @Override
-    public void recalculateForDrawingIn(CoordinateSystem dstCoordinateSystem) {
-        Coordinates converted = dstCoordinateSystem.convertCoordinatesFrom(getSrcCoordinateSystem(), srcCoordinates);
-        dstCoordinates = new IntCoordinates(converted.getXCoordinatesAsInts(), converted.getYCoordinatesAsInts());
-    }
-
-    protected final Coordinates getDstCoordinates() {
-        return dstCoordinates;
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/drawables/DrawableObject.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/drawables/DrawableObject.java
deleted file mode 100644
index ec4b0d5824ce7d9b9c677c3884b9755c06898c5d..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/drawables/DrawableObject.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core.drawables;
-
-import java.awt.Graphics2D;
-
-import com.oracle.truffle.r.library.graphics.core.geometry.CoordinateSystem;
-
-/**
- * Denotes an object defined in <code>srcCoordinateSystem</code> that can be drawn in
- * <code>dstCoordinateSystem</code> on {@link Graphics2D}.
- */
-public abstract class DrawableObject {
-    private final CoordinateSystem srcCoordinateSystem;
-
-    protected DrawableObject(CoordinateSystem srcCoordinateSystem) {
-        this.srcCoordinateSystem = srcCoordinateSystem;
-    }
-
-    public abstract void drawOn(Graphics2D g2);
-
-    /**
-     * Override to prepare coordinates given in <code>srcCoordinateSystem</code> to be drawn in
-     * <code>srcCoordinateSystem</code>.
-     */
-    public abstract void recalculateForDrawingIn(CoordinateSystem dstCoordinateSystem);
-
-    protected final CoordinateSystem getSrcCoordinateSystem() {
-        return srcCoordinateSystem;
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/drawables/PolylineDrawableObject.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/drawables/PolylineDrawableObject.java
deleted file mode 100644
index 2cf12e7f6aed8572cb9334b8d4241a24dd505012..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/drawables/PolylineDrawableObject.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core.drawables;
-
-import java.awt.Graphics2D;
-
-import com.oracle.truffle.r.library.graphics.core.geometry.CoordinateSystem;
-import com.oracle.truffle.r.library.graphics.core.geometry.Coordinates;
-
-/**
- * Able to draw a polyline on {@link Graphics2D}.
- */
-public class PolylineDrawableObject extends CoordinatesDrawableObject {
-    public PolylineDrawableObject(CoordinateSystem coordinateSystem, Coordinates coordinates) {
-        super(coordinateSystem, coordinates);
-    }
-
-    @Override
-    public void drawOn(Graphics2D g2) {
-        Coordinates coords = getDstCoordinates();
-        int[] xCoords = coords.getXCoordinatesAsInts();
-        g2.drawPolyline(xCoords, coords.getYCoordinatesAsInts(), xCoords.length);
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/drawables/StringDrawableObject.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/drawables/StringDrawableObject.java
deleted file mode 100644
index ccf4e16876bbd90c3b576366407838592916ff4f..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/drawables/StringDrawableObject.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core.drawables;
-
-import java.awt.Graphics2D;
-import java.util.stream.IntStream;
-
-import com.oracle.truffle.r.library.graphics.core.geometry.CoordinateSystem;
-import com.oracle.truffle.r.library.graphics.core.geometry.Coordinates;
-
-/**
- * Able to render a text on {@link Graphics2D}.
- */
-public class StringDrawableObject extends CoordinatesDrawableObject {
-    private final String[] strings;
-
-    public StringDrawableObject(CoordinateSystem coordinateSystem, Coordinates coordinates, String[] strings) {
-        super(coordinateSystem, coordinates);
-        this.strings = strings;
-    }
-
-    @Override
-    public void drawOn(Graphics2D g2) {
-        Coordinates dstCoordinates = getDstCoordinates();
-        int[] xCoords = dstCoordinates.getXCoordinatesAsInts();
-        int[] yCoords = dstCoordinates.getYCoordinatesAsInts();
-        IntStream.range(0, strings.length).forEach(i -> g2.drawString(strings[i], xCoords[i], yCoords[i]));
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/Axis.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/Axis.java
deleted file mode 100644
index c9a33c65a82da57a5b2f57a5fb1c65dbc2203be4..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/Axis.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core.geometry;
-
-public final class Axis {
-    private final double minValue;
-    private final double maxValue;
-    private final AxisDirection direction;
-
-    public Axis(double minValue, double maxValue, AxisDirection direction) {
-        this.minValue = minValue;
-        this.maxValue = maxValue;
-        this.direction = direction;
-    }
-
-    public double getMinValue() {
-        return minValue;
-    }
-
-    public double getMaxValue() {
-        return maxValue;
-    }
-
-    public AxisDirection getDirection() {
-        return direction;
-    }
-
-    public double getRange() {
-        return maxValue - minValue;
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/AxisDirection.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/AxisDirection.java
deleted file mode 100644
index 8099a58058eae00db737d42cc2c75a918ff9fcb0..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/AxisDirection.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core.geometry;
-
-public enum AxisDirection {
-    NORTH,
-    SOUTH,
-    WEST,
-    EAST
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/CoordinateSystem.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/CoordinateSystem.java
deleted file mode 100644
index 2c1fca3b817a003160c798290ea87c1d93c0d7f2..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/CoordinateSystem.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core.geometry;
-
-import java.util.stream.DoubleStream;
-
-/**
- * Denotes X-Y coordinate system by specifying max and min values for X-Y axis. Able to convert
- * coordinates given in another {@link CoordinateSystem}
- */
-public final class CoordinateSystem {
-    private final Axis xAxis;
-    private final Axis yAxis;
-
-    /**
-     * Uses Java graphics default axis orientation: x increases to the right, y increases to the
-     * bottom.
-     */
-    public CoordinateSystem(double minX, double maxX, double minY, double maxY) {
-        this(minX, maxX, minY, maxY, AxisDirection.EAST, AxisDirection.SOUTH);
-    }
-
-    public CoordinateSystem(double minX, double maxX, double minY, double maxY, AxisDirection xDirection, AxisDirection yDirection) {
-        this(new Axis(minX, maxX, xDirection), new Axis(minY, maxY, yDirection));
-    }
-
-    public CoordinateSystem(Axis xAxis, Axis yAxis) {
-        this.xAxis = xAxis;
-        this.yAxis = yAxis;
-    }
-
-    /**
-     * Transforms <code> otherCoordinates </code> given in <code> otherCoordinateSystem</code> to
-     * this coordinate system. Also applies the affine transformation defined by ratio and shifts.
-     */
-    public Coordinates convertCoordinatesFrom(CoordinateSystem otherCoordinateSystem, Coordinates otherCoordinates, double ratio, double xAxisShift, double yAxisShift) {
-        double[] resultX = convertCoordinatesBetweenAxises(getXAxis(), otherCoordinateSystem.getXAxis(), otherCoordinates.getXCoordinatesAsDoubles(), ratio, xAxisShift);
-        double[] resultY = convertCoordinatesBetweenAxises(getYAxis(), otherCoordinateSystem.getYAxis(), otherCoordinates.getYCoordinatesAsDoubles(), ratio, yAxisShift);
-        return new DoubleCoordinates(resultX, resultY);
-    }
-
-    public Coordinates convertCoordinatesFrom(CoordinateSystem otherCoordinateSystem, Coordinates otherCoordinates) {
-        double noRatio = 1;
-        double noShift = 0;
-        return convertCoordinatesFrom(otherCoordinateSystem, otherCoordinates, noRatio, noShift, noShift);
-    }
-
-    private static double[] convertCoordinatesBetweenAxises(Axis toAxis, Axis fromAxis, double[] coords, double givenRatio, double givenShift) {
-        boolean sameDirection = toAxis.getDirection() == fromAxis.getDirection();
-        double ratio = toAxis.getRange() / fromAxis.getRange();
-        ratio = sameDirection ? ratio : -ratio;
-        ratio *= givenRatio; // adding given ratio
-        double shift = sameDirection ? 0 : toAxis.getMaxValue();
-        shift += givenShift * ratio; // adding given shift
-        return applyShiftAndRatio(coords, ratio, shift);
-    }
-
-    private static double[] applyShiftAndRatio(double[] coords, double ratio, double shift) {
-        return DoubleStream.of(coords).map(d -> d * ratio + shift).toArray();
-    }
-
-    private Axis getXAxis() {
-        return xAxis;
-    }
-
-    private Axis getYAxis() {
-        return yAxis;
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/Coordinates.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/Coordinates.java
deleted file mode 100644
index 95396232f26e67252041de67b3653756097f7c7c..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/Coordinates.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core.geometry;
-
-/**
- * Denotes X-Y coordinates. Instances must be immutable objects.
- */
-public interface Coordinates {
-    double[] getXCoordinatesAsDoubles();
-
-    double[] getYCoordinatesAsDoubles();
-
-    int[] getXCoordinatesAsInts();
-
-    int[] getYCoordinatesAsInts();
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/CoordinatesFactory.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/CoordinatesFactory.java
deleted file mode 100644
index 33e81bb144b46109300d75c7c5881ace2e86b41b..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/CoordinatesFactory.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core.geometry;
-
-import java.util.Arrays;
-import java.util.stream.DoubleStream;
-import java.util.stream.IntStream;
-
-import com.oracle.truffle.r.runtime.data.RDoubleVector;
-
-public final class CoordinatesFactory {
-    private CoordinatesFactory() {
-    }
-
-    public static DoubleCoordinates createWithSameX(double x, double[] yCoords) {
-        double[] xCoords = createdFilledArray(x, yCoords.length);
-        return new DoubleCoordinates(xCoords, yCoords);
-    }
-
-    public static DoubleCoordinates createWithSameY(double[] xCoords, double y) {
-        double[] yCoords = createdFilledArray(y, xCoords.length);
-        return new DoubleCoordinates(xCoords, yCoords);
-    }
-
-    private static double[] createdFilledArray(double value, int size) {
-        double[] result = new double[size];
-        Arrays.fill(result, value);
-        return result;
-    }
-
-    public static DoubleCoordinates createByXYVector(RDoubleVector xyVector) {
-        int length = xyVector.getLength();
-        double[] xCoords = IntStream.range(0, length).filter(i -> i % 2 == 0).mapToDouble(xyVector::getDataAt).toArray();
-        double[] yCoords = IntStream.range(0, length).filter(i -> i % 2 != 0).mapToDouble(xyVector::getDataAt).toArray();
-        return new DoubleCoordinates(xCoords, yCoords);
-    }
-
-    public static DoubleCoordinates createByXYPairs(double[] xyPairs) {
-        int length = xyPairs.length;
-        double[] xCoords = IntStream.range(0, length).filter(i -> i % 2 == 0).mapToDouble(i -> xyPairs[i]).toArray();
-        double[] yCoords = IntStream.range(0, length).filter(i -> i % 2 != 0).mapToDouble(i -> xyPairs[i]).toArray();
-        return new DoubleCoordinates(xCoords, yCoords);
-    }
-
-    public static DoubleCoordinates withRatioAndShift(Coordinates coordinates, double ratio, double shift) {
-        return withRatioAndShift(coordinates, ratio, shift, shift);
-    }
-
-    public static DoubleCoordinates withRatioAndShift(Coordinates coordinates, double ratio, double xShift, double yShift) {
-        double[] convertedX = applyRatioAndShiftTo(coordinates.getXCoordinatesAsDoubles(), ratio, xShift);
-        double[] convertedY = applyRatioAndShiftTo(coordinates.getYCoordinatesAsDoubles(), ratio, yShift);
-        return new DoubleCoordinates(convertedX, convertedY);
-    }
-
-    private static double[] applyRatioAndShiftTo(double[] coordinates, double ratio, double shift) {
-        return DoubleStream.of(coordinates).map(d -> d * ratio + shift).toArray();
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/DoubleCoordinates.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/DoubleCoordinates.java
deleted file mode 100644
index 90dcb51dd61f9b8f234b097f6b5ff97531aff1af..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/DoubleCoordinates.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core.geometry;
-
-import java.util.stream.DoubleStream;
-
-public final class DoubleCoordinates implements Coordinates {
-
-    private final double[] xCoords;
-    private final double[] yCoords;
-
-    public DoubleCoordinates(double[] xCoords, double[] yCoords) {
-        this.xCoords = xCoords;
-        this.yCoords = yCoords;
-    }
-
-    @Override
-    public double[] getXCoordinatesAsDoubles() {
-        return xCoords;
-    }
-
-    @Override
-    public double[] getYCoordinatesAsDoubles() {
-        return yCoords;
-    }
-
-    @Override
-    public int[] getXCoordinatesAsInts() {
-        return toInt(getXCoordinatesAsDoubles());
-    }
-
-    @Override
-    public int[] getYCoordinatesAsInts() {
-        return toInt(getYCoordinatesAsDoubles());
-    }
-
-    private static int[] toInt(double[] doubleArray) {
-        return DoubleStream.of(doubleArray).mapToInt(d -> (int) d).toArray();
-    }
-}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/IntCoordinates.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/IntCoordinates.java
deleted file mode 100644
index cd62ef35fe6eca47082d6314c2852f7daec5f8c1..0000000000000000000000000000000000000000
--- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/IntCoordinates.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * This material is distributed under the GNU General Public License
- * Version 2. You may review the terms of this license at
- * http://www.gnu.org/licenses/gpl-2.0.html
- *
- * Copyright (C) 1995, 1996  Robert Gentleman and Ross Ihaka
- * Copyright (C) 1998 Ross Ihaka
- * Copyright (c) 1998--2014, The R Core Team
- * Copyright (c) 2002--2010, The R Foundation
- * Copyright (C) 2005--2006, Morten Welinder
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates
- *
- * All rights reserved.
- */
-package com.oracle.truffle.r.library.graphics.core.geometry;
-
-import java.util.stream.IntStream;
-
-public final class IntCoordinates implements Coordinates {
-    private final int[] xCoords;
-    private final int[] yCoords;
-
-    public IntCoordinates(int[] xCoords, int[] yCoords) {
-        this.xCoords = xCoords;
-        this.yCoords = yCoords;
-    }
-
-    @Override
-    public double[] getXCoordinatesAsDoubles() {
-        return toDouble(xCoords);
-    }
-
-    @Override
-    public double[] getYCoordinatesAsDoubles() {
-        return toDouble(yCoords);
-    }
-
-    @Override
-    public int[] getXCoordinatesAsInts() {
-        return xCoords;
-    }
-
-    @Override
-    public int[] getYCoordinatesAsInts() {
-        return yCoords;
-    }
-
-    private static double[] toDouble(int[] intArray) {
-        return IntStream.of(intArray).mapToDouble(i -> i).toArray();
-    }
-}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
index 16e97d3504be001bfc427e6ff7b491f67f484b17..ac9d5aa0bc70311575eb482b5521b1207fb2f4fa 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
@@ -25,6 +25,7 @@ package com.oracle.truffle.r.nodes.builtin.base;
 import java.util.function.Supplier;
 
 import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.r.library.fastrGrid.DoSetViewPortBuiltin;
 import com.oracle.truffle.r.nodes.RRootNode;
 import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode;
 import com.oracle.truffle.r.nodes.binary.BinaryArithmeticNodeGen;
@@ -717,6 +718,9 @@ public class BasePackage extends RBuiltinPackage {
         add(UpdateSubset.class, UpdateSubsetNodeGen::create, UpdateSubset::special);
         add(UpdateField.class, UpdateFieldNodeGen::create, UpdateField::createSpecial);
         add(WhileBuiltin.class, WhileBuiltinNodeGen::create);
+
+        // grid intrinsics
+        add(DoSetViewPortBuiltin.class, DoSetViewPortBuiltin::create);
     }
 
     private void addBinaryArithmetic(Class<?> builtinClass, BinaryArithmeticFactory binaryFactory, UnaryArithmeticFactory unaryFactory) {
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java
index ab28e791fd8d730b36e6b70e815fb86459e08692..b7bee9a581eb1f2add866a49558752b2e3e1eb72 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java
@@ -22,12 +22,7 @@ import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.r.library.grDevices.DevicesCCalls;
-import com.oracle.truffle.r.library.graphics.GraphicsCCalls;
-import com.oracle.truffle.r.library.graphics.GraphicsCCalls.C_Par;
-import com.oracle.truffle.r.library.graphics.GraphicsCCalls.C_PlotXY;
-import com.oracle.truffle.r.library.grid.GridFunctionsFactory.InitGridNodeGen;
-import com.oracle.truffle.r.library.grid.GridFunctionsFactory.ValidUnitsNodeGen;
+import com.oracle.truffle.r.library.fastrGrid.FastRGridExternalLookup;
 import com.oracle.truffle.r.library.methods.MethodsListDispatchFactory.R_M_setPrimitiveMethodsNodeGen;
 import com.oracle.truffle.r.library.methods.MethodsListDispatchFactory.R_externalPtrPrototypeObjectNodeGen;
 import com.oracle.truffle.r.library.methods.MethodsListDispatchFactory.R_getClassFromCacheNodeGen;
@@ -55,10 +50,10 @@ import com.oracle.truffle.r.library.stats.RandFunctionsNodes.RandFunction3Node;
 import com.oracle.truffle.r.library.stats.SignrankFreeNode;
 import com.oracle.truffle.r.library.stats.SplineFunctionsFactory.SplineCoefNodeGen;
 import com.oracle.truffle.r.library.stats.SplineFunctionsFactory.SplineEvalNodeGen;
-import com.oracle.truffle.r.library.stats.deriv.D;
-import com.oracle.truffle.r.library.stats.deriv.Deriv;
 import com.oracle.truffle.r.library.stats.StatsFunctionsNodes;
 import com.oracle.truffle.r.library.stats.WilcoxFreeNode;
+import com.oracle.truffle.r.library.stats.deriv.D;
+import com.oracle.truffle.r.library.stats.deriv.Deriv;
 import com.oracle.truffle.r.library.tools.C_ParseRdNodeGen;
 import com.oracle.truffle.r.library.tools.DirChmodNodeGen;
 import com.oracle.truffle.r.library.tools.Rmd5NodeGen;
@@ -79,6 +74,7 @@ import com.oracle.truffle.r.nodes.objects.NewObjectNodeGen;
 import com.oracle.truffle.r.runtime.FastROptions;
 import com.oracle.truffle.r.runtime.RError;
 import com.oracle.truffle.r.runtime.RInternalCode;
+import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
@@ -240,6 +236,12 @@ public class CallAndExternalFunctions {
         @TruffleBoundary
         protected RExternalBuiltinNode lookupBuiltin(RList symbol) {
             String name = lookupName(symbol);
+            if (FastROptions.UseInternalGridGraphics.getBooleanValue() && name != null) {
+                RExternalBuiltinNode gridExternal = FastRGridExternalLookup.lookupDotCall(name);
+                if (gridExternal != null) {
+                    return gridExternal;
+                }
+            }
             switch (name) {
                 // methods
                 case "R_initMethodDispatch":
@@ -629,24 +631,6 @@ public class CallAndExternalFunctions {
                 // parallel
                 case "mc_is_child":
                     return MCIsChildNodeGen.create();
-                default:
-                    return FastROptions.UseInternalGraphics.getBooleanValue() ? lookupGraphicsBuiltin(name) : null;
-            }
-        }
-
-        private static RExternalBuiltinNode lookupGraphicsBuiltin(String name) {
-            switch (name) {
-                // grDevices
-                case "cairoProps":
-                    return CairoPropsNodeGen.create();
-                case "makeQuartzDefault":
-                    return new MakeQuartzDefault();
-
-                // grid
-                case "L_initGrid":
-                    return InitGridNodeGen.create();
-                case "L_validUnits":
-                    return ValidUnitsNodeGen.create();
                 default:
                     return null;
             }
@@ -656,7 +640,7 @@ public class CallAndExternalFunctions {
          * {@code .NAME = NativeSymbolInfo} implemented as a builtin.
          */
         @SuppressWarnings("unused")
-        @Specialization(limit = "1", guards = {"cached == symbol", "builtin != null"})
+        @Specialization(limit = "99", guards = {"cached == symbol", "builtin != null"})
         protected Object doExternal(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName,
                         @Cached("symbol") RList cached,
                         @Cached("lookupBuiltin(symbol)") RExternalBuiltinNode builtin) {
@@ -668,9 +652,10 @@ public class CallAndExternalFunctions {
          * package)
          */
         @SuppressWarnings("unused")
-        @Specialization(limit = "2", guards = {"cached == symbol"})
+        @Specialization(limit = "2", guards = {"cached == symbol", "builtin == null"})
         protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName,
                         @Cached("symbol") RList cached,
+                        @Cached("lookupBuiltin(symbol)") RExternalBuiltinNode builtin,
                         @Cached("extractSymbolInfo(frame, symbol)") NativeCallInfo nativeCallInfo) {
             return callRFFINode.execute(nativeCallInfo, args.getArguments());
         }
@@ -680,8 +665,12 @@ public class CallAndExternalFunctions {
          * such cases there is this generic version.
          */
         @SuppressWarnings("unused")
-        @Specialization(replaces = "callNamedFunction")
+        @Specialization(replaces = {"callNamedFunction", "doExternal"})
         protected Object callNamedFunctionGeneric(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName) {
+            RExternalBuiltinNode builtin = lookupBuiltin(symbol);
+            if (builtin != null) {
+                throw RInternalError.shouldNotReachHere("Cache for .Calls with FastR reimplementation (lookupBuiltin(...) != null) exceeded the limit");
+            }
             NativeCallInfo nativeCallInfo = extractSymbolInfo(frame, symbol);
             return callRFFINode.execute(nativeCallInfo, args.getArguments());
         }
@@ -733,14 +722,10 @@ public class CallAndExternalFunctions {
         @TruffleBoundary
         protected RExternalBuiltinNode lookupBuiltin(RList f) {
             String name = lookupName(f);
-            if (FastROptions.UseInternalGraphics.getBooleanValue()) {
-                switch (name) {
-                    case "PDF":
-                        return new DevicesCCalls.C_PDF();
-                    case "devoff":
-                        return DevicesCCalls.C_DevOff.create();
-                    case "devcur":
-                        return new DevicesCCalls.C_DevCur();
+            if (FastROptions.UseInternalGridGraphics.getBooleanValue()) {
+                RExternalBuiltinNode gridExternal = FastRGridExternalLookup.lookupDotExternal(name);
+                if (gridExternal != null) {
+                    return gridExternal;
                 }
             }
             switch (name) {
@@ -854,13 +839,13 @@ public class CallAndExternalFunctions {
         @Override
         @TruffleBoundary
         protected RExternalBuiltinNode lookupBuiltin(RList symbol) {
-            if (FastROptions.UseInternalGraphics.getBooleanValue()) {
-                switch (lookupName(symbol)) {
-                    case "C_par":
-                        return new C_Par();
+            String name = lookupName(symbol);
+            if (FastROptions.UseInternalGridGraphics.getBooleanValue()) {
+                RExternalBuiltinNode gridExternal = FastRGridExternalLookup.lookupDotExternal2(name);
+                if (gridExternal != null) {
+                    return gridExternal;
                 }
             }
-            String name = lookupName(symbol);
             switch (name) {
                 // tools
                 case "writetable":
@@ -929,14 +914,6 @@ public class CallAndExternalFunctions {
         @Override
         @TruffleBoundary
         protected RExternalBuiltinNode lookupBuiltin(RList f) {
-            if (FastROptions.UseInternalGraphics.getBooleanValue()) {
-                switch (lookupName(f)) {
-                    case "C_mtext":
-                        return new GraphicsCCalls.C_mtext();
-                    case "C_plotXY":
-                        return new C_PlotXY();
-                }
-            }
             return null;
         }
 
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/ExtBuiltinsList.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/ExtBuiltinsList.java
index 98eb067b7a6a144ec15572ed5ce698a971c85cd2..1035f57473bb7f0193a228c9faaac48d2cd6fddf 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/ExtBuiltinsList.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/ExtBuiltinsList.java
@@ -58,12 +58,6 @@ public class ExtBuiltinsList {
                     com.oracle.truffle.r.library.tools.Rmd5NodeGen.class,
                     com.oracle.truffle.r.library.tools.DirChmodNodeGen.class,
                     com.oracle.truffle.r.library.tools.C_ParseRdNodeGen.class,
-                    com.oracle.truffle.r.library.grDevices.DevicesCCallsFactory.C_DevOffNodeGen.class,
-                    com.oracle.truffle.r.library.grDevices.DevicesCCalls.C_DevCur.class,
-                    com.oracle.truffle.r.library.grDevices.DevicesCCalls.C_PDF.class,
-                    com.oracle.truffle.r.library.graphics.GraphicsCCalls.C_PlotXY.class,
-                    com.oracle.truffle.r.library.graphics.GraphicsCCalls.C_Par.class,
-                    com.oracle.truffle.r.library.graphics.GraphicsCCalls.C_mtext.class,
                     com.oracle.truffle.r.library.stats.WilcoxFreeNode.class,
                     com.oracle.truffle.r.library.stats.StatsFunctionsNodesFactory.Function3_2NodeGen.class,
                     com.oracle.truffle.r.library.stats.StatsFunctionsNodesFactory.Function4_1NodeGen.class,
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
index 925e1f39042c3510b80c4d18eb2f1cdbcb18bd93..24f998565a1d465f9bf3e16285bd485ea2cf431a 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/FastROptions.java
@@ -51,7 +51,7 @@ public enum FastROptions {
     FullPrecisionSum("Use 128 bit arithmetic in sum builtin", false),
     InvisibleArgs("Argument writes do not trigger state transitions", true),
     RefCountIncrementOnly("Disable reference count decrements for experimental state transition implementation", false),
-    UseInternalGraphics("Whether the internal (Java) graphics subsystem should be used", false),
+    UseInternalGridGraphics("Whether the internal (Java) grid graphics implementation should be used", false),
     UseSpecials("Whether the fast-path special call nodes should be created for simple enough arguments.", true),
     ForceSources("Generate source sections for unserialized code", false),
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java
index c999db5a78e8e5a78182682ed4d41e9a57e2801b..32d4e8881858f429fa8a630ce55be34b9f4e4001 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPairList.java
@@ -211,6 +211,17 @@ public final class RPairList extends RSharingAttributeStorage implements RAbstra
         return type;
     }
 
+    /**
+     * Appends given value as the last element of the pairlist.
+     */
+    public void appendToEnd(RPairList value) {
+        RPairList last = null;
+        for (RPairList item : this) {
+            last = item;
+        }
+        last.setCdr(value);
+    }
+
     @Override
     public boolean isComplete() {
         // TODO: is it important to get more precise information here?
diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastrGrid/GridColorUtilsTests.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastrGrid/GridColorUtilsTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..8e43a26774ed8279b5b93faf526f3e1889520dc0
--- /dev/null
+++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/fastrGrid/GridColorUtilsTests.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2017, 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.
+ */
+package com.oracle.truffle.r.test.library.fastrGrid;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+import com.oracle.truffle.r.library.fastrGrid.GridColorUtils;
+import com.oracle.truffle.r.library.fastrGrid.device.GridColor;
+import com.oracle.truffle.r.test.TestBase;
+
+public class GridColorUtilsTests extends TestBase {
+    @Test
+    public void convertFromHex() {
+        GridColor color = GridColorUtils.gridColorFromString("#FF01FE");
+        assertEquals(255, color.getRed());
+        assertEquals(1, color.getGreen());
+        assertEquals(254, color.getBlue());
+        assertEquals(255, color.getAlpha());
+    }
+
+    @Test
+    public void convertFromHexWithAlpha() {
+        GridColor color = GridColorUtils.gridColorFromString("#FF00FE02");
+        assertEquals(255, color.getRed());
+        assertEquals(0, color.getGreen());
+        assertEquals(254, color.getBlue());
+        assertEquals(02, color.getAlpha());
+    }
+
+    @Test
+    public void convertSynonymBlack() {
+        GridColor black = GridColorUtils.gridColorFromString("black");
+        assertEquals(0, black.getRed());
+        assertEquals(0, black.getGreen());
+        assertEquals(0, black.getBlue());
+        assertEquals(255, black.getAlpha());
+    }
+
+    @Test
+    public void convertSynonymUpercaseRed() {
+        GridColor black = GridColorUtils.gridColorFromString("RED");
+        assertEquals(255, black.getRed());
+        assertEquals(0, black.getGreen());
+        assertEquals(0, black.getBlue());
+        assertEquals(255, black.getAlpha());
+    }
+
+    @Test
+    public void convertSynonymLightGreenWithSpace() {
+        GridColor black = GridColorUtils.gridColorFromString("light green");
+        assertEquals(0x90, black.getRed());
+        assertEquals(0xee, black.getGreen());
+        assertEquals(0x90, black.getBlue());
+        assertEquals(255, black.getAlpha());
+    }
+
+    @Test
+    public void convertSynonymLightGreen() {
+        GridColor black = GridColorUtils.gridColorFromString("light green");
+        assertEquals(0x90, black.getRed());
+        assertEquals(0xee, black.getGreen());
+        assertEquals(0x90, black.getBlue());
+        assertEquals(255, black.getAlpha());
+    }
+
+    @Test
+    public void convertSynonymTransparent() {
+        GridColor transparent = GridColorUtils.gridColorFromString("transparent");
+        assertEquals(0, transparent.getAlpha());
+    }
+}
diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides
index f6786e5110ab80a04fbc540258a0b5e20b71bfcc..d3c6075238f30f0347d2ed8f3bb85f70613cd644 100644
--- a/mx.fastr/copyrights/overrides
+++ b/mx.fastr/copyrights/overrides
@@ -1,30 +1,5 @@
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/BaseGraphicsSystem.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/AbstractGraphicsSystem.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/drawables/CoordinatesDrawableObject.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/drawables/DrawableObject.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/drawables/PolylineDrawableObject.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/drawables/StringDrawableObject.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/DrawingParameters.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/Axis.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/AxisDirection.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/Coordinates.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/CoordinatesFactory.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/CoordinateSystem.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/DoubleCoordinates.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/geometry/IntCoordinates.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsDevice.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsEngine.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsEngineImpl.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsEvent.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsSystem.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/core/GraphicsSystemParameters.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/FastRComponent.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/GraphicsCCalls.java,gnu_r_graphics.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/graphics/RGraphics.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grDevices/DevicesCCalls.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grDevices/fastrgd/FastRGraphicsDevice.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grDevices/NullGraphicsDevice.java,gnu_r_graphics.copyright
-com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grDevices/pdf/PdfGraphicsDevice.java,gnu_r_graphics.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/graphics/RGridGraphicsAdapter.java,gnu_r_graphics.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/grid/GridFunctions.java,gnu_r_murrel_core.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/MethodsListDispatch.java,gnu_r.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/methods/Slot.java,gnu_r.copyright
@@ -766,3 +741,30 @@ com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/p
 com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/printer/PairListPrinter.java,gnu_r_gentleman_ihaka2.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/deriv/Deriv.java,gnu_r_gentleman_ihaka2.copyright
 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/deriv/DerivVisitor.java,gnu_r_gentleman_ihaka2.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRect.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DrawArrowsNode.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPoints.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPortBuiltin.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LUpViewPort.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LNewPage.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitGrid.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitViewPortStack.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/TransformMatrix.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LSegments.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPort.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LUnsetViewPort.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridUtils.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/Unit.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortLocation.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortContext.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridLinesNode.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPortTransform.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LGridDirty.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridColorUtils.java,gnu_r.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircle.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/EdgeDetection.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridTextNode.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LConvert.java,gnu_r_murrel_core.copyright
diff --git a/mx.fastr/mx_fastr.py b/mx.fastr/mx_fastr.py
index 782257b532a78329e952b39f4c02db0d82a52882..259351b416143f772c52d0fbfb08dc3e71ffb675 100644
--- a/mx.fastr/mx_fastr.py
+++ b/mx.fastr/mx_fastr.py
@@ -404,7 +404,7 @@ def _test_subpackage(name):
     return '.'.join((_test_package(), name))
 
 def _simple_generated_unit_tests():
-    return ','.join(map(_test_subpackage, ['engine.shell', 'library.base', 'library.grid', 'library.methods', 'library.stats', 'library.utils', 'library.fastr', 'builtins', 'functions', 'parser', 'rng', 'runtime.data', 'S4']))
+    return ','.join(map(_test_subpackage, ['engine.shell', 'library.base', 'library.fastrGrid', 'library.methods', 'library.stats', 'library.utils', 'library.fastr', 'builtins', 'functions', 'parser', 'rng', 'runtime.data', 'S4']))
 
 def _simple_unit_tests():
     return ','.join([_simple_generated_unit_tests(), _test_subpackage('tck')])