//
// GDInterface.java
// Java Graphics Device
//
// Created by Simon Urbanek on Thu Aug 05 2004.
// Copyright (c) 2004-2016 Simon Urbanek. All rights reserved.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation;
// version 2.1 of the License.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
package org.rosuda.javaGD;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.lang.reflect.Method;
public class GDContainerGD extends GDInterface {
/** container that will receive all drawing methods. It should be created by subclasses in the {@link #gdOpen} method. */
public GDContainer c=null;
/** synchronization object for locator calls */
public LocatorSync ls=null;
/**
* Opens the new device with the specified size
* @param devNr device number
* @param w width of the device
* @param h height of the device */
@Override
public void gdOpen(int devNr) {
super.gdOpen(devNr);
if (c != null) {
c.setDeviceNumber(devNr);
}
}
/** draw a circle
* @param x x coordinate of the center
* @param y y coordinate of the center
* @param r radius */
@Override
public void gdCircle(double x, double y, double r) {
if (c==null) return;
c.add(new GDCircle(x,y,r));
}
/** clip drawing to the specified region
* @param x0 left coordinate
* @param x1 right coordinate
* @param y0 top coordinate
* @param y1 bottom coordinate */
@Override
public void gdClip(double x0, double x1, double y0, double y1) {
if (c==null) return;
c.add(new GDClip(x0, y0, x1, y1));
}
/** close the display */
@Override
public void gdClose() {
super.gdClose();
if (c!=null) c.closeDisplay();
}
@Override
public void gdFlush(boolean flush) {
super.gdFlush(flush);
if (c != null && !isHolding()) {
c.syncDisplay(true);
}
}
/** invoke the locator
* @return array of indices or <code>null</code> is cancelled */
@Override
public double[] gdLocator() {
if (c==null) return null;
if (ls==null) ls=new LocatorSync();
if (!c.prepareLocator(ls)) return null;
return ls.waitForAction();
}
/** draw a line
* @param x1 x coordinate of the origin
* @param y1 y coordinate of the origin
* @param x2 x coordinate of the end
* @param y2 y coordinate of the end */
public void gdLine(double x1, double y1, double x2, double y2) {
if (c==null) return;
c.add(new GDLine(x1, y1, x2, y2));
}
/** retrieve font metrics info for the given unicode character
* @param ch character (encoding may depend on the font type)
* @return an array consisting for three doubles: ascent, descent and width */
public double[] gdMetricInfo(int ch) {
double[] res=new double[3];
double ascent=0.0, descent=0.0, width=8.0;
if (c!=null) {
Graphics g=c.getGraphics();
if (g!=null) {
Font f=c.getGState().f;
if (f!=null) {
FontMetrics fm=g.getFontMetrics(c.getGState().f);
if (fm!=null) {
ascent=(double) fm.getAscent();
descent=(double) fm.getDescent();
width=(double) fm.charWidth((ch==0)?77:ch);
}
}
}
}
res[0]=ascent; res[1]=descent; res[2]=width;
return res;
}
/** R signalled a mode change
* @param mode mode as signalled by R (currently 0=R stopped drawing, 1=R started drawing, 2=graphical input exists) */
public void gdMode(int mode) {
if (c != null && !isHolding()) c.syncDisplay(mode==0);
}
public void gdNewPage() {
if (c!=null) {
c.reset();
}
}
@Override
public void gdPath(int nPoly, int[] nPer, double[] x, double[] y, int mode) {
if (c==null) return;
c.add(new GDPath(nPer, x, y, mode == 1));
}
@Override
public void gdPolygon(int n, double[] x, double[] y) {
if (c==null) return;
c.add(new GDPolygon(n, x, y, false));
}
@Override
public void gdPolyline(int n, double[] x, double[] y) {
if (c==null) return;
c.add(new GDPolygon(n, x, y, true));
}
@Override
public void gdRect(double x0, double y0, double x1, double y1) {
if (c==null) return;
c.add(new GDRect(x0, y0, x1, y1));
}
/** retrieve the current size of the device
* @return an array of four doubles: 0, width, height, 0 */
@Override
public double[] gdSize() {
double[] res=new double[4];
double width=0d, height=0d;
if (c != null) {
Dimension d = c.getSize();
width = d.getWidth();
height = d.getHeight();
}
res[0]=0d;
res[1]=width - 1;
res[2]=height - 1;
res[3]=0;
return res;
}
/** retrieve width of the given text when drawn in the current font
* @param str text
* @return width of the text */
@Override
public double gdStrWidth(String str) {
double width=(double)(8*str.length()); // rough estimate
if (c!=null) { // if canvas is active, we can do better
Graphics g=c.getGraphics();
if (g!=null) {
Font f=c.getGState().f;
if (f!=null) {
FontMetrics fm=g.getFontMetrics(f);
if (fm!=null) width=(double)fm.stringWidth(str);
}
}
}
return width;
}
/** draw text
* @param x x coordinate of the origin
* @param y y coordinate of the origin
* @param str text to draw
* @param rot rotation (in degrees)
* @param hadj horizontal adjustment with respect to the text size (0=left-aligned wrt origin, 0.5=centered, 1=right-aligned wrt origin) */
@Override
public void gdText(double x, double y, String str, double rot, double hadj) {
if (c==null) return;
c.add(new GDText(x, y, rot, hadj, str));
}
@Override
public void gdRaster(byte[] img, boolean imgAlpha, int img_w, int img_h, double x, double y, double w, double h, double rot, boolean interpolate) {
if (c == null) return;
c.add(new GDRaster(img, img_w, img_h, x, y, w, h, rot, interpolate));
}
@Override
public byte[] gdCap(int[] dim) {
return null;
}
/*-- GDC - manipulation of the current graphics state */
/** set drawing color
* @param cc color */
@Override
public void gdcSetColor(int cc) {
if (c==null) return;
c.add(new GDColor(cc));
}
/** set fill color
* @param cc color */
@Override
public void gdcSetFill(int cc) {
if (c==null) return;
c.add(new GDFill(cc));
}
@Override
public void gdcSetLine(final double lwd, final int lty,
final byte cap, final byte join, final float joinMiterLimit) {
if (c==null) return;
c.add(new GDLinePar(lwd, lty));
}
/** set current font
* @param cex character expansion (see <code>cex</code> parameter in R)
* @param ps point size (see <code>ps</code> parameter in R - for all practical purposes the requested font size in points is <code>cex * ps</code>)
* @param lineheight line height
* @param fontface font face (see <code>font</code> parameter in R: 1=plain, 2=bold, 3=italic, 4=bold-italic, 5=symbol)
* @param fontfamily font family (see <code>family</code> parameter in R) */
@Override
public void gdcSetFont(double cex, double ps, double lineheight, int fontface, String fontfamily) {
if (c==null) return;
GDFont f=new GDFont(cex, ps, lineheight, fontface, fontfamily);
c.add(f);
c.getGState().f=f.getFont();
}
/** close the device in R associted with this instance */
public void executeDevOff() {
if (c==null || c.getDeviceNumber()<0) return;
try { // for now we use no cache - just pure reflection API for: Rengine.getMainEngine().eval("...")
Class cl=Class.forName("org.rosuda.JRI.Rengine");
if (cl==null)
System.out.println(">> can't find Rengine, close function disabled. [c=null]");
else {
Method m=cl.getMethod("getMainEngine",null);
Object o=m.invoke(null,null);
if (o!=null) {
Class[] par=new Class[1];
par[0]=Class.forName("java.lang.String");
m=cl.getMethod("eval",par);
Object[] pars=new Object[1];
pars[0]="try({ dev.set("+(c.getDeviceNumber()+1)+"); dev.off()},silent=TRUE)";
m.invoke(o, pars);
}
}
} catch (Exception e) {
System.out.println(">> can't find Rengine, close function disabled. [x:"+e.getMessage()+"]");
}
}
}