Skip to content
Snippets Groups Projects
Commit 193fbef0 authored by stepan's avatar stepan
Browse files

FastR Grid: support arrows

parent 3dd52c8c
No related branches found
No related tags found
No related merge requests found
/*
* 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
}
}
}
......@@ -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;
......
......@@ -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);
......
......@@ -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;
}
}
......@@ -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;
}
}
......@@ -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;
}
......
......@@ -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
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment