/**
* Copyright (C) DataMelt project. The jHPLot package by S.Chekanov and Work.ORG
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This program 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 for more details.
*
* You should have received a copy of the GNU General Public License along with this program;
* if not, see .
*
* Additional permission under GNU GPL version 3 section 7:
* If you have received this program as a library with written permission from the DataMelt team,
* you can link or combine this library with your non-GPL project to convey the resulting work.
* In this case, this library should be considered as released under the terms of
* GNU Lesser public license (see ),
* provided you include this license notice and a URL through which recipients can access the
* Corresponding Source.
**/
package jhplot;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.rmi.RemoteException;
import javax.swing.*;
import jhplot.gui.HelpBrowser;
import visad.*;
import visad.java2d.*;
import visad.java3d.*;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.util.List;
import java.util.Properties;
import java.util.ArrayList;
import java.util.Iterator;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.JPEGTranscoder;
import org.apache.fop.render.ps.EPSTranscoder;
import org.apache.fop.render.ps.PSTranscoder;
import org.apache.fop.svg.PDFTranscoder;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.freehep.graphicsbase.util.export.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
/**
* Interactive canvas for visualizing and experimenting with a variety of
* mathematical objects in 2D and 3D using the VisAd environment.
*
* @author S.Chekanov
*
*/
public class HVisAd extends JFrame {
private static final long serialVersionUID = 1L;
private DisplayImpl display;
private DisplayRenderer render;
private boolean is3D = true;
private int xsize = 600;
private int ysize = 600;
private GraphicsModeControl dispGMC;
private MouseBehavior mouseBehavior;
private ProjectionControl proj;
private ScalarMap rX,rY,rZ;
private RealType xx,yy,zz;
private Color background=Color.blue;
private Color foreground=Color.gray;
/**
* Create 2D/3D canvas based on VisAd classes.
*
* @param xsize
* size in x direction
* @param ysize
* size in y direction
*
* @param title
* name of this canvas.
* @param is3D
* set to true for 3D canvas. If false, assume 2D.
*/
public HVisAd(String title, int xsize, int ysize, boolean is3D) {
this.is3D = is3D;
this.xsize = xsize;
this.ysize = ysize;
if (is3D) {
try {
display = new DisplayImplJ3D(title);
display.getGraphicsModeControl().setPointSize(4.0f);
DisplayRendererJ3D render = (DisplayRendererJ3D) display.getDisplayRenderer();
KeyboardBehaviorJ3D key = new KeyboardBehaviorJ3D(render);
render.addKeyboardBehavior(key); // use shift and arrows to
dispGMC = (GraphicsModeControl) display.getGraphicsModeControl();
dispGMC.setScaleEnable(true);
// Set the display background color
render.setBackgroundColor(background);
render.setCursorColor(Color.red);
render.setForegroundColor(foreground);
// Set box on or off: you choose
render.setBoxOn(false);
proj = render.getDisplay().getProjectionControl();
mouseBehavior = render.getMouseBehavior();
double[] tstart = proj.getMatrix();
double scale = 0.9;
double[] t1 = mouseBehavior.make_matrix(0.0, 0.0, 0.0, scale, 0.0, 0.0, 0.0);
t1 = mouseBehavior.multiply_matrix(t1, tstart);
proj.setMatrix(t1);
this.render = (DisplayRendererJ3D)render;
} catch (RemoteException | VisADException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
try {
display = new DisplayImplJ2D(title);
DisplayRendererJ2D render = (DisplayRendererJ2D) display.getDisplayRenderer();
KeyboardBehaviorJ2D key = new KeyboardBehaviorJ2D((DisplayRendererJ2D)render);
render.addKeyboardBehavior(key); // use shift and arrows to
dispGMC = (GraphicsModeControl) display.getGraphicsModeControl();
dispGMC.setScaleEnable(true);
// Set the display background color
render.setBackgroundColor(background);
render.setCursorColor(Color.red);
render.setForegroundColor(foreground);
// Set box on or off: you choose
render.setBoxOn(false);
proj = render.getDisplay().getProjectionControl();
double[] tstart = proj.getMatrix();
mouseBehavior = render.getMouseBehavior();
double scale = 0.9;
double[] t1 = mouseBehavior.make_matrix(0.0, 0.0, 0.0, scale, 0.0, 0.0, 0.0);
t1 = mouseBehavior.multiply_matrix(t1, tstart);
proj.setMatrix(t1);
this.render = (DisplayRendererJ2D)render;
} catch (RemoteException | VisADException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
setTitle(title);
JMenuBar bar = new JMenuBar();
JMenu menu = new JMenu("File");
JMenu help = new JMenu("Help");
JMenuItem item1 = new JMenuItem("Export");
item1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
exportDialog();
}
});
JMenuItem item2 = new JMenuItem("Close");
item2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
close();
}
});
JMenuItem item3 = new JMenuItem("Help");
item3.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
help();
}
});
menu.add(item1);
menu.add(item2);
help.add(item3);
bar.add(menu);
bar.add(help);
setJMenuBar(bar);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
getContentPane().add(display.getComponent());
setSize(xsize, ysize);
}
/**
* Create 3D canvas based on VisAd classes.
*
* @param xsize
* size in x direction
* @param ysize
* size in y direction
*
* @param title
* name of this canvas.
*/
public HVisAd(String title, int xsize, int ysize) {
this(title, xsize, ysize, true);
}
/**
* Create 2D/3D canvas based on VisAd classes.
*
* @param title
* name of this canvas.
* @param is3D
* set to true for 3D canvas. If false, assume 2D.
*/
public HVisAd(String title, boolean is3D) {
this(title, 600, 600, is3D);
}
/**
* Get current display.
*
* @return current display
*/
public DisplayImpl getDisplay() {
return display;
}
/**
* Set the scaling of the box.
*
* @param scale
* factor
*/
public void setScaling(double scale) {
proj = render.getDisplay().getProjectionControl();
mouseBehavior = render.getMouseBehavior();
double[] tstart = proj.getMatrix();
double[] t1 = mouseBehavior.make_matrix(0.0, 0.0, 0.0, scale, 0.0, 0.0, 0.0);
t1 = mouseBehavior.multiply_matrix(t1, tstart);
try {
proj.setMatrix(t1);
} catch (VisADException | RemoteException e) {
System.err.println(e.toString());
}
}
/**
* Get the scaling.
*
* @return scaling factor.
*/
public double getScaling() {
proj = render.getDisplay().getProjectionControl();
double[] tstart = proj.getMatrix();
return tstart[2];
}
/**
* Set the rotation angles in degrees.
*
* @param rotX
* rotation in X
* @param rotY
* rotation in Y
* @param rotZ
* rotation in Z
*/
public void setRotation(double rotX, double rotY, double rotZ) {
proj = render.getDisplay().getProjectionControl();
mouseBehavior = render.getMouseBehavior();
double[] tstart = proj.getMatrix();
double[] t1 = mouseBehavior.make_matrix(rotX, rotY, rotZ, 1.0, 0.0, 0.0, 0.0);
t1 = mouseBehavior.multiply_matrix(t1, tstart);
try {
proj.setMatrix(t1);
} catch (VisADException | RemoteException e) {
System.err.println(e.toString());
}
}
/**
* Set the translation (0-1 values)
*
* @param X
* translate in X
* @param Y
* translate in Y
* @param Z
* translate in Z
*/
public void setTranslation(double X, double Y, double Z) {
proj = render.getDisplay().getProjectionControl();
mouseBehavior = render.getMouseBehavior();
double[] tstart = proj.getMatrix();
double[] t1 = mouseBehavior.make_matrix(0, 0, 0, 1.0, X, Y, Z);
t1 = mouseBehavior.multiply_matrix(t1, tstart);
try {
proj.setMatrix(t1);
} catch (VisADException | RemoteException e) {
System.err.println(e.toString());
}
}
/**
* Get the rotation angles in degrees.
*
* @return rotation in X, Y,Z
*/
public double[] getRotation() {
proj = render.getDisplay().getProjectionControl();
mouseBehavior = render.getMouseBehavior();
double[] tstart = proj.getMatrix();
double[] rotA = { tstart[0], tstart[1], tstart[2] };
return rotA;
}
/**
* Get the translation.
*
* @return translation in X, Y,Z
*/
public double[] getTranslation() {
proj = render.getDisplay().getProjectionControl();
mouseBehavior = render.getMouseBehavior();
double[] tstart = proj.getMatrix();
double[] rotA = { tstart[4], tstart[5], tstart[6] };
return rotA;
}
/**
* Get current projection.
*
* @return current projection.
*/
public ProjectionControl getProjection() {
return proj;
}
/**
* Get mouse behaviour.
*
* @return current behaviour of mouse.
*/
public MouseBehavior getMouse() {
return mouseBehavior;
}
/**
* Get control of this display.
*
* @return control
*/
public GraphicsModeControl getControl() {
return dispGMC;
}
/**
* Get render of this display.
*
* @return render class.
*/
public DisplayRenderer getRender() {
return display.getDisplayRenderer();
}
/**
* Construct a 3D canvas with a plot with the default parameters. It assumes
* 600x600 size and 3D display.
*/
public HVisAd() {
this("VisAd", 600, 600, true);
}
/**
* Construct a 3D canvas with a plot with the default parameters. It assumes
* 600x400 size and 3D display.
*
* @param title
* name of this canvas.
*/
public HVisAd(String title) {
this(title, 600, 600, true);
}
/**
* Clear the current graph including graph settings. Note: the current graph
* is set by the cd() method
*/
public void clear() {
// window.dispose();
}
/**
* Close the canvas (and dispose all components) Note: a memory leak is
* found - no time to study it. set to null all the stuff
*/
public void close() {
setVisible(false);
dispose();
}
/**
* Draw 3D data points.
* @param p input array
*/
public void draw(P2D p){
int nn=p.size();
float[][] ntuple= new float[3][nn];
for (int i=0; i setAxes(String nameX, double minX, double maxX, String nameY, double minY, double maxY,
String nameZ, double minZ, double maxZ) {
xx = RealType.getRealType("x",null,null);
yy = RealType.getRealType("y",null,null);
zz = RealType.getRealType("z",null,null);
float[] col = { 0.1f, 0.1f, 0.1f }; // color for axis
Font ff = new Font("Arial", Font.PLAIN, 16);
try {
rX = new ScalarMap(xx, Display.XAxis);
rY = new ScalarMap(yy, Display.YAxis);
rZ = new ScalarMap(zz, Display.ZAxis);
rX.setScaleColor(col);
rY.setScaleColor(col);
rZ.setScaleColor(col);
try {
if (maxX>minX) rX.setRange(minX, maxX);
if (maxY>minY) rY.setRange(minY, maxY);
if (maxZ>minZ) rZ.setRange(minZ, maxZ);
AxisScale axisX = rX.getAxisScale();
axisX.setFont(ff);
axisX.setLabelSize(16);
axisX.setSnapToBox(true);
axisX.setColor(foreground);
AxisScale axisY = rY.getAxisScale();
axisY.setFont(ff);
axisY.setLabelSize(16);
axisY.setSnapToBox(true);
axisY.setColor(foreground);
AxisScale axisZ = rZ.getAxisScale();
axisZ.setFont(ff);
axisZ.setLabelSize(16);
axisZ.setSnapToBox(true);
axisZ.setColor(foreground);
//display.addMap(rX);
//display.addMap(rY);
//display.addMap(rZ);
ArrayList xyz = new ArrayList();
xyz.add(axisX);
xyz.add(axisY);
xyz.add(axisZ);
return xyz;
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (VisADException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/**
* Set the canvas frame visible or not
*
* @param vs
* (boolean) true: visible, false: not visible
*/
public void visible(boolean vs) {
// updateAll();
if (vs == false) {
setVisible(false);
} else {
setSize(xsize, ysize);
setVisible(true);
}
}
/**
* Update the canvas
*
*/
public void update() {
display.reDisplayAll();
validate();
}
/**
* Show the canvas
*/
public void visible() {
visible(true);
}
/**
* Show online documentation.
*/
public void doc() {
String a = this.getClass().getName();
a = a.replace(".", "/") + ".html";
new HelpBrowser(HelpBrowser.JHPLOT_HTTP + a);
}
/**
*
* Export graph into an image file. The the image format is given by
* extension. "png", "jpg", "eps", "pdf", "svg". In case of "eps", "pdf" and
* "svg", vector graphics is used.
*
* @param file
* name of the image
*/
public void export(String filename) {
int calculatedWidth = (int) xsize;
int calculatedHeight = (int) ysize;
if (calculatedWidth <= 0)
calculatedWidth = 600;
if (calculatedHeight <= 0)
calculatedHeight = 400;
this.export(filename, calculatedWidth, calculatedHeight);
}
/**
* Export graph into an image file. The the image format is given by
* extension. "png", "jpg", "eps", "pdf", "svg". In case of "eps", "pdf" and
* "svg", vector graphics is used.
*
* @param filename
* file name
* @param width
* width
* @param height
* hight
*/
public void export(String filename, int width, int height) {
File f = new File(filename);
ExportFileType t = null;
int dot = filename.lastIndexOf('.');
String base = (dot == -1) ? filename : filename.substring(0, dot);
String fext = (dot == -1) ? "" : filename.substring(dot + 1);
fext = fext.trim();
List list = ExportFileType.getExportFileTypes();
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
t = (ExportFileType) iterator.next();
String[] ext = t.getExtensions();
if (fext.equalsIgnoreCase(ext[0]))
break; // not svgz
// overwrite this since we use svn conversions
if (ext[0].equalsIgnoreCase("svg"))
break;
if (fext.equalsIgnoreCase(ext[0]))
break; // not any of the above
t = null;
}
String rootKey = "";
final String SAVE_AS_TYPE = rootKey + ".SaveAsType";
final String SAVE_AS_FILE = rootKey + ".SaveAsFile";
final Properties props = new Properties();
String a = t.getDescription();
props.put(SAVE_AS_FILE, filename);
props.put(SAVE_AS_TYPE, a);
final String metadata = "(C) DataMelt (https://datamelt.org/). S.Chekanov";
String fname = filename;
String filetype = "pdf";
int i = filename.lastIndexOf('.');
if (i > 0) {
filetype = fname.substring(i + 1);
}
java.awt.Component cp = display.getComponent();
try {
if (filetype.equalsIgnoreCase("png")) {
DisplayRenderer ren = display.getDisplayRenderer();
BufferedImage b = ren.getImage();
ImageIO.write(b, "png", new File(fname));
} else if (filetype.equalsIgnoreCase("jpg") || filetype.equalsIgnoreCase("jpeg")) {
DisplayRenderer ren = display.getDisplayRenderer();
BufferedImage b = ren.getImage();
ImageIO.write(b, "jpg", new File(fname));
} else if (filetype.equalsIgnoreCase("eps")) {
/*
* ImageType currentImageType = ImageType.EPS; Rectangle r = new
* Rectangle (0, 0, width, height); Export.exportComponent(cp,
* r, new File(filename), currentImageType); } catch
* (IOException e) { e.printStackTrace(); } catch
* (org.apache.batik.transcoder.TranscoderException e) {
* e.printStackTrace(); }
*
*/
ByteArrayOutputStream bytes2 = new ByteArrayOutputStream();
t.exportToFile(bytes2, (Component) cp, (Component) cp, props, metadata);
bytes2.flush();
byte[] bb = bytes2.toByteArray();
bytes2.close();
if (bb == null)
return;
if (bb.length < 1)
return;
TranscoderInput input = new TranscoderInput(new ByteArrayInputStream(bb));
FileOutputStream os = new FileOutputStream(filename);
EPSTranscoder trans = new EPSTranscoder();
TranscoderOutput output = new TranscoderOutput(os);
trans.transcode(input, output);
os.flush();
os.close();
} else if (filetype.equalsIgnoreCase("ps")) {
JOptionPane.showMessageDialog(this, "PS format not supported");
/*
* try { ImageType currentImageType = ImageType.PS; Rectangle r
* = new Rectangle(0, 0, width, height);
* Export.exportComponent(cp, r, new File( filename),
* currentImageType); } catch (IOException e) {
* e.printStackTrace(); } catch
* (org.apache.batik.transcoder.TranscoderException e) {
* e.printStackTrace(); }
*/
} else if (filetype.equalsIgnoreCase("pdf")) {
JOptionPane.showMessageDialog(this, "PDF format not supported");
/*
*
*
* try {
*
* ByteArrayOutputStream bytes2 = new ByteArrayOutputStream();
* t.exportToFile(bytes2, cp, cp, props, metadata);
* bytes2.flush(); byte[] bb=bytes2.toByteArray();
* bytes2.close(); if (bb==null) return; if (bb.length<1)
* return;
*
* TranscoderInput input = new TranscoderInput( new
* ByteArrayInputStream(bb)); FileOutputStream os = new
* FileOutputStream(filename); EPSTranscoder trans = new
* EPSTranscoder(); TranscoderOutput output = new
* TranscoderOutput(os); trans.transcode(input, output);
* os.flush(); os.close();
*
* return; } catch (IOException e) { System.err.
* println("DataMelt ExportVGraphics: IOException: Error while converting "
* + e.getLocalizedMessage()); } catch (TranscoderException e) {
* // System.err.
* println("DataMelt ExportVGraphics: Error while converting "
* // + e.getLocalizedMessage()); }
*
*
*
*/
} else if (filetype.equalsIgnoreCase("svg")) {
JOptionPane.showMessageDialog(this, "SVG format not supported");
}
} catch (IOException | TranscoderException e) {
e.printStackTrace();
}
}
/**
* Exports the image to some graphic format.
*/
protected void exportImage() {
final JFileChooser fileChooser = new JFileChooser();
fileChooser.removeChoosableFileFilter(fileChooser.getAcceptAllFileFilter());
// fileChooser.setCurrentDirectory(new
// File(System.getProperty("user.home")));
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
// fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("PDF
// Documents (*.pdf)", "pdf"));
// fileChooser.addChoosableFileFilter(new
// FileNameExtensionFilter("Encapsulated PostScript (*.eps)", "eps"));
// fileChooser.addChoosableFileFilter(new
// FileNameExtensionFilter("PostScript (*.ps)", "ps"));
// fileChooser.addChoosableFileFilter(new
// FileNameExtensionFilter("Scalable Vector Graphics (*.svg)", "svg"));
// fileChooser.addChoosableFileFilter(new
// FileNameExtensionFilter("Compressed SVG (*.svgz)", "svgz"));
fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("Portable Network Graphics (*.png)", "png"));
fileChooser.addChoosableFileFilter(
new FileNameExtensionFilter("JPEG Raster Images (*.jpg, *.jpeg)", "jpg", "jpeg"));
fileChooser.setAcceptAllFileFilterUsed(true);
final File sFile = new File("dmelt.png");
fileChooser.setSelectedFile(sFile);
fileChooser.addPropertyChangeListener(JFileChooser.FILE_FILTER_CHANGED_PROPERTY, new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
Object o = evt.getNewValue();
if (o instanceof FileNameExtensionFilter) {
FileNameExtensionFilter filter = (FileNameExtensionFilter) o;
String ex = filter.getExtensions()[0];
File selectedFile = fileChooser.getSelectedFile();
if (selectedFile == null) {
selectedFile = sFile;
}
String path = selectedFile.getName();
path = path.substring(0, path.lastIndexOf("."));
fileChooser.setSelectedFile(new File(path + "." + ex));
}
}
});
// JFileChooser fileChooser = jhplot.gui.CommonGUI
// .openImageFileChooser(frame);
if (fileChooser.showDialog(this, "Save As") == 0) {
final File scriptFile = fileChooser.getSelectedFile();
if (scriptFile == null)
return;
else if (scriptFile.exists()) {
int res = JOptionPane.showConfirmDialog(this, "The file exists. Do you want to overwrite the file?", "",
JOptionPane.YES_NO_OPTION);
if (res == JOptionPane.NO_OPTION)
return;
}
String mess = "write image file ..";
JHPlot.showStatusBarText(mess);
Thread t = new Thread(mess) {
public void run() {
export(scriptFile.getAbsolutePath());
};
};
t.start();
}
}
/**
* Exports the image to some graphic format.
*/
private void exportDialog() {
final JFileChooser fileChooser = new JFileChooser();
fileChooser.removeChoosableFileFilter(fileChooser.getAcceptAllFileFilter());
// fileChooser.setCurrentDirectory(new
// File(System.getProperty("user.home")));
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
// fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("PDF
// Documents (*.pdf)", "pdf"));
// fileChooser.addChoosableFileFilter(new
// FileNameExtensionFilter("Encapsulated PostScript (*.eps)", "eps"));
// fileChooser.addChoosableFileFilter(new
// FileNameExtensionFilter("PostScript (*.ps)", "ps"));
// fileChooser.addChoosableFileFilter(new
// FileNameExtensionFilter("Scalable Vector Graphics (*.svg)", "svg"));
// fileChooser.addChoosableFileFilter(new
// FileNameExtensionFilter("Compressed SVG (*.svgz)", "svgz"));
fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("Portable Network Graphics (*.png)", "png"));
fileChooser.addChoosableFileFilter(
new FileNameExtensionFilter("JPEG Raster Images (*.jpg, *.jpeg)", "jpg", "jpeg"));
fileChooser.setAcceptAllFileFilterUsed(true);
final File sFile = new File("dmelt.png");
fileChooser.setSelectedFile(sFile);
fileChooser.addPropertyChangeListener(JFileChooser.FILE_FILTER_CHANGED_PROPERTY, new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
Object o = evt.getNewValue();
if (o instanceof FileNameExtensionFilter) {
FileNameExtensionFilter filter = (FileNameExtensionFilter) o;
String ex = filter.getExtensions()[0];
File selectedFile = fileChooser.getSelectedFile();
if (selectedFile == null) {
selectedFile = sFile;
}
String path = selectedFile.getName();
path = path.substring(0, path.lastIndexOf("."));
fileChooser.setSelectedFile(new File(path + "." + ex));
}
}
});
if (fileChooser.showDialog(this, "Export to ") == 0) {
// final File scriptFile = fileChooser.getSelectedFile();
final File scriptFile = jhplot.io.images.ExportVGraphics.getSelectedFileWithExtension(fileChooser);
// System.out.println(scriptFile.getAbsolutePath());
if (scriptFile == null)
return;
else if (scriptFile.exists()) {
int res = JOptionPane.showConfirmDialog(this, "The file exists. Do you want to overwrite the file?", "",
JOptionPane.YES_NO_OPTION);
if (res == JOptionPane.NO_OPTION)
return;
}
String mess = "write image file ..";
Thread t = new Thread(mess) {
public void run() {
export(scriptFile.getAbsolutePath());
};
};
t.start();
}
}
public void help() {
String shelp = "Help on 2D display of HVisAd";
if (is3D)
shelp = "Help on 3D display of HVisAd";
String st = "" + shelp
+ "
Central mouse button: Show coordinatesArrow keys: rotate up, down, left, right
Zoom in: Shift + Up arrow
Zoom out: Shift + Down arrow
Rotate: Shift + Left arrow (Right arrow)
Translate: Ctrl + Arrow keys - translate up, down, left, right
Reset: Ctrl + R key - reset to original projection ";
JOptionPane.showMessageDialog(this, st);
}
// end
}