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
index 7134c501c43cd9307bc0493796c3d8e3b26026ae..ec20b0d423e149c5f6586dab80eeee192e20bdad 100644
--- 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
@@ -107,7 +107,7 @@ class DoSetViewPort extends RBaseNode {
             }
         }
 
-        UnitConversionContext conversionCtx = new UnitConversionContext(parentSize, parentContext, drawingContext);
+        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);
@@ -141,7 +141,7 @@ class DoSetViewPort extends RBaseNode {
         if (!isNull(viewPort.getDataAt(ViewPort.VP_LAYOUT))) {
             ViewPortContext vpCtx = vpContextFromVP.execute(viewPort);
             DrawingContext drawingCtx = GPar.asDrawingContext(asList(viewPort.getDataAt(ViewPort.PVP_GPAR)));
-            calcViewPortLayout(viewPort, new Size(width, height), vpCtx, drawingCtx);
+            calcViewPortLayout(viewPort, new Size(width, height), vpCtx, device, drawingCtx);
         }
 
         Object[] viewPortData = viewPort.getDataWithoutCopying();
@@ -151,13 +151,13 @@ class DoSetViewPort extends RBaseNode {
         viewPortData[ViewPort.PVP_TRANS] = RDataFactory.createDoubleVector(flatten(transform), RDataFactory.COMPLETE_VECTOR, new int[]{3, 3});
     }
 
-    private void calcViewPortLayout(RList viewPort, Size size, ViewPortContext parentVPCtx, DrawingContext drawingCtx) {
+    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, drawingCtx);
+        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
@@ -178,8 +178,8 @@ class DoSetViewPort extends RBaseNode {
         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(layoutSize, layoutWidths, relativeWidths, parentVPCtx, drawingCtx, true);
-            double sumRelHeight = sumRelativeDimension(layoutSize, layoutHeights, relativeHeights, parentVPCtx, drawingCtx, false);
+            double sumRelWidth = sumRelativeDimension(layoutSize, layoutWidths, relativeWidths, parentVPCtx, device, drawingCtx, true);
+            double sumRelHeight = sumRelativeDimension(layoutSize, layoutHeights, relativeHeights, parentVPCtx, device, drawingCtx, false);
             double tempWidth = reducedWidth;
             double tempHeight = reducedHeight;
             double denom;
@@ -233,8 +233,8 @@ class DoSetViewPort extends RBaseNode {
         }
 
         // Secondly, allocate remaining relative widths and heights in the remaining space
-        allocateRelativeDim(layoutSize, layoutWidths, npcWidths, relativeWidths, reducedWidth, respect, layoutRespectMat, drawingCtx, parentVPCtx, true);
-        allocateRelativeDim(layoutSize, layoutHeights, npcHeights, relativeHeights, reducedHeight, respect, layoutRespectMat, drawingCtx, parentVPCtx, false);
+        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();
@@ -243,8 +243,8 @@ class DoSetViewPort extends RBaseNode {
     }
 
     private void allocateRelativeDim(LayoutSize layoutSize, RAbstractContainer layoutItems, double[] npcItems, boolean[] relativeItems, double reducedDim, int respect, int[] layoutRespectMat,
-                    DrawingContext drawingCtx, ViewPortContext parentVPCtx, boolean isWidth) {
-        UnitConversionContext layoutModeCtx = new UnitConversionContext(new Size(0, 0), parentVPCtx, drawingCtx, 1, 0);
+                    GridDevice device, DrawingContext drawingCtx, ViewPortContext parentVPCtx, boolean isWidth) {
+        UnitConversionContext layoutModeCtx = new UnitConversionContext(new Size(0, 0), parentVPCtx, device, drawingCtx, 1, 0);
         double totalUnrespectedSize = 0;
         if (reducedDim > 0) {
             for (int i = 0; i < layoutSize.ncol; i++) {
@@ -293,8 +293,9 @@ class DoSetViewPort extends RBaseNode {
         return false;
     }
 
-    private double sumRelativeDimension(LayoutSize layoutSize, RAbstractContainer layoutItems, boolean[] relativeItems, ViewPortContext parentVPCtx, DrawingContext drawingCtx, boolean isWidth) {
-        UnitConversionContext layoutModeCtx = new UnitConversionContext(new Size(0, 0), parentVPCtx, drawingCtx, 0, 1);
+    private double sumRelativeDimension(LayoutSize layoutSize, RAbstractContainer layoutItems, boolean[] relativeItems, ViewPortContext parentVPCtx, GridDevice device, DrawingContext drawingCtx,
+                    boolean isWidth) {
+        UnitConversionContext layoutModeCtx = new UnitConversionContext(new Size(0, 0), parentVPCtx, device, drawingCtx, 0, 1);
         double totalWidth = 0;
         for (int i = 0; i < layoutSize.ncol; i++) {
             if (relativeItems[i]) {
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..695175e7c26b926c6ce2c47c78c524cd69208b63
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridLinesNode.java
@@ -0,0 +1,119 @@
+/*
+ * 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.nodes.Node;
+import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
+import com.oracle.truffle.r.library.fastrGrid.ViewPortContext.VPContextFromVPNode;
+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 VPContextFromVPNode vpContextFromVP = new VPContextFromVPNode();
+
+    void execute(RAbstractVector x, RAbstractVector y, RList lengths) {
+        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);
+        ViewPortContext vpContext = vpContextFromVP.execute(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);
+                    }
+                }
+                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/GridTextNode.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridTextNode.java
index 8fbbe300af3a8ffa4d06306f45c38402604c92dc..13e52dfc66885702da15fcee914f19de9291ce4c 100644
--- 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
@@ -97,7 +97,7 @@ public final class GridTextNode extends RBaseNode {
         DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
         ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP);
         ViewPortContext vpContext = vpContextFromVP.execute(currentVP);
-        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, drawingCtx);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
 
         int length = GridUtils.maxLength(unitLength, x, y);
 
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
index 0de2442c7b6c55ce38242daec0a3d6be4c9eefdf..1b7ff9197a90621615f2628037cd26df56593c6b 100644
--- 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
@@ -21,6 +21,7 @@ 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;
@@ -89,6 +90,16 @@ final class GridUtils {
         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");
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
index 60448d3fa71cfa21343359505fc0c9a635a4a0be..c5262bd3ff8b07d7bf2db368fb117cd930dcad56 100644
--- 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
@@ -51,7 +51,7 @@ public abstract class LCircle extends RExternalBuiltinNode.Arg3 {
         DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
         ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP);
         ViewPortContext vpContext = vpContextFromVP.execute(currentVP);
-        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, drawingCtx);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
 
         int length = GridUtils.maxLength(unitLength, xVec, yVec, radiusVec);
         for (int i = 0; i < length; i++) {
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
index 04f325f4853d495702f376c79e16f2c606e33fa4..3703c83baa9d98587184cef5cc43f1dae7d81b64 100644
--- 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
@@ -19,6 +19,7 @@ import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVect
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
 
 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.ViewPortContext.VPContextFromVPNode;
 import com.oracle.truffle.r.library.fastrGrid.ViewPortTransform.GetViewPortTransformNode;
@@ -59,7 +60,7 @@ public abstract class LConvert extends RExternalBuiltinNode.Arg4 {
         DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
         ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP);
         ViewPortContext vpContext = vpContextFromVP.execute(currentVP);
-        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, drawingCtx);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
 
         int length = unitLength.execute(units);
         double[] result = new double[length];
@@ -69,15 +70,11 @@ public abstract class LConvert extends RExternalBuiltinNode.Arg4 {
 
         for (int i = 0; i < length; i++) {
             // scalar values used in current iteration
-            int axisFrom = axisFromVec.getDataAt(i % axisFromVec.getLength());
-            int axisTo = axisToVec.getDataAt(i % axisToVec.getLength());
-            boolean compatibleAxes = axisFrom == axisTo ||
-                            (axisFrom == 0 && axisTo == 2) ||
-                            (axisFrom == 2 && axisTo == 0) ||
-                            (axisFrom == 1 && axisTo == 3) ||
-                            (axisFrom == 3 && axisTo == 1);
-            double vpToSize = isXAxis(axisTo) ? vpTransform.size.getWidth() : vpTransform.size.getHeight();
-            double vpFromSize = isXAxis(axisFrom) ? vpTransform.size.getWidth() : vpTransform.size.getHeight();
+            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());
 
@@ -94,26 +91,26 @@ public abstract class LConvert extends RExternalBuiltinNode.Arg4 {
                 result[i] = transformFromNPC(tranfromToNPC(fromValue, fromUnitId, axisFrom, vpContext), unitTo, axisTo, vpContext);
             } else {
                 double inches = toInches(units, i, axisFrom, conversionCtx);
-                boolean isX = isXAxis(axisTo);
+                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, isDimension(axisTo), drawingCtx);
+                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, int axisFrom, UnitConversionContext conversionCtx) {
+    private double toInches(RAbstractVector units, int index, AxisOrDimension axisFrom, UnitConversionContext conversionCtx) {
         double inches;
-        if (isXAxis(axisFrom)) {
-            if (isDimension(axisFrom)) {
+        if (axisFrom.isHorizontal()) {
+            if (axisFrom.isDimension()) {
                 inches = unitToInches.convertWidth(units, index, conversionCtx);
             } else {
                 inches = unitToInches.convertX(units, index, conversionCtx);
             }
         } else {
-            if (isDimension(axisFrom)) {
+            if (axisFrom.isDimension()) {
                 inches = unitToInches.convertHeight(units, index, conversionCtx);
             } else {
                 inches = unitToInches.convertY(units, index, conversionCtx);
@@ -122,30 +119,30 @@ public abstract class LConvert extends RExternalBuiltinNode.Arg4 {
         return inches;
     }
 
-    private static double tranfromToNPC(double value, int fromUnitId, int axisFrom, ViewPortContext vpContext) {
+    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 = isXAxis(axisFrom);
+        boolean isX = axisFrom.isHorizontal();
         double min = isX ? vpContext.xscalemin : vpContext.yscalemin;
         double max = isX ? vpContext.xscalemax : vpContext.yscalemax;
-        if (isDimension(axisFrom)) {
+        if (axisFrom.isDimension()) {
             return value / (max - min);
         } else {
             return (value - min) / (max - min);
         }
     }
 
-    private static double transformFromNPC(double value, int unitTo, int axisTo, ViewPortContext vpContext) {
+    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 = isXAxis(axisTo);
+        boolean isX = axisTo.isHorizontal();
         double min = isX ? vpContext.xscalemin : vpContext.yscalemin;
         double max = isX ? vpContext.xscalemax : vpContext.yscalemax;
-        if (isDimension(axisTo)) {
+        if (axisTo.isDimension()) {
             return value * (max - min);
         } else {
             return min + value * (max - min);
@@ -157,13 +154,4 @@ public abstract class LConvert extends RExternalBuiltinNode.Arg4 {
     private static boolean isRelative(int unitId) {
         return unitId == NPC || unitId == NATIVE;
     }
-
-    // what = 0 means x, 1 means y, 2 means width, 3 means height
-    private static boolean isXAxis(int what) {
-        return what % 2 == 0;
-    }
-
-    private static boolean isDimension(int what) {
-        return what >= 2;
-    }
 }
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
index 0f429081b6cc99c8f4a9cd88e349e107acf55af6..de270734525fa83add0e666a969031c40c1ec98d 100644
--- 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
@@ -1,40 +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) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
- * Copyright (C) 2001-3 Paul Murrell
- * Copyright (c) 1998-2013, The R Core Team
- * Copyright (c) 2017, Oracle and/or its affiliates
+ * 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.
  *
- * All rights reserved.
+ * 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.GridUtils.asIntVector;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue;
 
 import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext;
-import com.oracle.truffle.r.library.fastrGrid.ViewPortContext.VPContextFromVPNode;
-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.RAbstractIntVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 
-/**
- * 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}.
- */
 public abstract class LLines extends RExternalBuiltinNode.Arg4 {
-    @Child private Unit.UnitToInchesNode unitToInches = Unit.createToInchesNode();
-    @Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode();
-    @Child private VPContextFromVPNode vpContextFromVP = new VPContextFromVPNode();
+    @Child private GridLinesNode gridLinesNode = GridLinesNode.createLines();
 
     static {
         Casts casts = new Casts(LLines.class);
@@ -48,59 +45,9 @@ public abstract class LLines extends RExternalBuiltinNode.Arg4 {
     }
 
     @Specialization
-    Object doLines(RAbstractVector x, RAbstractVector y, RList lengths, Object arrowIgnored) {
-        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);
-        ViewPortContext vpContext = vpContextFromVP.execute(currentVP);
-        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, 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];
-        double[] yy = new double[maxIndexesLen];
-        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
-                        dev.drawPolyLines(drawingCtx, xx, yy, start, (i - start) + 1);
-                    }
-                }
-                oldIsFinite = currIsFinite;
-            }
-        }
-
+    Object doLines(RAbstractVector x, RAbstractVector y, RList lengths, @SuppressWarnings("unused") Object arrowIgnored) {
+        // TODO: implement arrows
+        gridLinesNode.execute(x, y, lengths);
         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
index 040c72660f91850bbe93a56306fde72aff4c0345..67604044ec4ba6305f9429280680d2daf7e8d8ed 100644
--- 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
@@ -68,7 +68,7 @@ public abstract class LPoints extends RExternalBuiltinNode.Arg4 {
         double cex = GPar.getCex(gpar);
         ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP);
         ViewPortContext vpContext = vpContextFromVP.execute(currentVP);
-        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, drawingCtx);
+        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);
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..8d4343e9ab8267fb2732e90a6fa6568f8bb14376
--- /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);
+        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
index a7557972f356b4ffb2222996a75a3078ce376da0..e79c9dcd67cd90dba367f726de114c2dfb310ad3 100644
--- 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
@@ -56,7 +56,7 @@ public abstract class LRect extends RExternalBuiltinNode.Arg6 {
         DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
         ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP);
         ViewPortContext vpContext = vpContextFromVP.execute(currentVP);
-        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, drawingCtx);
+        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++) {
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
index 3aca3f3afc573ff7ecbffdd36790bb2c506d18cf..426234d0fbf5b1a9ae978f84f4d4ffc45a3731ad 100644
--- 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
@@ -55,7 +55,7 @@ public abstract class LSegments extends RExternalBuiltinNode.Arg5 {
         DrawingContext drawingCtx = GPar.asDrawingContext(ctx.getGridState().getGpar());
         ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP);
         ViewPortContext vpContext = vpContextFromVP.execute(currentVP);
-        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, drawingCtx);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, drawingCtx);
 
         int length = GridUtils.maxLength(unitLength, x0, y0, x1, y1);
         double[] xx = new double[2];
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
index 06952fdba756c381d8ce11f23b25e645625e2fa3..a9dd76013ca467a1189d3bcefbdad1c519d3c010 100644
--- 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
@@ -15,6 +15,7 @@ import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asAbstractContain
 import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asDouble;
 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.hasRClass;
@@ -34,6 +35,7 @@ import com.oracle.truffle.r.library.fastrGrid.UnitFactory.UnitToInchesNodeGen;
 import com.oracle.truffle.r.library.fastrGrid.ViewPortContext.VPContextFromVPNode;
 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.attributes.GetFixedAttributeNode;
 import com.oracle.truffle.r.nodes.helpers.InheritsCheckNode;
 import com.oracle.truffle.r.nodes.unary.CastNode;
@@ -177,13 +179,14 @@ public class Unit {
         }
     }
 
