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

FastR grid: implement L_textBounds

parent 853ac156
No related branches found
No related tags found
No related merge requests found
Showing
with 610 additions and 105 deletions
/*
* 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.fmax;
import static com.oracle.truffle.r.library.fastrGrid.GridUtils.fmin;
import static com.oracle.truffle.r.runtime.nmath.MathConstants.M_PI;
import static com.oracle.truffle.r.runtime.nmath.RMath.fmax2;
import static com.oracle.truffle.r.runtime.nmath.RMath.fmin2;
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;
/**
* Contains static method related to edge detection for bounds calculations.
*/
public final class EdgeDetection {
private EdgeDetection() {
// only static members
}
/**
* Do two lines intersect? Algorithm from Paul Bourke
* (http://www.swin.edu.au/astronomy/pbourke/geometry/lineline2d/index.html)
*/
static boolean linesIntersect(double x1, double x2, double x3, double x4,
double y1, double y2, double y3, double y4) {
double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
double ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3));
// If the lines are parallel ...
if (denom == 0) {
// If the lines are coincident ...
if (ua == 0) {
// If the lines are vertical ...
if (x1 == x2) {
// Compare y-values
if (!((y1 < y3 && fmax2(y1, y2) < fmin2(y3, y4)) ||
(y3 < y1 && fmax2(y3, y4) < fmin2(y1, y2))))
return true;
} else {
// Compare x-values
if (!((x1 < x3 && fmax2(x1, x2) < fmin2(x3, x4)) ||
(x3 < x1 && fmax2(x3, x4) < fmin2(x1, x2))))
return true;
}
}
}
// ... otherwise, calculate where the lines intersect ...
else {
double ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3));
ua = ua / denom;
ub = ub / denom;
// Check for overlap
if ((ua > 0 && ua < 1) && (ub > 0 && ub < 1))
return true;
}
return false;
}
static boolean edgesIntersect(double x1, double x2, double y1, double y2, Rectangle r) {
return linesIntersect(x1, x2, r.x[0], r.x[1], y1, y2, r.y[0], r.y[1]) ||
linesIntersect(x1, x2, r.x[1], r.x[2], y1, y2, r.y[1], r.y[2]) ||
linesIntersect(x1, x2, r.x[2], r.x[3], y1, y2, r.y[2], r.y[3]) ||
linesIntersect(x1, x2, r.x[3], r.x[0], y1, y2, r.y[3], r.y[0]);
}
static Point rectEdge(double xmin, double ymin, double xmax, double ymax, double theta) {
double xm = (xmin + xmax) / 2;
double ym = (ymin + ymax) / 2;
double dx = (xmax - xmin) / 2;
double dy = (ymax - ymin) / 2;
/*
* GNUR fixme: Special case 0 width or 0 height
*/
// Special case angles
if (theta == 0) {
return new Point(xmax, ym);
} else if (theta == 270) {
return new Point(xm, ymin);
} else if (theta == 180) {
return new Point(xmin, ym);
} else if (theta == 90) {
return new Point(xm, ymax);
} else {
double cutoff = dy / dx;
double angle = theta / 180 * M_PI;
double tanTheta = Math.tan(angle);
double cosTheta = Math.cos(angle);
double sinTheta = Math.sin(angle);
if (fabs(tanTheta) < cutoff) { /* Intersect with side */
if (cosTheta > 0) { /* Right side */
return new Point(xmax, ym + tanTheta * dx);
} else { /* Left side */
return new Point(xmin, ym - tanTheta * dx);
}
} else { /* Intersect with top/bottom */
if (sinTheta > 0) { /* Top */
return new Point(ymax, xm + dy / tanTheta);
} else { /* Bottom */
return new Point(ymin, xm - dy / tanTheta);
}
}
}
}
static Point polygonEdge(double[] x, double[] y, int n, double theta) {
// centre of the polygon
double xmin = fmin(Double.MAX_VALUE, x);
double xmax = fmax(Double.MIN_VALUE, x);
double ymin = fmin(Double.MAX_VALUE, y);
double ymax = fmax(Double.MIN_VALUE, y);
double xm = (xmin + xmax) / 2;
double ym = (ymin + ymax) / 2;
// Special case zero-width or zero-height
if (fabs(xmin - xmax) < 1e-6) {
double resultY = theta == 90 ? ymax : theta == 270 ? ymin : ym;
return new Point(xmin, ymin);
}
if (fabs(ymin - ymax) < 1e-6) {
double resultY = theta == 0 ? xmax : theta == 180 ? xmin : xm;
return new Point(ymin, resultY);
}
/*
* Find edge that intersects line from centre at angle theta
*/
boolean found = false;
double angle = theta / 180 * M_PI;
double vangle1;
double vangle2;
int v1 = 0;
int v2 = 1;
for (int i = 0; i < n; i++) {
v1 = i;
v2 = v1 + 1;
if (v2 == n) {
v2 = 0;
}
/*
* Result of atan2 is in range -PI, PI so convert to 0, 360 to correspond to angle
*/
vangle1 = Math.atan2(y[v1] - ym, x[v1] - xm);
if (vangle1 < 0) {
vangle1 = vangle1 + 2 * M_PI;
}
vangle2 = Math.atan2(y[v2] - ym, x[v2] - xm);
if (vangle2 < 0) {
vangle2 = vangle2 + 2 * M_PI;
}
/*
* If vangle1 < vangle2 then angles are either side of 0 so check is more complicated
*/
if ((vangle1 >= vangle2 &&
vangle1 >= angle && vangle2 < angle) ||
(vangle1 < vangle2 &&
((vangle1 >= angle && 0 <= angle) ||
(vangle2 < angle && 2 * M_PI >= angle)))) {
found = true;
break;
}
}
/*
* Find intersection point of "line from centre to bounding rect" and edge
*/
if (!found) {
throw RError.error(RError.NO_CALLER, Message.GENERIC, "polygon edge not found");
}
double x3 = x[v1];
double y3 = y[v1];
double x4 = x[v2];
double y4 = y[v2];
Point tmp = rectEdge(xmin, ymin, xmax, ymax, theta);
double x2 = tmp.x;
double y2 = tmp.y;
double numa = ((x4 - x3) * (ym - y3) - (y4 - y3) * (xm - x3));
double denom = ((y4 - y3) * (x2 - xm) - (x4 - x3) * (y2 - ym));
double ua = numa / denom;
if (!Double.isFinite(ua)) {
/*
* Should only happen if lines are parallel, which shouldn't happen! Unless, perhaps the
* polygon has zero extent vertically or horizontally ... ?
*/
throw RInternalError.shouldNotReachHere("polygon edge not found (zero-width or zero-height?)");
}
/*
* numb = ((x2 - x1)*(y1 - y3) - (y2 - y1)*(x1 - x3)); ub = numb/denom;
*/
return new Point(xm + ua * (x2 - xm), ym + ua * (y2 - ym));
}
/**
* An arbitrarily-oriented rectangle. The vertices are assumed to be in order going
* anticlockwise around the rectangle.
*/
public static final class Rectangle {
public final double[] x;
public final double[] y;
public Rectangle(Point p1, Point p2, Point p3, Point p4) {
x = new double[]{p1.x, p2.x, p3.x, p4.x};
y = new double[]{p1.y, p2.y, p3.y, p4.y};
}
public boolean intersects(Rectangle r2) {
return edgesIntersect(this.x[0], this.x[1], this.y[0], this.y[1], r2) ||
edgesIntersect(this.x[1], this.x[2], this.y[1], this.y[2], r2) ||
edgesIntersect(this.x[2], this.x[3], this.y[2], this.y[3], r2) ||
edgesIntersect(this.x[3], this.x[0], this.y[3], this.y[0], r2);
}
}
}
......@@ -20,6 +20,7 @@ public final class GridState {
private RList gpar;
private RList viewPort;
private REnvironment gridEnv;
private double scale = 1;
private boolean deviceInitialized;
/**
......@@ -79,4 +80,9 @@ public final class GridState {
public void setCurrentGrob(Object currentGrob) {
this.currentGrob = currentGrob;
}
public double getScale() {
return scale;
}
}
/*
* 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.
*/
/*
* 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-2015, 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.fmax;
import static com.oracle.truffle.r.library.fastrGrid.GridUtils.fmin;
import static com.oracle.truffle.r.library.fastrGrid.GridUtils.getDataAtMod;
import static com.oracle.truffle.r.library.fastrGrid.TransformMatrix.multiply;
import static com.oracle.truffle.r.library.fastrGrid.TransformMatrix.transLocation;
import static com.oracle.truffle.r.library.fastrGrid.TransformMatrix.translation;
import static com.oracle.truffle.r.library.fastrGrid.device.DrawingContext.INCH_TO_POINTS_FACTOR;
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.profiles.ConditionProfile;
import com.oracle.truffle.r.library.fastrGrid.EdgeDetection.Rectangle;
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.NodeWithArgumentCasts.Casts;
import com.oracle.truffle.r.runtime.RInternalError;
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.RAbstractStringVector;
import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
import com.oracle.truffle.r.runtime.nodes.RBaseNode;
/**
* Implements what is in the original grid code implemented by {@code gridText} function.
*
* 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 Unit.UnitLengthNode}.
*/
public final class GridTextNode extends RBaseNode {
@Child private Unit.UnitToInchesNode unitToInches = Unit.createToInchesNode();
@Child private Unit.UnitLengthNode unitLength = Unit.createLengthNode();
@Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode();
@Child private VPContextFromVPNode vpContextFromVP = new VPContextFromVPNode();
private final ConditionProfile checkOverlapProfile = ConditionProfile.createBinaryProfile();
private final boolean draw;
static void addGridTextCasts(Casts casts) {
casts.arg(0).asStringVector();
casts.arg(1).mustBe(abstractVectorValue());
casts.arg(2).mustBe(abstractVectorValue());
casts.arg(3).mustBe(numericValue()).asDoubleVector();
casts.arg(4).mustBe(numericValue()).asDoubleVector();
casts.arg(5).mustBe(numericValue()).asDoubleVector();
}
private GridTextNode(boolean draw) {
this.draw = draw;
}
public static GridTextNode createDraw() {
return new GridTextNode(true);
}
public static GridTextNode createCalculateBounds() {
return new GridTextNode(false);
}
public Object gridText(RAbstractStringVector textVec, RAbstractVector x, RAbstractVector y, RAbstractDoubleVector hjustVec, RAbstractDoubleVector vjustVec, RAbstractDoubleVector rotationVec,
boolean checkOverlapIn, double theta) {
if (textVec.getLength() == 0) {
return RNull.instance;
}
boolean checkOverlap = checkOverlapProfile.profile(checkOverlapIn);
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 = GridUtils.maxLength(unitLength, x, y);
// following variables will hold the (intermediate) results of bounds checking
int boundsCount = 0;
Point edge = null;
double xmin = Double.MAX_VALUE;
double xmax = Double.MIN_VALUE;
double ymin = Double.MAX_VALUE;
double ymax = Double.MIN_VALUE;
int ntxt = 0; // number of texts that were actually used for bounds computation
EdgeDetection.Rectangle[] bounds = null;
if (checkOverlap || !draw) {
bounds = new EdgeDetection.Rectangle[length];
}
for (int i = 0; i < length; i++) {
Point loc = Point.fromUnits(unitToInches, x, y, i, conversionCtx);
if (draw) {
// transformation not necessary for bounds calculation
loc = transLocation(loc, vpTransform.transform);
}
String text = textVec.getDataAt(i % textVec.getLength());
double hjust = getDataAtMod(hjustVec, i);
double vjust = getDataAtMod(vjustVec, i);
double rotation = getDataAtMod(rotationVec, i);
// update bounds if necessary
boolean doDraw = true;
Rectangle trect = null;
if (checkOverlap || !draw) {
trect = textRect(loc, hjust, vjust, rotation, text, drawingCtx, dev);
for (int j = 0; j < boundsCount; j++) {
if (trect.intersects(bounds[j])) {
doDraw = false;
break;
}
}
if (doDraw) {
bounds[boundsCount++] = trect;
}
}
// actual drawing
if (draw && doDraw) {
text(loc.x, loc.y, text, hjust, vjust, rotation, drawingCtx, dev);
}
// or bounds checking
if (!draw) {
if (Double.isFinite(loc.x) && Double.isFinite(loc.y)) {
xmin = fmin(xmin, trect.x);
xmax = fmax(xmax, trect.x);
ymin = fmin(ymin, trect.y);
ymax = fmax(ymax, trect.y);
// Calculate edgex and edgey for case where this is the only rect
edge = EdgeDetection.polygonEdge(trect.x, trect.y, 4, theta);
ntxt++;
}
}
}
if (!draw && ntxt > 0) {
// If there is more than one text, just produce edge based on bounding rect of all text
if (ntxt > 1) {
// Produce edge of rect bounding all text
edge = EdgeDetection.rectEdge(xmin, ymin, xmax, ymax, theta);
}
double scale = GridContext.getContext().getGridState().getScale();
double[] result = new double[4];
result[0] = edge.x / scale;
result[1] = edge.y / scale;
result[2] = (xmax - xmin) / scale;
result[3] = (ymax - ymin) / scale;
return RDataFactory.createDoubleVector(result, RDataFactory.COMPLETE_VECTOR);
}
// NULL is OK result even for bound checking if there was no text actually "drawn", the R
// wrapper deals with NULL in such case. For actual drawing case, we should always return
// NULL
return RNull.instance;
}
// transcribed from utils.c
private EdgeDetection.Rectangle textRect(Point loc, double xadj, double yadj, double rotation, String text, DrawingContext drawingCtx, GridDevice device) {
// TODO: for expressions the h and w are calculated differently
double h = device.getStringHeight(drawingCtx, text);
double w = device.getStringWidth(drawingCtx, text);
double[][] thisJustification = translation(-xadj * w, -yadj * h);
double[][] thisRotation = TransformMatrix.rotation(rotation);
double[][] transform = multiply(multiply(thisJustification, thisRotation), translation(loc.x, loc.y));
Point bl = transLocation(new Point(0, 0), transform);
Point br = transLocation(new Point(w, 0), transform);
Point tr = transLocation(new Point(w, h), transform);
Point tl = transLocation(new Point(0, h), transform);
return new Rectangle(bl, br, tr, tl);
}
// transcribed from engine.c
private void text(double x, double y, String text, double xadjIn, double yadj, double rotation, DrawingContext drawingCtx, GridDevice device) {
if (!Double.isFinite(yadj)) {
throw RInternalError.unimplemented("'exact' vertical centering, see engine.c:1700");
}
double xadj = Double.isFinite(xadjIn) ? xadjIn : 0.5;
double radRotation = Math.toRadians(rotation);
double cosRot = Math.cos(radRotation);
double sinRot = Math.sin(radRotation);
String[] lines = text.split("\n");
for (int lineIdx = 0; lineIdx < lines.length; lineIdx++) {
double xoff;
double yoff;
if (lines.length == 1) {
// simplification for single line
xoff = x;
yoff = y;
} else {
yoff = (1 - yadj) * (lines.length - 1) - lineIdx;
// TODO: in the original the following formula uses "dd->dev->cra[1]"
yoff *= (drawingCtx.getFontSize() * drawingCtx.getLineHeight()) / INCH_TO_POINTS_FACTOR;
xoff = -yoff * sinRot;
yoff = yoff * cosRot;
xoff = x + xoff;
yoff = y + yoff;
}
double xleft = xoff;
double ybottom = yoff;
// now determine bottom-left for THIS line
if (xadj != 0.0 || yadj != 0.0) {
// otherwise simply the initial values for xleft and ybottom are OK
double width = device.getStringWidth(drawingCtx, lines[lineIdx]);
double height = device.getStringHeight(drawingCtx, lines[lineIdx]);
xleft = xoff - (xadj) * width * cosRot + yadj * height * sinRot;
ybottom = yoff - (xadj) * width * sinRot - yadj * height * cosRot;
}
device.drawString(drawingCtx, xleft, ybottom, rotation, lines[lineIdx]);
}
}
}
......@@ -11,6 +11,9 @@
*/
package com.oracle.truffle.r.library.fastrGrid;
import static com.oracle.truffle.r.runtime.nmath.RMath.fmax2;
import static com.oracle.truffle.r.runtime.nmath.RMath.fmin2;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.r.library.fastrGrid.Unit.UnitLengthNode;
import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
......@@ -46,4 +49,22 @@ final class GridUtils {
}
return result;
}
@ExplodeLoop
static double fmax(double firstVal, double... vals) {
double result = firstVal;
for (double val : vals) {
result = fmax2(result, val);
}
return result;
}
@ExplodeLoop
static double fmin(double firstVal, double... vals) {
double result = firstVal;
for (double val : vals) {
result = fmin2(result, val);
}
return result;
}
}
/*
* 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.getDataAtMod;
import static com.oracle.truffle.r.library.fastrGrid.device.DrawingContext.INCH_TO_POINTS_FACTOR;
import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.abstractVectorValue;
import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
import static com.oracle.truffle.r.library.fastrGrid.GridTextNode.addGridTextCasts;
import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalValue;
import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
import com.oracle.truffle.api.dsl.Cached;
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.api.nodes.NodeCost;
import com.oracle.truffle.api.nodes.NodeInfo;
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.RAbstractDoubleVector;
import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
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 Unit.UnitLengthNode}.
*/
@NodeInfo(cost = NodeCost.NONE)
public abstract class LText extends RExternalBuiltinNode.Arg7 {
@Child private Unit.UnitToInchesNode unitToInches = Unit.createToInchesNode();
@Child private Unit.UnitLengthNode unitLength = Unit.createLengthNode();
@Child private GetViewPortTransformNode getViewPortTransform = new GetViewPortTransformNode();
@Child private VPContextFromVPNode vpContextFromVP = new VPContextFromVPNode();
static {
Casts casts = new Casts(LText.class);
// TODO: expressions and maybe other types should have special handling, not only simple
// String coercion
casts.arg(0).asStringVector();
casts.arg(1).mustBe(abstractVectorValue());
casts.arg(2).mustBe(abstractVectorValue());
casts.arg(3).mustBe(numericValue()).asDoubleVector();
casts.arg(4).mustBe(numericValue()).asDoubleVector();
casts.arg(5).mustBe(numericValue()).asDoubleVector();
addGridTextCasts(casts);
casts.arg(6).mustBe(logicalValue()).asLogicalVector().findFirst().map(toBoolean());
}
public static LText create() {
......@@ -56,70 +48,8 @@ public abstract class LText extends RExternalBuiltinNode.Arg7 {
}
@Specialization
Object drawText(RAbstractStringVector text, RAbstractVector x, RAbstractVector y, RAbstractDoubleVector hjust, RAbstractDoubleVector vjust, RAbstractDoubleVector rotation,
Object checkOverlapIgnored) {
if (text.getLength() == 0) {
return RNull.instance;
}
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 = GridUtils.maxLength(unitLength, x, y);
for (int i = 0; i < length; i++) {
Point loc = TransformMatrix.transLocation(Point.fromUnits(unitToInches, x, y, i, conversionCtx), vpTransform.transform);
text(loc.x, loc.y, text.getDataAt(i % text.getLength()), getDataAtMod(hjust, i), getDataAtMod(vjust, i), getDataAtMod(rotation, i), drawingCtx, dev);
}
return RNull.instance;
}
// transcribed from engine.c
private void text(double x, double y, String text, double xadjIn, double yadj, double rotation, DrawingContext drawingCtx, GridDevice device) {
if (!Double.isFinite(yadj)) {
throw new RuntimeException("Not implemented: 'exact' vertical centering, see engine.c:1700");
}
double xadj = Double.isFinite(xadjIn) ? xadjIn : 0.5;
double radRotation = Math.toRadians(rotation);
double cosRot = Math.cos(radRotation);
double sinRot = Math.sin(radRotation);
String[] lines = text.split("\n");
for (int lineIdx = 0; lineIdx < lines.length; lineIdx++) {
double xoff;
double yoff;
if (lines.length == 1) {
// simplification for single line
xoff = x;
yoff = y;
} else {
yoff = (1 - yadj) * (lines.length - 1) - lineIdx;
// TODO: in the original the following formula uses "dd->dev->cra[1]"
yoff *= (drawingCtx.getFontSize() * drawingCtx.getLineHeight()) / INCH_TO_POINTS_FACTOR;
xoff = -yoff * sinRot;
yoff = yoff * cosRot;
xoff = x + xoff;
yoff = y + yoff;
}
double xleft = xoff;
double ybottom = yoff;
// now determine bottom-left for THIS line
if (xadj != 0.0 || yadj != 0.0) {
// otherwise simply the initial values for xleft and ybottom are OK
double width = device.getStringWidth(drawingCtx, lines[lineIdx]);
double height = device.getStringHeight(drawingCtx, lines[lineIdx]);
xleft = xoff - (xadj) * width * cosRot + yadj * height * sinRot;
ybottom = yoff - (xadj) * width * sinRot - yadj * height * cosRot;
}
device.drawString(drawingCtx, xleft, ybottom, rotation, lines[lineIdx]);
}
Object drawText(RAbstractStringVector text, RAbstractVector x, RAbstractVector y, RAbstractDoubleVector hjust, RAbstractDoubleVector vjust, RAbstractDoubleVector rotation, boolean checkOverlap,
@Cached("createDraw()") GridTextNode gridText) {
return gridText.gridText(text, x, y, hjust, vjust, rotation, checkOverlap, 0);
}
}
/*
* 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.library.fastrGrid.GridTextNode.addGridTextCasts;
import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.constant;
import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.missingValue;
import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.nullValue;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.NodeCost;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode;
import com.oracle.truffle.r.runtime.data.model.RAbstractDoubleVector;
import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
@NodeInfo(cost = NodeCost.NONE)
public abstract class LTextBounds extends RExternalBuiltinNode.Arg7 {
static {
Casts casts = new Casts(LTextBounds.class);
addGridTextCasts(casts);
casts.arg(6).returnIf(missingValue().or(nullValue()), constant(0)).asDoubleVector().findFirst();
}
public static LTextBounds create() {
return LTextBoundsNodeGen.create();
}
@Specialization
public Object textBounds(RAbstractStringVector text, RAbstractVector x, RAbstractVector y, RAbstractDoubleVector hjust, RAbstractDoubleVector vjust, RAbstractDoubleVector rotation, double theta,
@Cached("createCalculateBounds()") GridTextNode gridText) {
return gridText.gridText(text, x, y, hjust, vjust, rotation, false, theta);
}
}
......@@ -11,6 +11,8 @@
*/
package com.oracle.truffle.r.library.fastrGrid;
import static com.oracle.truffle.r.runtime.nmath.MathConstants.M_PI;
/**
* Operations on transformation (3x3) matrices.
*/
......@@ -40,6 +42,21 @@ final class TransformMatrix {
return m;
}
static double[][] rotation(double theta) {
double thetarad = theta / 180 * M_PI;
double costheta = Math.cos(thetarad);
double sintheta = Math.sin(thetarad);
double[][] result = identity();
if (theta == 0) {
return result;
}
result[0][0] = costheta;
result[0][1] = sintheta;
result[1][0] = -sintheta;
result[1][1] = costheta;
return result;
}
static double[][] identity() {
double[][] result = new double[3][3];
result[0][0] = 1;
......
......@@ -22,6 +22,7 @@ import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.r.library.fastrGrid.GridState;
import com.oracle.truffle.r.library.fastrGrid.GridStateGetNode;
import com.oracle.truffle.r.library.fastrGrid.GridStateSetNode;
import com.oracle.truffle.r.library.fastrGrid.IgnoredGridExternal;
......@@ -34,6 +35,7 @@ import com.oracle.truffle.r.library.fastrGrid.LNewPage;
import com.oracle.truffle.r.library.fastrGrid.LRect;
import com.oracle.truffle.r.library.fastrGrid.LSegments;
import com.oracle.truffle.r.library.fastrGrid.LText;
import com.oracle.truffle.r.library.fastrGrid.LTextBounds;
import com.oracle.truffle.r.library.fastrGrid.LUpViewPort;
import com.oracle.truffle.r.library.grDevices.DevicesCCalls;
import com.oracle.truffle.r.library.graphics.GraphicsCCalls;
......@@ -695,6 +697,8 @@ public class CallAndExternalFunctions {
return LLines.create();
case "L_text":
return LText.create();
case "L_textBounds":
return LTextBounds.create();
case "L_segments":
return LSegments.create();
case "L_circle":
......@@ -702,15 +706,15 @@ public class CallAndExternalFunctions {
// Simple grid state access
case "L_getGPar":
return new GridStateGetNode(state -> state.getGpar());
return new GridStateGetNode(GridState::getGpar);
case "L_setGPar":
return GridStateSetNode.create((state, val) -> state.setGpar((RList) val));
case "L_getCurrentGrob":
return new GridStateGetNode(state -> state.getCurrentGrob());
return new GridStateGetNode(GridState::getCurrentGrob);
case "L_setCurrentGrob":
return GridStateSetNode.create((state, val) -> state.setCurrentGrob(val));
return GridStateSetNode.create(GridState::setCurrentGrob);
case "L_currentViewport":
return new GridStateGetNode(state -> state.getViewPort());
return new GridStateGetNode(GridState::getViewPort);
// Display list stuff: not implemented atm
case "L_getDisplayList":
......
......@@ -773,7 +773,6 @@ com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LUpViewP
com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LNewPage.java,gnu_r_murrel_core.copyright
com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitGrid.java,gnu_r_murrel_core.copyright
com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LInitViewPortStack.java,gnu_r_murrel_core.copyright
com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LText.java,gnu_r_murrel_core.copyright
com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/GPar.java,gnu_r_murrel_core.copyright
com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/TransformMatrix.java,gnu_r_murrel_core.copyright
com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/LSegments.java,gnu_r_murrel_core.copyright
......@@ -788,3 +787,5 @@ com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/fastrGrid/ViewPort
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/ColorNames.java,gnu_r.copyright
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
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