diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DrawArrowsNode.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DrawArrowsNode.java new file mode 100644 index 0000000000000000000000000000000000000000..21a2208f1664412db23a9fba42873b2b579ab081 --- /dev/null +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DrawArrowsNode.java @@ -0,0 +1,99 @@ +/* + * This material is distributed under the GNU General Public License + * Version 2. You may review the terms of this license at + * http://www.gnu.org/licenses/gpl-2.0.html + * + * Copyright (C) 2001-3 Paul Murrell + * Copyright (c) 1998-2013, The R Core Team + * Copyright (c) 2017, Oracle and/or its affiliates + * + * All rights reserved. + */ +package com.oracle.truffle.r.library.fastrGrid; + +import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asAbstractContainer; +import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asDouble; +import static com.oracle.truffle.r.library.fastrGrid.GridUtils.asInt; + +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.r.library.fastrGrid.Unit.UnitConversionContext; +import com.oracle.truffle.r.library.fastrGrid.Unit.UnitToInchesNode; +import com.oracle.truffle.r.library.fastrGrid.device.DrawingContext; +import com.oracle.truffle.r.library.fastrGrid.device.GridDevice; +import com.oracle.truffle.r.runtime.data.RList; +import com.oracle.truffle.r.runtime.data.model.RAbstractContainer; + +public class DrawArrowsNode extends Node { + // Structure of an arrow description + public static final int ARROWANGLE = 0; + public static final int ARROWLENGTH = 1; + public static final int ARROWENDS = 2; + public static final int ARROWTYPE = 3; + // known values of ARROWTYPE + public static final int ARROWTYPE_LINES = 1; + public static final int ARROWTYPE_POLYGON = 2; + + @Child private UnitToInchesNode unitToInches = Unit.createToInchesNode(); + + /** + * Draws arrows at the start and end of given lines. + * + * @param x x-positions of the line(s) + * @param y y-positions of the line(s) + * @param startIndex consider arrays x,y to start from this index + * @param length consider arrays x,y to have this length + * @param parentIndex the index of the line we are drawing, this is used for choosing the right + * index into vectors extracted from arrow + * @param arrow list with various attributes of the arrow + * @param start should we draw start arrow if the arrow list says so. Otherwise never draw it. + * @param end should we draw end arrow if the arrow list says so. Otherwise never draw it. + * @param conversionCtx needed for unit conversions. + */ + public void drawArrows(double[] x, double[] y, int startIndex, int length, int parentIndex, RList arrow, boolean start, boolean end, UnitConversionContext conversionCtx) { + assert x.length == y.length; + int endsVal = asInt(arrow.getDataAt(ARROWENDS), parentIndex); + boolean first = endsVal != 2; + boolean last = endsVal != 1; + if ((!first || !start) && (!last || !end)) { + // if we are not going to draw any arrow anyway, just finish + return; + } + // extract angle, length in inches and arrow type from 'arrow' + double angle = asDouble(arrow.getDataAt(ARROWANGLE), parentIndex); + int arrowType = asInt(arrow.getDataAt(ARROWTYPE), parentIndex); + RAbstractContainer lengthVec = asAbstractContainer(arrow.getDataAt(ARROWLENGTH)); + double arrowLength = unitToInches.convertHeight(lengthVec, parentIndex, conversionCtx); + arrowLength = Math.max(arrowLength, unitToInches.convertWidth(lengthVec, parentIndex, conversionCtx)); + // draw the arrows + GridDevice device = conversionCtx.device; + DrawingContext drawingCtx = conversionCtx.drawingContext; + if (first && start) { + drawArrow(drawingCtx, device, arrowType, x[startIndex], y[startIndex], x[startIndex + 1], y[startIndex + 1], angle, arrowLength); + } + if (last && end) { + int n = startIndex + length; + drawArrow(drawingCtx, device, arrowType, x[n - 1], y[n - 1], x[n - 2], y[n - 2], angle, arrowLength); + } + } + + private void drawArrow(DrawingContext drawingCtx, GridDevice device, int arrowType, double x0, double y0, double x1, double y1, double angle, double length) { + double a = Math.toRadians(angle); + double xc = x1 - x0; + double yc = y1 - y0; + double rot = Math.atan2(yc, xc); + double[] vertx = new double[3]; + double[] verty = new double[3]; + vertx[0] = x0 + length * Math.cos(rot + a); + verty[0] = y0 + length * Math.sin(rot + a); + vertx[1] = x0; + verty[1] = y0; + vertx[2] = x0 + length * Math.cos(rot - a); + verty[2] = y0 + length * Math.sin(rot - a); + if (arrowType == ARROWTYPE_LINES) { + device.drawPolyLines(drawingCtx, vertx, verty, 0, 3); + } else if (arrowType == ARROWTYPE_POLYGON) { + device.drawPolyLines(drawingCtx, vertx, verty, 0, 3); + // TODO: real polygon + } + } +} diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridLinesNode.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridLinesNode.java index 24cb71abc5bd43d09fa64d4c97790293c3a0119f..11946cb521b7cbecf7857795fc26748d434a47d2 100644 --- 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 @@ -42,8 +42,9 @@ public abstract class GridLinesNode extends Node { @Child private Unit.UnitToInchesNode unitToInches = Unit.createToInchesNode(); @Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode(); + @Child private DrawArrowsNode drawArrowsNode = new DrawArrowsNode(); - void execute(RAbstractVector x, RAbstractVector y, RList lengths) { + void execute(RAbstractVector x, RAbstractVector y, RList lengths, RList arrow) { GridContext ctx = GridContext.getContext(); GridDevice dev = ctx.getCurrentDevice(); @@ -90,6 +91,11 @@ public abstract class GridLinesNode extends Node { // it's last iteration. This seems slightly weird, but that's how GnuR seems // to work drawPolylines(dev, drawingCtx, yy, xx, start, (i - start) + 1); + if (arrow != null) { + // Can draw an arrow at the start if the points include the first point. + // Draw an arrow at the end only if this is the last series + drawArrowsNode.drawArrows(xx, yy, start, (i - start) + 1, unitIndex, arrow, start == 0, lastIter, conversionCtx); + } } } oldIsFinite = currIsFinite; 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 1b7ff9197a90621615f2628037cd26df56593c6b..c7680a918f6ef6518023b6cb14dcbbe1258d6e68 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 @@ -124,6 +124,30 @@ final class GridUtils { throw RError.error(RError.NO_CALLER, Message.GENERIC, "Unexpected non double/integer value " + val.getClass().getSimpleName()); } + static double asDouble(Object val, int cyclicIndex) { + if (val instanceof Double) { + return (int) val; + } else if (val instanceof RAbstractDoubleVector) { + RAbstractDoubleVector vec = (RAbstractDoubleVector) val; + if (vec.getLength() > 0) { + return vec.getDataAt(cyclicIndex % vec.getLength()); + } + } + throw RError.error(RError.NO_CALLER, Message.GENERIC, "Unexpected non double value " + val.getClass().getSimpleName()); + } + + static int asInt(Object val, int cyclicIndex) { + if (val instanceof Integer) { + return (int) val; + } else if (val instanceof RAbstractIntVector) { + RAbstractIntVector vec = (RAbstractIntVector) val; + if (vec.getLength() > 0) { + return vec.getDataAt(cyclicIndex % vec.getLength()); + } + } + throw RError.error(RError.NO_CALLER, Message.GENERIC, "Unexpected non integer value " + val.getClass().getSimpleName()); + } + static RAbstractIntVector asIntVector(Object value) { if (value instanceof Integer) { return RDataFactory.createIntVectorFromScalar((Integer) value); 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 de270734525fa83add0e666a969031c40c1ec98d..e38149b8f1da4283dc5986c7e42032678fcc7b84 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 @@ -38,6 +38,7 @@ public abstract class LLines extends RExternalBuiltinNode.Arg4 { casts.arg(0).mustBe(abstractVectorValue()); casts.arg(1).mustBe(abstractVectorValue()); casts.arg(2).mustBe(RList.class); + casts.arg(2).allowNull().mustBe(RList.class); } public static LLines create() { @@ -45,9 +46,14 @@ public abstract class LLines extends RExternalBuiltinNode.Arg4 { } @Specialization - Object doLines(RAbstractVector x, RAbstractVector y, RList lengths, @SuppressWarnings("unused") Object arrowIgnored) { - // TODO: implement arrows - gridLinesNode.execute(x, y, lengths); + Object doLines(RAbstractVector x, RAbstractVector y, RList lengths, RNull arrowIgnored) { + gridLinesNode.execute(x, y, lengths, null); + return RNull.instance; + } + + @Specialization + Object doLines(RAbstractVector x, RAbstractVector y, RList lengths, @SuppressWarnings("unused") RList arrow) { + gridLinesNode.execute(x, y, lengths, arrow); return RNull.instance; } } diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPolygon.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPolygon.java index 8d4343e9ab8267fb2732e90a6fa6568f8bb14376..a4d8a917bd2bc2387d1faa7cb8b5026ee5fb37c3 100644 --- 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 @@ -46,7 +46,7 @@ public abstract class LPolygon extends RExternalBuiltinNode.Arg3 { @Specialization Object doLines(RAbstractVector x, RAbstractVector y, RList lengths) { - gridLinesNode.execute(x, y, lengths); + gridLinesNode.execute(x, y, lengths, null); return RNull.instance; } } diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LSegments.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LSegments.java index 33e8f7d514746ddb8cd051186cf3c7fe9b06a146..6ec185aef101492430df1e2bcc70870fc69c9663 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 @@ -31,6 +31,7 @@ public abstract class LSegments extends RExternalBuiltinNode.Arg5 { @Child private Unit.UnitToInchesNode unitToInches = Unit.createToInchesNode(); @Child private Unit.UnitLengthNode unitLength = Unit.createLengthNode(); @Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode(); + @Child private DrawArrowsNode drawArrowsNode = new DrawArrowsNode(); static { Casts casts = new Casts(LSegments.class); @@ -38,6 +39,7 @@ public abstract class LSegments extends RExternalBuiltinNode.Arg5 { casts.arg(1).mustBe(abstractVectorValue()); casts.arg(2).mustBe(abstractVectorValue()); casts.arg(3).mustBe(abstractVectorValue()); + casts.arg(4).allowNull().mustBe(RList.class); } public static LSegments create() { @@ -45,7 +47,12 @@ public abstract class LSegments extends RExternalBuiltinNode.Arg5 { } @Specialization - Object doSegments(RAbstractVector x0, RAbstractVector y0, RAbstractVector x1, RAbstractVector y1, Object arrowIgnored) { + Object doSegments(RAbstractVector x0, RAbstractVector y0, RAbstractVector x1, RAbstractVector y1, RNull arrow) { + return doSegments(x0, y0, x1, y1, (RList) null); + } + + @Specialization + Object doSegments(RAbstractVector x0, RAbstractVector y0, RAbstractVector x1, RAbstractVector y1, RList arrow) { GridContext ctx = GridContext.getContext(); GridDevice dev = ctx.getCurrentDevice(); @@ -69,6 +76,9 @@ public abstract class LSegments extends RExternalBuiltinNode.Arg5 { yy[0] = loc1.y; yy[1] = loc2.y; dev.drawPolyLines(drawingCtx, xx, yy, 0, 2); + if (arrow != null) { + drawArrowsNode.drawArrows(xx, yy, 0, 2, i, arrow, true, true, conversionCtx); + } } return RNull.instance; } diff --git a/mx.fastr/copyrights/overrides b/mx.fastr/copyrights/overrides index 7c18b674af82fa67a5378855747a905b446ecfca..8dc2bf1018ce4a56a935a0346e28f0c49e17a721 100644 --- a/mx.fastr/copyrights/overrides +++ b/mx.fastr/copyrights/overrides @@ -767,6 +767,7 @@ 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/DrawArrowsNode.java,gnu_r_murrel_core.copyright com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LPoints.java,gnu_r_murrel_core.copyright com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GridState.java,gnu_r_murrel_core.copyright com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/DoSetViewPortBuiltin.java,gnu_r_murrel_core.copyright