-    private static double convertToInches(double value, int unitId, double vpSize, double scalemin, double scalemax, int nullLMode, int nullAMode, boolean isDimension, DrawingContext drawingCtx) {
+    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 = isDimension ? value : (value - scalemin);
-                return (tmp / (scalemax - scalemin)) * vpSize;
+                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:
@@ -194,12 +197,18 @@ public class Unit {
                 return value / (CM_IN_INCH * 10);
             case CHAR:
             case MYCHAR:
-                return (value * drawingCtx.getFontSize()) / INCH_TO_POINTS_FACTOR;
+                return (value * ctx.drawingContext.getFontSize()) / INCH_TO_POINTS_FACTOR;
             case LINES:
             case MYLINES:
-                return (value * drawingCtx.getFontSize() * drawingCtx.getLineHeight()) / INCH_TO_POINTS_FACTOR;
+                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, nullLMode, nullAMode);
+                return evaluateNullUnit(value, vpSize, ctx.nullLayoutMode, ctx.nullArithmeticMode);
             default:
                 throw RInternalError.unimplemented("unit type " + unitId + " in convertToInches");
         }
@@ -387,6 +396,33 @@ public class Unit {
         }
     }
 
+    /**
+     * 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
@@ -397,20 +433,34 @@ public class Unit {
         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, DrawingContext drawingContext) {
-            this(viewPortSize, viewPortContext, drawingContext, 0, 0);
+        public UnitConversionContext(Size viewPortSize, ViewPortContext viewPortContext, GridDevice device, DrawingContext drawingContext) {
+            this(viewPortSize, viewPortContext, device, drawingContext, 0, 0);
         }
 
-        public UnitConversionContext(Size viewPortSize, ViewPortContext viewPortContext, DrawingContext drawingContext, int nullLMode, int nullAMode) {
+        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;
+        }
     }
 
     /**
@@ -425,34 +475,29 @@ public class Unit {
         }
 
         public double convertX(RAbstractContainer vector, int index, UnitConversionContext ctx) {
-            return execute(vector, index, ctx.viewPortSize.getWidth(), ctx.viewPortContext.xscalemin, ctx.viewPortContext.xscalemax, ctx.nullLayoutMode, ctx.nullArithmeticMode, false,
-                            ctx.drawingContext);
+            return execute(vector, index, ctx, AxisOrDimension.X);
         }
 
         public double convertY(RAbstractContainer vector, int index, UnitConversionContext ctx) {
-            return execute(vector, index, ctx.viewPortSize.getHeight(), ctx.viewPortContext.yscalemin, ctx.viewPortContext.yscalemax, ctx.nullLayoutMode, ctx.nullArithmeticMode, false,
-                            ctx.drawingContext);
+            return execute(vector, index, ctx, AxisOrDimension.Y);
         }
 
         public double convertWidth(RAbstractContainer vector, int index, UnitConversionContext ctx) {
-            return execute(vector, index, ctx.viewPortSize.getWidth(), ctx.viewPortContext.xscalemin, ctx.viewPortContext.xscalemax, ctx.nullLayoutMode, ctx.nullArithmeticMode, true,
-                            ctx.drawingContext);
+            return execute(vector, index, ctx, AxisOrDimension.WIDTH);
         }
 
         public double convertHeight(RAbstractContainer vector, int index, UnitConversionContext ctx) {
-            return execute(vector, index, ctx.viewPortSize.getHeight(), ctx.viewPortContext.yscalemin, ctx.viewPortContext.yscalemax, ctx.nullLayoutMode, ctx.nullArithmeticMode, true,
-                            ctx.drawingContext);
+            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, double vpSize, double scalemin, double scalemax, int nullLMode, int nullAMode, boolean isDimension,
-                        DrawingContext drawingCtx);
+        public abstract double execute(RAbstractContainer vector, int index, UnitConversionContext ctx, AxisOrDimension axisOrDim);
 
         @Specialization(guards = "isSimple(value)")
-        double doNormal(RAbstractContainer value, int index, double vpSize, double scalemin, double scalemax, int nullLMode, int nullAMode, boolean isDimension, DrawingContext drawingCtx,
+        double doNormal(RAbstractContainer value, int index, UnitConversionContext ctx, AxisOrDimension axisOrDim,
                         @Cached("createAsDoubleCast()") CastNode asDoubleCast,
                         @Cached("create()") UnitUnitIdNode unitUnitId) {
             int unitId = unitUnitId.execute(value, index);
@@ -460,29 +505,27 @@ public class Unit {
             double scalarValue = vector.getDataAt(index % vector.getLength());
             if (isGrobUnit(unitId)) {
                 RList grobList = asList(vector.getAttr("data"));
-                return grobUnitToInches.execute(scalarValue, unitId, grobList.getDataAt(index % grobList.getLength()), nullLMode, nullAMode);
+                return grobUnitToInches.execute(scalarValue, unitId, grobList.getDataAt(index % grobList.getLength()), ctx);
             }
-            return convertToInches(scalarValue, unitId, vpSize, scalemin, scalemax, nullLMode, nullAMode, isDimension, drawingCtx);
+            return convertToInches(scalarValue, unitId, asListOrNull(vector.getAttr("data")), ctx, axisOrDim);
         }
 
         @Specialization(guards = "isUnitList(value)")
-        double doList(RList value, int index, double vpSize, double scalemin, double scalemax, int nullLMode, int nullAMode, boolean isDimension, DrawingContext drawingCtx,
+        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, vpSize, scalemin, scalemax, nullLMode, nullAMode, isDimension, drawingCtx);
+                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, double vpSize, double scalemin, double scalemax, int nullLMode, int nullAMode, boolean isDimension, DrawingContext drawingCtx,
+        double doArithmetic(RList list, int index, UnitConversionContext ctx, AxisOrDimension axisOrDim,
                         @Cached("create()") UnitLengthNode unitLengthNode,
                         @Cached("create()") UnitToInchesNode recursiveNode) {
             ArithmeticUnit expr = ArithmeticUnit.asArithmeticUnit(list);
-            // Note the catch: newNullAMode is applied only if isDimension == true
-            BiFunction<RAbstractContainer, Integer, Double> recursive = (x, newNullAMode) -> recursiveNode.execute(x, index, vpSize, scalemin, scalemax, nullLMode,
-                            isDimension ? newNullAMode : nullAMode, isDimension, drawingCtx);
+            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);
@@ -496,11 +539,11 @@ public class Unit {
             }
 
             // must be aggregate operation
-            int newNullAMode = isDimension ? getNullAMode(expr.op) : nullAMode;
+            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, vpSize, scalemin, scalemax, nullLMode, newNullAMode, isDimension, drawingCtx);
+                values[i] = recursiveNode.execute(expr.arg1, i, newCtx, axisOrDim);
             }
 
             switch (expr.op) {
@@ -515,6 +558,12 @@ public class Unit {
             }
         }
 
+        // 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":
@@ -543,7 +592,7 @@ public class Unit {
 
         // transcribed from unit.c function evaluateGrobUnit
 
-        public double execute(double value, int unitId, Object grob, int nullLMode, int nullAMode) {
+        public double execute(double value, int unitId, Object grob, UnitConversionContext conversionCtx) {
             GridContext ctx = GridContext.getContext();
             RList currentVP = ctx.getGridState().getViewPort();
             getViewPortTransform.execute(currentVP);
@@ -571,10 +620,10 @@ public class Unit {
                 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(), nullLMode, nullAMode);
+                        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(), nullLMode, nullAMode);
+                        result = evaluateNullUnit(nullUnitValue, vpTransform.size.getWidth(), conversionCtx.nullLayoutMode, conversionCtx.nullArithmeticMode);
                     } else {
                         throw RInternalError.unimplemented("GrobUnitToInches from unit.c: 610");
                     }
@@ -585,16 +634,16 @@ public class Unit {
                         // 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(), nullLMode, nullAMode);
+                        result = evaluateNullUnit(nullUnitValue, vpTransform.size.getWidth(), conversionCtx.nullLayoutMode, conversionCtx.nullArithmeticMode);
                     } else {
-                        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, GPar.asDrawingContext(currentGP));
+                        UnitConversionContext newConversionCtx = new UnitConversionContext(vpTransform.size, vpContext, conversionCtx.device, GPar.asDrawingContext(currentGP));
                         initUnitToInchesNode();
                         if (unitId == GROBWIDTH) {
-                            result = unitToInchesNode.convertWidth((RAbstractContainer) unitxy.getDataAt(0), 0, conversionCtx);
+                            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, conversionCtx);
+                            result = unitToInchesNode.convertHeight((RAbstractContainer) unitxy.getDataAt(0), 0, newConversionCtx);
                         }
                     }
                     break;
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 6b0b5d88beed9b7238b8802637835e00132d6dcf..ba396e0f39fc1d68144c2a87f676b6cb71d0cf4b 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
@@ -35,6 +35,7 @@ import com.oracle.truffle.r.library.fastrGrid.LInitViewPortStack;
 import com.oracle.truffle.r.library.fastrGrid.LLines;
 import com.oracle.truffle.r.library.fastrGrid.LNewPage;
 import com.oracle.truffle.r.library.fastrGrid.LPoints;
+import com.oracle.truffle.r.library.fastrGrid.LPolygon;
 import com.oracle.truffle.r.library.fastrGrid.LRect;
 import com.oracle.truffle.r.library.fastrGrid.LSegments;
 import com.oracle.truffle.r.library.fastrGrid.LText;
@@ -702,6 +703,8 @@ public class CallAndExternalFunctions {
                     return LRect.create();
                 case "L_lines":
                     return LLines.create();
+                case "L_polygon":
+                    return LPolygon.create();
                 case "L_text":
                     return LText.create();
                 case "L_textBounds":
diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides
index 8cef96974b030c84f4d8b3c6f076da0f399fe32d..0095ec54be32afb7dde5f2cff0b1a5d62c8bf2d8 100644
--- a/mx.fastr/copyrights/overrides
+++ b/mx.fastr/copyrights/overrides
@@ -783,7 +783,7 @@ com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridUtil
 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/LLines.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