From cf4551064e7d6a2531e1859a24dd9dbea2cccdbf Mon Sep 17 00:00:00 2001
From: stepan <stepan.sindelar@oracle.com>
Date: Tue, 7 Mar 2017 14:16:07 +0100
Subject: [PATCH] FastR grid: implement L_convert

---
 .../truffle/r/library/fastrGrid/LConvert.java | 98 +++++++++++++++++++
 .../truffle/r/library/fastrGrid/Unit.java     | 84 ++++++++++++----
 .../foreign/CallAndExternalFunctions.java     |  3 +
 mx.fastr/copyrights/overrides                 |  1 +
 4 files changed, 167 insertions(+), 19 deletions(-)
 create mode 100644 com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LConvert.java

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 0000000000..0733409cb1
--- /dev/null
+++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LConvert.java
@@ -0,0 +1,98 @@
+/*
+ * 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.VALID_UNIT_ATTR;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.gte;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.lte;
+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.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.RInternalError;
+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.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();
+    @Child private VPContextFromVPNode vpContextFromVP = new VPContextFromVPNode();
+
+    static {
+        Casts casts = new Casts(LConvert.class);
+        casts.arg(0).mustBe(abstractVectorValue());
+        casts.arg(1).mustBe(numericValue()).asIntegerVector().findFirst().mustBe(gte(0).and(lte(3)));
+        casts.arg(2).mustBe(numericValue()).asIntegerVector().findFirst().mustBe(gte(0).and(lte(3)));
+        casts.arg(3).mustBe(numericValue()).asIntegerVector().findFirst().mustBe(gte(0).and(lte(Unit.LAST_NORMAL_UNIT)));
+    }
+
+    public static LConvert create() {
+        return LConvertNodeGen.create();
+    }
+
+    @Specialization
+    Object doConvert(RAbstractVector units, int axisFrom, int axisTo, int unitTo) {
+        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);
+
+        int length = unitLength.execute(units);
+        double[] result = new double[length];
+
+        int fromUnitId = RRuntime.asInteger(units.getAttr(VALID_UNIT_ATTR));
+        boolean relativeUnits = isRelative(unitTo) || isRelative(fromUnitId);
+        if ((vpTransform.size.getHeight() < 1e-6 || vpTransform.size.getWidth() < 1e-6) && relativeUnits) {
+            throw RInternalError.unimplemented("L_convert: relative units with close to zero width or height");
+        }
+
+        for (int i = 0; i < length; i++) {
+            double inches;
+            if (isXAxis(axisFrom)) {
+                inches = unitToInches.convertX(units, i, conversionCtx);
+            } else {
+                inches = unitToInches.convertY(units, i, conversionCtx);
+            }
+            if (isXAxis(axisTo)) {
+                result[i] = Unit.convertFromInches(inches, unitTo, vpTransform.size.getWidth(), vpContext.xscalemin, vpContext.xscalemax, drawingCtx);
+            } else {
+                result[i] = Unit.convertFromInches(inches, unitTo, vpTransform.size.getHeight(), vpContext.yscalemin, vpContext.yscalemax, drawingCtx);
+            }
+        }
+
+        return RDataFactory.createDoubleVector(result, RDataFactory.COMPLETE_VECTOR);
+    }
+
+    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;
+    }
+}
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 c12899186e..f646edaf0c 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
@@ -34,13 +34,13 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
  * enter our system.
  */
 public class Unit {
-    private static final String VALID_UNIT_ATTR = "valid.unit";
+    static final String VALID_UNIT_ATTR = "valid.unit";
 
-    private static final int NPC = 0;
+    public static final int NPC = 0;
     public static final int CM = 1;
     public static final int INCHES = 2;
-    private static final int LINES = 3;
-    private static final int NATIVE = 4;
+    public static final int LINES = 3;
+    public static final int NATIVE = 4;
     public static final int NULL = 5; /* only used in layout specifications (?) */
     public static final int SNPC = 6;
     public static final int MM = 7;
@@ -71,6 +71,7 @@ public class Unit {
     public static final int GROBHEIGHT = 22;
     public static final int GROBASCENT = 23;
     public static final int GROBDESCENT = 24;
+    public static final int LAST_NORMAL_UNIT = GROBDESCENT;
     /*
      * No longer used
      */
@@ -97,6 +98,65 @@ public class Unit {
         return UnitToInchesNode.create();
     }
 
+    static double convertFromInches(double value, int unitId, double vpSize, double scalemin, double scalemax, DrawingContext drawingCtx) {
+        switch (unitId) {
+            case NATIVE:
+                return ((value + scalemin) * (scalemax - scalemin)) / vpSize;
+            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");
+        }
+    }
+
+    static double convertToInches(double value, int unitId, double vpSize, double scalemin, double scalemax, DrawingContext drawingCtx) {
+        switch (unitId) {
+            case NATIVE:
+                return ((value - scalemin) / (scalemax - scalemin)) * 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 LINES:
+            case MYLINES:
+                return (value * drawingCtx.getFontSize() * drawingCtx.getLineHeight()) / INCH_TO_POINTS_FACTOR;
+
+            default:
+                throw RInternalError.unimplemented("unit type " + unitId + " in convertToInches");
+        }
+    }
+
+    public static UnitElementAtNode createElementAtNode() {
+        return UnitElementAtNode.create();
+    }
+
     public abstract static class UnitNodeBase extends Node {
         @Child private InheritsCheckNode inheritsCheckNode = new InheritsCheckNode("unit.arithmetic");
 
@@ -191,7 +251,7 @@ public class Unit {
         @Specialization(guards = "!isArithmetic(value)")
         double doNormal(RAbstractContainer value, int index, double vpSize, double scalemin, double scalemax, DrawingContext drawingCtx) {
             int unitId = (Integer) castUnitId.execute(value.getAttr(VALID_UNIT_ATTR));
-            return convert(elementAtNode.execute(value, index % value.getLength()), unitId, vpSize, scalemin, scalemax, drawingCtx);
+            return convertToInches(elementAtNode.execute(value, index % value.getLength()), unitId, vpSize, scalemin, scalemax, drawingCtx);
         }
 
         @Specialization(guards = "isArithmetic(list)")
@@ -199,19 +259,5 @@ public class Unit {
             throw RInternalError.unimplemented("UnitToInches for arithmetic units");
         }
 
-        private static double convert(double value, int unitId, double vpSize, double scalemin, double scalemax, DrawingContext drawingCtx) {
-            switch (unitId) {
-                case NATIVE:
-                    return ((value - scalemin) / (scalemax - scalemin)) * vpSize;
-                case NPC:
-                    return value * vpSize;
-                case LINES:
-                case MYLINES:
-                    return (value * drawingCtx.getFontSize() * drawingCtx.getLineHeight()) / INCH_TO_POINTS_FACTOR;
-
-                default:
-                    throw RInternalError.unimplemented("unit type " + unitId + " in UnitToInches");
-            }
-        }
     }
 }
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 9a51634737..9e39ceca94 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
@@ -27,6 +27,7 @@ import com.oracle.truffle.r.library.fastrGrid.GridStateGetNode;
 import com.oracle.truffle.r.library.fastrGrid.GridStateSetNode;
 import com.oracle.truffle.r.library.fastrGrid.IgnoredGridExternal;
 import com.oracle.truffle.r.library.fastrGrid.LCircle;
+import com.oracle.truffle.r.library.fastrGrid.LConvert;
 import com.oracle.truffle.r.library.fastrGrid.LGridDirty;
 import com.oracle.truffle.r.library.fastrGrid.LInitGrid;
 import com.oracle.truffle.r.library.fastrGrid.LInitViewPortStack;
@@ -680,6 +681,8 @@ public class CallAndExternalFunctions {
                     return LInitGrid.create();
                 case "L_newpage":
                     return new LNewPage();
+                case "L_convert":
+                    return LConvert.create();
 
                 // Viewport management
                 case "L_upviewport":
diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides
index 670e335899..8119e383c8 100644
--- a/mx.fastr/copyrights/overrides
+++ b/mx.fastr/copyrights/overrides
@@ -789,3 +789,4 @@ com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ColorNam
 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
-- 
GitLab