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
index b9d4070415da9c45435dc397973f973b1bd156c3..b78840d4010b45f7bd560bb8dac34d44d8c6292d 100644
--- 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
@@ -21,6 +21,9 @@ 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;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractIntVector;
 
 /**
  * Contains static method related to edge detection for bounds calculations.
@@ -106,9 +109,9 @@ final class EdgeDetection {
                 }
             } else { /* Intersect with top/bottom */
                 if (sinTheta > 0) { /* Top */
-                    return new Point(ymax, xm + dy / tanTheta);
+                    return new Point(xm + dy / tanTheta, ymax);
                 } else { /* Bottom */
-                    return new Point(ymin, xm - dy / tanTheta);
+                    return new Point(xm - dy / tanTheta, ymin);
                 }
             }
         }
@@ -200,6 +203,25 @@ final class EdgeDetection {
         return new Point(xm + ua * (x2 - xm), ym + ua * (y2 - ym));
     }
 
+    public static Point hullEdge(GridContext ctx, double[] xx, double[] yy, double theta) {
+        RDoubleVector xVec = RDataFactory.createDoubleVector(xx, RDataFactory.COMPLETE_VECTOR);
+        RDoubleVector yVec = RDataFactory.createDoubleVector(yy, RDataFactory.COMPLETE_VECTOR);
+        Object hullObj = ctx.evalInternalRFunction("chullWrapper", xVec, yVec);
+        RAbstractIntVector hull = GridUtils.asIntVector(hullObj);
+        double[] newXX = new double[hull.getLength()];
+        double[] newYY = new double[hull.getLength()];
+        for (int i = 0; i < hull.getLength(); i++) {
+            newXX[i] = xx[hull.getDataAt(i) - 1];
+            newYY[i] = yy[hull.getDataAt(i) - 1];
+        }
+        return polygonEdge(newXX, newYY, newXX.length, theta);
+    }
+
+    public static Point circleEdge(Point loc, double radius, double theta) {
+        double angle = theta / 180 * Math.PI;
+        return new Point(loc.x + radius * Math.cos(angle), loc.y + radius * Math.sin(angle));
+    }
+
     /**
      * An arbitrarily-oriented rectangle. The vertices are assumed to be in order going
      * anticlockwise around the rectangle.
@@ -220,4 +242,38 @@ final class EdgeDetection {
                             edgesIntersect(this.x[3], this.x[0], this.y[3], this.y[0], r2);
         }
     }
+
+    /**
+     * Represents min and max value for X and Y coordinates and provides convenient methods to
+     * update them.
+     */
+    public static final class Bounds {
+        public double minX = Double.MAX_VALUE;
+        public double maxX = Double.MIN_VALUE;
+        public double minY = Double.MAX_VALUE;
+        public double maxY = Double.MIN_VALUE;
+
+        public void update(Point p) {
+            updateX(p.x);
+            updateY(p.y);
+        }
+
+        public void updateX(double... values) {
+            minX = GridUtils.fmin(minX, values);
+            maxX = GridUtils.fmax(maxX, values);
+        }
+
+        public void updateY(double... values) {
+            minY = GridUtils.fmin(minY, values);
+            maxY = GridUtils.fmax(maxY, values);
+        }
+
+        public double getWidth() {
+            return maxX - minX;
+        }
+
+        public double getHeight() {
+            return maxY - minY;
+        }
+    }
 }
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
index 80d6a41600810582f69d82ff57ea527271e50bea..129215301579262da65f91884841a5bf9bbdf656 100644
--- 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
@@ -120,6 +120,14 @@ public final class FastRGridExternalLookup {
             case "L_points":
                 return LPoints.create();
 
+            // Bounds primitive:
+            case "L_rectBounds":
+                return LRectBounds.create();
+            case "L_locnBounds":
+                return LLocnBounds.create();
+            case "L_circleBounds":
+                return LCircleBounds.create();
+
             // Simple grid state access
             case "L_getGPar":
                 return new GridStateGetNode(GridState::getGpar);
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 8784637eb910a9e67c215f32264e12911f74d377..ebb3a0437791db7472543efd1764f8897ff54214 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
@@ -20,6 +20,7 @@ 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.RDoubleVector;
 import com.oracle.truffle.r.runtime.data.RList;
 import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RStringVector;
@@ -82,6 +83,10 @@ final class GridUtils {
         return result;
     }
 
+    static RDoubleVector createDoubleVector(double... values) {
+        return RDataFactory.createDoubleVector(values, RDataFactory.COMPLETE_VECTOR);
+    }
+
     static boolean hasRClass(RAttributable obj, String clazz) {
         RStringVector classAttr = obj.getClassAttr();
         if (classAttr == null) {
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 07ff0d4e4044922888dbb6dd11ae21aeb72b3e72..537905c6123e3541c2701f0aff4b24d20a29d9df 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
@@ -31,6 +31,10 @@ public abstract class LCircle extends RExternalBuiltinNode.Arg3 {
 
     static {
         Casts casts = new Casts(LCircle.class);
+        addCircleCasts(casts);
+    }
+
+    static void addCircleCasts(Casts casts) {
         casts.arg(0).mustBe(abstractVectorValue());
         casts.arg(1).mustBe(abstractVectorValue());
         casts.arg(2).mustBe(abstractVectorValue());
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircleBounds.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircleBounds.java
new file mode 100644
index 0000000000000000000000000000000000000000..3b18079922fd1f66c35627f5e57009f6bf794109
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircleBounds.java
@@ -0,0 +1,85 @@
+/*
+ * 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 com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.r.library.fastrGrid.EdgeDetection.Bounds;
+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.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 LCircleBounds extends RExternalBuiltinNode.Arg4 {
+    @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(LCircleBounds.class);
+        LCircle.addCircleCasts(casts);
+        casts.arg(3).mustBe(numericValue()).asDoubleVector().findFirst();
+    }
+
+    public static LCircleBounds create() {
+        return LCircleBoundsNodeGen.create();
+    }
+
+    @Specialization
+    @TruffleBoundary
+    Object doCircle(RAbstractVector xVec, RAbstractVector yVec, RAbstractVector radiusVec, double theta) {
+        GridContext ctx = GridContext.getContext();
+        GridDevice dev = ctx.getCurrentDevice();
+
+        RList currentVP = ctx.getGridState().getViewPort();
+        GPar gpar = GPar.create(ctx.getGridState().getGpar());
+        ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, dev);
+        ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, gpar);
+
+        int length = GridUtils.maxLength(unitLength, xVec, yVec, radiusVec);
+        Bounds bounds = new Bounds();
+        int count = 0;
+        Point loc = null;  // we remember the last position and radius
+        double radius = -1;
+        for (int i = 0; i < length; i++) {
+            Size radiusSizes = Size.fromUnits(unitToInches, radiusVec, radiusVec, i, conversionCtx);
+            radius = RMath.fmin2(radiusSizes.getWidth(), radiusSizes.getHeight());
+            loc = Point.fromUnits(unitToInches, xVec, yVec, i, conversionCtx);
+            if (loc.isFinite() && Double.isFinite(radius)) {
+                bounds.updateX(loc.x - radius, loc.x + radius);
+                bounds.updateY(loc.y - radius, loc.y + radius);
+                count++;
+            }
+        }
+
+        if (count == 0) {
+            return RNull.instance;
+        }
+        Point result;
+        assert loc != null;
+        if (count == 1) {
+            result = EdgeDetection.circleEdge(loc, radius, theta);
+        } else {
+            result = EdgeDetection.rectEdge(bounds.minX, bounds.minY, bounds.maxX, bounds.maxY, theta);
+        }
+
+        double scale = ctx.getGridState().getScale();
+        return GridUtils.createDoubleVector(result.x / scale, result.y / scale, bounds.getWidth() / scale, bounds.getHeight() / scale);
+    }
+}
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LLocnBounds.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LLocnBounds.java
new file mode 100644
index 0000000000000000000000000000000000000000..cd63a6165ef3b255ff5151b1373f3a4765f0c2aa
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LLocnBounds.java
@@ -0,0 +1,83 @@
+/*
+ * 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 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.EdgeDetection.Bounds;
+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.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;
+
+public abstract class LLocnBounds 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(LLocnBounds.class);
+        casts.arg(0).mustBe(abstractVectorValue());
+        casts.arg(1).mustBe(abstractVectorValue());
+        casts.arg(2).mustBe(numericValue()).asDoubleVector().findFirst();
+    }
+
+    public static LLocnBounds create() {
+        return LLocnBoundsNodeGen.create();
+    }
+
+    @Specialization
+    @TruffleBoundary
+    Object doBounds(RAbstractVector xVec, RAbstractVector yVec, double theta) {
+        GridContext ctx = GridContext.getContext();
+        GridDevice dev = ctx.getCurrentDevice();
+
+        RList currentVP = ctx.getGridState().getViewPort();
+        GPar gpar = GPar.create(ctx.getGridState().getGpar());
+        ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, dev);
+        ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, gpar);
+
+        int length = Math.max(Unit.getLength(xVec), Unit.getLength(yVec));
+        if (length == 0) {
+            return RNull.instance;
+        }
+
+        double[] xx = new double[length];
+        double[] yy = new double[length];
+        Bounds bounds = new Bounds();
+        int count = 0;
+        for (int i = 0; i < length; i++) {
+            Point loc = Point.fromUnits(unitToInches, xVec, yVec, i, conversionCtx);
+            xx[i] = loc.x;
+            yy[i] = loc.y;
+            if (loc.isFinite()) {
+                bounds.update(loc);
+                count++;
+            }
+        }
+
+        if (count == 0) {
+            return RNull.instance;
+        }
+
+        Point edge = EdgeDetection.hullEdge(ctx, xx, yy, theta);
+        double scale = ctx.getGridState().getScale();
+        return GridUtils.createDoubleVector(edge.x / scale, edge.y / scale, bounds.getWidth() / scale, bounds.getHeight() / scale);
+    }
+}
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 8fc3969d15b59e1d191e525a57fed2ce0948946f..2edc48e95a9791196cd97cdaac70c8b998a92281 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
@@ -33,6 +33,10 @@ public abstract class LRect extends RExternalBuiltinNode.Arg6 {
 
     static {
         Casts casts = new Casts(LRect.class);
+        addRectCasts(casts);
+    }
+
+    static void addRectCasts(Casts casts) {
         casts.arg(0).mustBe(abstractVectorValue());
         casts.arg(1).mustBe(abstractVectorValue());
         casts.arg(2).mustBe(abstractVectorValue());
@@ -60,8 +64,6 @@ public abstract class LRect extends RExternalBuiltinNode.Arg6 {
         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));
diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRectBounds.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRectBounds.java
new file mode 100644
index 0000000000000000000000000000000000000000..133bee5ddc4562265279aebaa73029616fc80ff4
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LRectBounds.java
@@ -0,0 +1,81 @@
+/*
+ * 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.EdgeDetection.rectEdge;
+import static com.oracle.truffle.r.library.fastrGrid.GridUtils.getDataAtMod;
+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.EdgeDetection.Bounds;
+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.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.RNull;
+import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
+import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
+
+public abstract class LRectBounds extends RExternalBuiltinNode.Arg7 {
+    @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(LRectBounds.class);
+        LRect.addRectCasts(casts);
+        casts.arg(6).mustBe(numericValue()).asDoubleVector().findFirst();
+    }
+
+    public static LRectBounds create() {
+        return LRectBoundsNodeGen.create();
+    }
+
+    @Specialization
+    @TruffleBoundary
+    Object execute(RAbstractVector xVec, RAbstractVector yVec, RAbstractVector wVec, RAbstractVector hVec, RAbstractDoubleVector hjust, RAbstractDoubleVector vjust, double theta) {
+        GridContext ctx = GridContext.getContext();
+        GridDevice dev = ctx.getCurrentDevice();
+
+        RList currentVP = ctx.getGridState().getViewPort();
+        GPar gpar = GPar.create(ctx.getGridState().getGpar());
+        ViewPortTransform vpTransform = getViewPortTransform.execute(currentVP, dev);
+        ViewPortContext vpContext = ViewPortContext.fromViewPort(currentVP);
+        UnitConversionContext conversionCtx = new UnitConversionContext(vpTransform.size, vpContext, dev, gpar);
+
+        int length = GridUtils.maxLength(unitLength, xVec, yVec, wVec, hVec);
+        Bounds bounds = new Bounds();
+        int nrect = 0;
+        for (int i = 0; i < length; i++) {
+            Size size = Size.fromUnits(unitToInches, wVec, hVec, i, conversionCtx);
+            Point origLoc = Point.fromUnits(unitToInches, xVec, yVec, i, conversionCtx);
+            // just calculate the bounds, no transformation necessary
+            Point loc = origLoc.justify(size, getDataAtMod(hjust, i), getDataAtMod(vjust, i));
+            if (size.isFinite() && loc.isFinite()) {
+                bounds.updateX(loc.x, loc.x + size.getWidth());
+                bounds.updateY(loc.y, loc.y + size.getHeight());
+                nrect++;
+            }
+        }
+
+        if (nrect == 0) {
+            return RNull.instance;
+        }
+
+        Point edge = rectEdge(bounds.minX, bounds.minY, bounds.maxX, bounds.maxY, theta);
+        double scale = ctx.getGridState().getScale();
+        return RDataFactory.createDoubleVector(new double[]{edge.x / scale, edge.y / scale, bounds.getWidth() / scale, bounds.getHeight() / scale}, RDataFactory.COMPLETE_VECTOR);
+    }
+}
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
index c428af3df6956bf7813712e41955f796e7d2e21f..5ab5ed637301e950b718f983d85a6afb86795f16 100644
--- 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
@@ -48,4 +48,8 @@ public final class Size {
     public double getHeight() {
         return height;
     }
+
+    public boolean isFinite() {
+        return Double.isFinite(width) && Double.isFinite(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
index e1051c98a46937db800261a3ea626a87c04061f9..28ed32b803c8510197c5f581ff4144f0a8b7e6d3 100644
--- 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
@@ -13,6 +13,11 @@ package com.oracle.truffle.r.library.fastrGrid;
 
 import static com.oracle.truffle.r.runtime.nmath.MathConstants.M_PI;
 
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RError.Message;
+
+// transcribed from matrix.c
+
 /**
  * Operations on transformation (3x3) matrices.
  */
@@ -115,4 +120,24 @@ final class TransformMatrix {
         }
         return res;
     }
+
+    public static double[][] inversion(double[][] t) {
+        double det = t[0][0] * (t[2][2] * t[1][1] - t[2][1] * t[1][2]) -
+                        t[1][0] * (t[2][2] * t[0][1] - t[2][1] * t[0][2]) +
+                        t[2][0] * (t[1][2] * t[0][1] - t[1][1] * t[0][2]);
+        if (det == 0) {
+            throw RError.error(RError.NO_CALLER, Message.GENERIC, "singular transformation matrix");
+        }
+        double[][] invt = new double[3][3];
+        invt[0][0] = 1 / det * (t[2][2] * t[1][1] - t[2][1] * t[1][2]);
+        invt[0][1] = -1 / det * (t[2][2] * t[0][1] - t[2][1] * t[0][2]);
+        invt[0][2] = 1 / det * (t[1][2] * t[0][1] - t[1][1] * t[0][2]);
+        invt[1][0] = -1 / det * (t[2][2] * t[1][0] - t[2][0] * t[1][2]);
+        invt[1][1] = 1 / det * (t[2][2] * t[0][0] - t[2][0] * t[0][2]);
+        invt[1][2] = -1 / det * (t[1][2] * t[0][0] - t[1][0] * t[0][2]);
+        invt[2][0] = 1 / det * (t[2][1] * t[1][0] - t[2][0] * t[1][1]);
+        invt[2][1] = -1 / det * (t[2][1] * t[0][0] - t[2][0] * t[0][1]);
+        invt[2][2] = 1 / det * (t[1][1] * t[0][0] - t[1][0] * t[0][1]);
+        return invt;
+    }
 }
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
index 4614a924ef43e17c6a2fb543700655a6cca47a79..70cf79d4d7c0399354a085f177ca612d986356e4 100644
--- 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
@@ -15,6 +15,15 @@
 # logic to R. Some functions implement whole externals, like L_downvppath, some implement coherent
 # parts of the logic and the rest is in Java.
 
+
+# chull from grDevices package is used in EdgeDetection.java
+# Note: chull calls to native function, which we may consider
+# porting or calling directly in the future.
+chullWrapper <- function(x, y) {
+    library(grDevices)
+    grDevices:::chull(x, y)
+}
+
 # Returns list with elements [[1]] - depth, zero if not found, [[2]] - the viewport, NULL if not found
 # We are searching for child "name" in "pvp", if the "path" is not missing,
 # then also pathMatch(path, currPath) must hold.
diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides
index 2298d75da7d9aa0550a0f5272cdc5557229768c5..a72c103a9a0f35fe0d2e62be3dd50fb1c660ff2d 100644
--- a/mx.fastr/copyrights/overrides
+++ b/mx.fastr/copyrights/overrides
@@ -744,6 +744,9 @@ com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/p
 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/LRectBounds.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LCircleBounds.java,gnu_r_murrel_core.copyright
+com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LLocnBounds.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