What is the best OpenGL java binding? [closed] - java

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
I am trying to achieve better performance for my Java SWT application, and I just found out it is possible to use OpenGL in SWT. It seems there are more than one Java binding for OpenGL. Which one do you prefer?
Note that I have never used OpenGL before, and that the application needs to work on Windows, Linux and Mac OS X.

I'd suggest checking out LWJGL, the LightWeight Java Game Library. It's got OpenGL bindings, but it also has OpenAL bindings and some great tutorials to get you started.
Just keep in mind that Swing/SWT and OpenGL are generally used for entirely different things. You may end up wanting to use a combination of both. Just try LWJGL out and see how well it fits with what you're doing.

JOGL
My reasons can be quoted off the previously linked site:
JOGL provides full access to the APIs in the OpenGL 2.0 specification as well as nearly all vendor extensions, and integrates with the AWT and Swing widget sets.
Also if you want to have some fun learning and poking around, Processing is an excellent way to start (Processing also uses JOGL btw...)

JOGL is probably the only option worth considering.
Notice that there are at least two options for integrating it into an SWT application. There's a GLCanvas that belongs to SWT and a GLCanvas that belongs to AWT.
The one in SWT is not feature complete and is not really maintained. It's much better to use the AWT GLCanvas inside a SWT_AWT container.
Some code from a recent project:
import org.eclipse.swt.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;
import javax.media.opengl.*;
import javax.media.opengl.glu.*;
import org.eclipse.swt.awt.SWT_AWT;
import org.eclipse.swt.events.*;
public class Main implements GLEventListener
{
public static void main(String[] args)
{
Display display = new Display();
Main main = new Main();
main.runMain(display);
display.dispose();
}
void runMain(Display display)
{
final Shell shell = new Shell(display);
shell.setText("Q*bert 3D - OpenGL Exercise");
GridLayout gridLayout = new GridLayout();
gridLayout.marginHeight = 0;
gridLayout.marginWidth = 0;
shell.setLayout(gridLayout);
// this allows us to set particular properties for the GLCanvas
GLCapabilities glCapabilities = new GLCapabilities();
glCapabilities.setDoubleBuffered(true);
glCapabilities.setHardwareAccelerated(true);
// instantiate the canvas
final GLCanvas canvas = new GLCanvas(glCapabilities);
// we can't use the default Composite because using the AWT bridge
// requires that it have the property of SWT.EMBEDDED
Composite composite = new Composite(shell, SWT.EMBEDDED);
GridData ld = new GridData(GridData.FILL_BOTH);
composite.setLayoutData(ld);
// set the internal layout so our canvas fills the whole control
FillLayout clayout = new FillLayout();
composite.setLayout(clayout);
// create the special frame bridge to AWT
java.awt.Frame glFrame = SWT_AWT.new_Frame(composite);
// we need the listener so we get the GL events
canvas.addGLEventListener(this);
// finally, add our canvas as a child of the frame
glFrame.add(canvas);
// show it all
shell.open();
// the event loop.
while (!shell.isDisposed ()) {
if (!display.readAndDispatch ()) display.sleep ();
}
}

JOGL will give you best performance and portability. But be aware that learning JOGL, which is essentially the same as learning OpenGL, is not easy.

Personally, I'm not even aware of Java bindings for OpenGL other than JOGL -- I think JOGL is pretty much the standard for Java OpenGL.
It works in Windows, Linux, and OS X, but you might want to read over the official documentation for some notes about specific issues in each platform.
Keep in mind that the OpenGL paradigm is quite different from Swing/AWT or the Java 2D API; OpenGL is not a drop-in replacement for Swing.

We've had lots of luck at work using JOGL. The new 2.0 version is at http://jogamp.org/ (the last "old" version is at http://download.java.net/media/jogl/builds/archive/jsr-231-1.1.1a/).
For JOGL 2 with SWT specifically, I've got a series of tutorials starting at http://wadeawalker.wordpress.com/2010/10/09/tutorial-a-cross-platform-workbench-program-using-java-opengl-and-eclipse/ that demonstrates exactly how to make cross-platform JOGL SWT applications, complete with installable native binaries.
Or if you don't want to use Eclipse RCP, here's an even simpler example that just draws one triangle with JOGL 2 and SWT. To build it, put it in a project with swt.jar (from http://www.eclipse.org/swt/) and the latest JOGL autobuild .jar and .dll files (from http://jogamp.org/). The only problem with this simple example is that it won't be cross-platform without some extra help -- you need the ability that Eclipse RCP gives you to bundle multiple sets of platform libraries together into one project.
package name.wadewalker.onetriangle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.opengl.GLCanvas;
import org.eclipse.swt.opengl.GLData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import javax.media.opengl.GL;
import javax.media.opengl.GLProfile;
import javax.media.opengl.GL2;
import javax.media.opengl.GLContext;
import javax.media.opengl.GLDrawableFactory;
import javax.media.opengl.glu.GLU;
public class OneTriangle {
public static void main(String [] args) {
GLProfile.initSingleton( true );
GLProfile glprofile = GLProfile.get( GLProfile.GL2 );
Display display = new Display();
Shell shell = new Shell( display );
shell.setLayout( new FillLayout() );
Composite composite = new Composite( shell, SWT.NONE );
composite.setLayout( new FillLayout() );
GLData gldata = new GLData();
gldata.doubleBuffer = true;
// need SWT.NO_BACKGROUND to prevent SWT from clearing the window
// at the wrong times (we use glClear for this instead)
final GLCanvas glcanvas = new GLCanvas( composite, SWT.NO_BACKGROUND, gldata );
glcanvas.setCurrent();
final GLContext glcontext = GLDrawableFactory.getFactory( glprofile ).createExternalGLContext();
// fix the viewport when the user resizes the window
glcanvas.addListener( SWT.Resize, new Listener() {
public void handleEvent(Event event) {
setup( glcanvas, glcontext );
}
});
// draw the triangle when the OS tells us that any part of the window needs drawing
glcanvas.addPaintListener( new PaintListener() {
public void paintControl( PaintEvent paintevent ) {
render( glcanvas, glcontext );
}
});
shell.setText( "OneTriangle" );
shell.setSize( 640, 480 );
shell.open();
while( !shell.isDisposed() ) {
if( !display.readAndDispatch() )
display.sleep();
}
glcanvas.dispose();
display.dispose();
}
private static void setup( GLCanvas glcanvas, GLContext glcontext ) {
Rectangle rectangle = glcanvas.getClientArea();
glcanvas.setCurrent();
glcontext.makeCurrent();
GL2 gl = glcontext.getGL().getGL2();
gl.glMatrixMode( GL2.GL_PROJECTION );
gl.glLoadIdentity();
// coordinate system origin at lower left with width and height same as the window
GLU glu = new GLU();
glu.gluOrtho2D( 0.0f, rectangle.width, 0.0f, rectangle.height );
gl.glMatrixMode( GL2.GL_MODELVIEW );
gl.glLoadIdentity();
gl.glViewport( 0, 0, rectangle.width, rectangle.height );
glcontext.release();
}
private static void render( GLCanvas glcanvas, GLContext glcontext ) {
Rectangle rectangle = glcanvas.getClientArea();
glcanvas.setCurrent();
glcontext.makeCurrent();
GL2 gl = glcontext.getGL().getGL2();
gl.glClear( GL.GL_COLOR_BUFFER_BIT );
// draw a triangle filling the window
gl.glLoadIdentity();
gl.glBegin( GL.GL_TRIANGLES );
gl.glColor3f( 1, 0, 0 );
gl.glVertex2f( 0, 0 );
gl.glColor3f( 0, 1, 0 );
gl.glVertex2f( rectangle.width, 0 );
gl.glColor3f( 0, 0, 1 );
gl.glVertex2f( rectangle.width / 2, rectangle.height );
gl.glEnd();
glcanvas.swapBuffers();
glcontext.release();
}
}

Related

getAWTGraphicsConfiguration returns null error

I've been tasked with programming a camera in a 3D environment as a part of my CS course.
The purpose is to show how basic camera view transformations are calculated and displayed.
I've been trying to do it in javafx first, but it seems to me too 'high-level' for showing basic calculations.
Now I'm trying to make below java 3d code work on my machine (copied from here), just to see how it works. I split it in two classes as suggested by IDE:
import com.sun.j3d.utils.universe.SimpleUniverse;
import javax.media.j3d.Appearance;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.LineStripArray;
import javax.media.j3d.Shape3D;
import javax.vecmath.Point3d;
public class tuval7 {
public tuval7() {
SimpleUniverse u = new SimpleUniverse();
BranchGroup group = new BranchGroup();
Point3d coords[] = new Point3d[4];
Appearance app = new Appearance();
coords[0] = new Point3d(-0.5d, -0.2d, 0.1d);
coords[1] = new Point3d(-0.2d, 0.1d, 0.0d);
coords[2] = new Point3d(0.2d, -0.3d, 0.1d);
coords[3] = new Point3d(0.3d, 0.5d, 0.10d);
int vertexCounts[] = {4};
LineStripArray lines = new LineStripArray(4,
GeometryArray.COORDINATES, vertexCounts);
lines.setCoordinates(0, coords);
Shape3D shape = new Shape3D(lines, app);
group.addChild(shape);
u.addBranchGraph(group);
u.getViewingPlatform().setNominalViewingTransform();
}
}
and
public class tuval1 {
public static void main(String[] args) {
new tuval7();
}
}
Initially I have installed old Java 3D from Oracle website, but after running into an issue with
incorrect bitness , I have added JARs to my library as per https://gouessej.wordpress.com/2012/08/01/java-3d-est-de-retour-java-3d-is-back/#netbeans, i.e. j3dcore.jar, j3dutils.jar, vecmath.jar, jogamp-fat.jar .
When run, I'm getting an NPE at a 'SimpleUniverse' line, that I don't know how to solve on my own:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.jogamp.nativewindow.awt.AWTGraphicsConfiguration.getAWTGraphicsConfiguration()" because "<local3>" is null
at javax.media.j3d.JoglPipeline.getGraphicsConfig(JoglPipeline.java:8291)
at javax.media.j3d.Canvas3D.getGraphicsConfig(Canvas3D.java:958)
at javax.media.j3d.Canvas3D.<init>(Canvas3D.java:1028)
at javax.media.j3d.Canvas3D.<init>(Canvas3D.java:990)
at com.sun.j3d.utils.universe.Viewer.<init>(Viewer.java:205)
at com.sun.j3d.utils.universe.Viewer.<init>(Viewer.java:151)
at com.sun.j3d.utils.universe.SimpleUniverse.<init>(SimpleUniverse.java:249)
at com.sun.j3d.utils.universe.SimpleUniverse.<init>(SimpleUniverse.java:211)
at com.sun.j3d.utils.universe.SimpleUniverse.<init>(SimpleUniverse.java:96)
at tuval7.<init>(tuval7.java:12)
at tuval1.main(tuval1.java:4)
Any hint on what <local3>is or what is the problem and how to solve that?
I'm using jdk-16.0.1 with NetBeans 12.4 on Windows 10 Pro Version 10.0.19042.
I would be thankful for:
A suggestion what Java libraries/extensions would be the simplest to use to program basic 3D camera behaviour. The scenery does not need to be complicated, e.g. just edges (lines) of a few rectangular cuboids would do. The point is to be able to show how camera view changes when rotating camera left/right or up/down or applying basic translations.
A solution to the NPE I'm getting, so that at least I could play a bit with these libraries.

How can I stop JLabel scaling an image?

I have a Swing app that can display an image by loading it into a JLabel. Up to Java 8, this worked fine. Since Java 9 there are changes intended to make apps resize on higher pixel densities, including rescaling images: when pixel density is high, images are scaled up. The results are ugly in many cases. As an example, if I have a photo with a high pixel count, my app scales it down to fit the available space, but then it's automatically scaled back up to allow for the higher pixel density - ugly.
A couple of options I can see for avoiding this problem are:
Implementing the interface java.awt.image.MultiResolutionImage which looks like it should help, but I don't see how to use that with JLabel.
Maybe there's a way to fool Swing into thinking that no pixel scaling is necessary, but I can't find anything like that
The following code is a simple example of displaying an image in a label. In Java 8, the image is displayed pixel-for-pixel. In Java 9+, the whole UI including the image is scaled up according to what your OS thinks is your pixel density. You should be able to set this scaling explicitly - Windows 10 has it under "Display", "Scale and Layout". Mine is set to 125% which seems to be a perfect amount to make almost any image look ugly.
import java.awt.*;
import javax.imageio.*;
import javax.swing.*;
import java.io.*;
public class Img extends JFrame {
public static void main( String[] args ) throws IOException {
new Img( new File( args[0] ) );
}
private Img( File file ) throws IOException {
super( "Img: " + file.getName () );
ImageIcon img = new ImageIcon( ImageIO.read( file ) );
JLabel label = new JLabel( img );
label.setPreferredSize( new Dimension( img.getIconWidth(), img.getIconHeight() ) );
add( label );
setDefaultCloseOperation( EXIT_ON_CLOSE );
pack();
setVisible( true );
}
}

Are jfreechart-fx 1.0.1 charts interactable with or just an image built and presented by fxgraphics2d?

I am currently trying to figure out, howjfreechart's splitting into swing(1.5) and JavaFX (1.0.1) affects the JavaFX part. As far as I (very limited knowledge on this topic) understand The jfree-fx uses fxgraphics2d to draw its original swing components(?) into an FX-canvas to add this into JavaFX nodes.
Now my question is, if that fxgraphics2d object is still interactable with? I mean things like tooltips and scrolling and similar stuff normal jfreecharts offer. Since my knowledge and time is somewhat limited, I would want to know, if it is worth digging deeper into jfree-fx (if those charts are still interactable with) or if those charts are solely pictures of the actual chart and not interactable with. Then I would need to find a better solution.
I am currently learning how to build a candlestick chart within my java application. While I managed to build a chart only using JavaFX, its performance was really bad as soon as there were drawn some hundred candlesticks.
Then I came across jfreechart, of which I read, that its performance is well above the internal JavaFX charting possibilities. So today I managed to construct my first chart with jfreechart-fx and performance is quite ok. Further I find it much more intuitive to build those charts... but I am not sure if the jfree-fx version only prints images or real chart objects to the nodes. (I read somewhere something about converting a chart into an image to increase performance of drawing...)
Thank you for any information on that topic.
For example here is my JFreeChart Class, which is drawn correctly, but I just do not get any interaction with the chart with my mouse. E.g. I'd like to zoom in/out using the mousewheel and I'd like to pan the chart to the left/right by clickhold leftmouse. That why I am concerned that I am only looking at an image right now. All viable solutions I find through google seem to address only JFreeChart and not JFreeChart-FX.
package org.ezstrats.jfreeChart;
import javafx.collections.ObservableList;
import org.ezstrats.model.chartData.Candlestick;
import org.ezstrats.model.chartData.Chart;
import org.ezstrats.model.chartData.Exchange;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.labels.HighLowItemLabelGenerator;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.CandlestickRenderer;
import org.jfree.chart.renderer.xy.XYBarRenderer;
import org.jfree.data.time.FixedMillisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.time.ohlc.OHLCSeries;
import org.jfree.data.time.ohlc.OHLCSeriesCollection;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
public class JFreeCandlestickChart extends JPanel {
private static final DateFormat READABLE_TIME_FORMAT = new SimpleDateFormat("kk:mm:ss");
private OHLCSeries ohlcSeries;
private TimeSeries volumeSeries;
private JFreeChart candlestickChart;
public JFreeCandlestickChart(String title) {
ObservableList<Candlestick> candlesticks = Exchange.getCandlesticks();
// Create new chart
candlestickChart = createChart(title, candlesticks);
// Create new chart panel
final ChartPanel chartPanel = new ChartPanel(candlestickChart);
chartPanel.setPreferredSize(new Dimension(832, 468));
chartPanel.getChart().getXYPlot().getDomainAxis().setAutoRange(false);
chartPanel.getChart().getXYPlot().getDomainAxis().setLowerBound(candlesticks.get(candlesticks.size() - 300).getTimestampOpen());
chartPanel.getChart().getXYPlot().getDomainAxis().setUpperBound(candlesticks.get(candlesticks.size() - 1).getTimestampOpen());
// Enable zooming - not workign?! ...
chartPanel.setMouseZoomable(true);
chartPanel.setMouseWheelEnabled(true);
chartPanel.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
// process before
super.mouseDragged(e);
chartPanel.getChart().getXYPlot().getDomainAxis().configure();
// process after
}
});
add(chartPanel, BorderLayout.CENTER);
}
public JFreeChart createChart(String title, ObservableList<Candlestick> candlesticks){
/**
* 1st:
* Creating candlestick subplot
*/
// Create OHLCSeriesCollection as a price dataset for candlestick chart
OHLCSeriesCollection candlestickDataset = new OHLCSeriesCollection();
ohlcSeries = new OHLCSeries("Price");
candlestickDataset.addSeries(ohlcSeries);
// Create candlestick chart priceAxis
NumberAxis priceAxis = new NumberAxis("Price");
priceAxis.setAutoRangeIncludesZero(false);
// Create candlestick chart renderer
CandlestickRenderer candlestickRenderer = new CandlestickRenderer(CandlestickRenderer.WIDTHMETHOD_AVERAGE,
false,
new HighLowItemLabelGenerator(new SimpleDateFormat("kk:mm"), new DecimalFormat("0.00000000")));
// Create candlestickSubplot
XYPlot candlestickSubplot = new XYPlot(candlestickDataset, null, priceAxis, candlestickRenderer);
candlestickSubplot.setBackgroundPaint(Color.white);
/**
* 2nd:
* Creating volume subplot
*/
// creates TimeSeriesCollection as a volume dataset for volume chart
TimeSeriesCollection volumeDataset = new TimeSeriesCollection();
volumeSeries = new TimeSeries("Volume");
volumeDataset.addSeries(volumeSeries);
// Create volume chart volumeAxis
NumberAxis volumeAxis = new NumberAxis("Volume");
volumeAxis.setAutoRangeIncludesZero(true);
// Set to no decimal
volumeAxis.setNumberFormatOverride(new DecimalFormat("0"));
// Create volume chart renderer
XYBarRenderer timeRenderer = new XYBarRenderer();
timeRenderer.setShadowVisible(false);
timeRenderer.setDefaultToolTipGenerator(new StandardXYToolTipGenerator("Volume--> Time={1} Size={2}",
new SimpleDateFormat("kk:mm"), new DecimalFormat("0")));
// Create volumeSubplot
XYPlot volumeSubplot = new XYPlot(volumeDataset, null, volumeAxis, timeRenderer);
volumeSubplot.setBackgroundPaint(Color.white);
/**
* 3rd:
* Adding Candles to this chart
**/
for (Candlestick candle: candlesticks){
addCandleToChart(candle.getTimestampOpen(),
candle.getPriceOpen(),
candle.getPriceHigh(),
candle.getPriceLow(),
candle.getPriceClose(),
candle.getVolumeQuote());
}
/**
* 4th:
* Create chart main plot with two subplots (candlestickSubplot,
* volumeSubplot) and one common dateAxis
*/
// Creating charts common dateAxis
DateAxis dateAxis = new DateAxis("Time");
dateAxis.setDateFormatOverride(new SimpleDateFormat("dd.mm.yy kk:mm"));
//dateAxis.setRange();
// reduce the default left/right margin from 0.05 to 0.02
dateAxis.setLowerMargin(0.02);
dateAxis.setUpperMargin(0.02);
dateAxis.setLabelAngle(0);
// Create mainPlot
CombinedDomainXYPlot mainPlot = new CombinedDomainXYPlot(dateAxis);
mainPlot.setGap(10.0);
mainPlot.add(candlestickSubplot, 4);
mainPlot.add(volumeSubplot, 1);
mainPlot.setOrientation(PlotOrientation.VERTICAL);
mainPlot.setDomainPannable(true);
JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, mainPlot, false);
//chart.removeLegend();
// Einbetten in JScrollPaenl??? um Scrollen zu ermöglichen...
// ChartPanel chartPanel = new ChartPanel(chart);
return chart;
}
/**
* Fill series with data.
*
* #param c opentime
* #param o openprice
* #param h highprice
* #param l lowprice
* #param c closeprice
* #param v volume
*/
private void addCandleToChart(long time, double o, double h, double l, double c, double v) {
// Add bar to the data. Let's repeat the same bar
FixedMillisecond t = new FixedMillisecond(time);
//READABLE_TIME_FORMAT.parse(String.valueOf(time)));
ohlcSeries.add(t, o, h, l, c);
volumeSeries.add(t, v);
}
public void setOhlcSeries(OHLCSeries ohlcSeries) {
this.ohlcSeries = ohlcSeries;
}
public void setVolumeSeries(TimeSeries volumeSeries) {
this.volumeSeries = volumeSeries;
}
public OHLCSeries getOhlcSeries() {
return ohlcSeries;
}
public TimeSeries getVolumeSeries() {
return volumeSeries;
}
public JFreeChart getCandlestickChart() {
return candlestickChart;
}
}
I haven't looked at jFreeChart in detail, but I think the main difference between it and the in-built JavaFX charting API is that jFreeChart uses a canvas for its implementation, whereas the in-built charts use the scene graph. Roughly, though not exactly, its similar to this definition of a retained mode (scene graph) vs immediate mode (canvas).
It's probably possible to add interactivity to canvas rendered graphics. It is likely technically challenging beyond basic whole-chart zoom and drag ops. Implementing or demonstrating the addition of such interactivity with canvas rendered graphics is beyond what I would be prepared to do within the context of a StackOverflow answer.
JFreeChart includes an interaction package:
https://github.com/jfree/jfreechart-fx/tree/master/src/main/java/org/jfree/chart/fx/interaction
I suggest you investigate the interaction package, try using it and see if it offers the level of interaction you need.
As Roger mentions in the comments, you can get some basic interaction on a JFreeChartFX chart by wrapping the chart in a ChartViewer using ChartViewer(JFreeChart myChart).
Related question:
How do I properly add a MouseHandler to my JFreeChart-FX to drag the chart from left to right
An aside on Canvas vs SceneGraph
This info on how canvas works is included so that you might have a better idea of what is going on here (note everything here may not be 100% correct, but is close enough to help understanding).
Technically, JavaFX only uses a SceneGraph for rendering. How canvas is internally implemented, as far as I understand, is that each canvas is a node in the scenegraph and comes with a command queue of drawing instructions. When you draw to the canvas, it doesn't draw immediately, instead it puts the drawing commands into a queue, then, at some point, before the next 60fps drawing pulse completes, it renders those to an image buffer that it relays into a JavaFX node. Old commands are forgotten by the canvas command queue once executed, so everything just ends up as pixels eventually. You can keep track of drawing commands within your application and re-issue them to repaint the canvas from scratch if you wish, but canvas won't help with that.
What JFreeChartFX is doing is providing an adapter which makes a JavaFX canvas look like a Swing painting surface, so that the heavy lifting and internal engine of JFreeChart can be used to issue all of the drawing commands, and those can be rendered to either a JavaFX canvas or Swing canvas depending upon the desired output UI tech.
If JFreeChart also provided a similar adapter for JavaFX events, rather than Swing events, and if JFreeChart already has a way to do interactivity using Swing events, then it could potentially add interactivity to JFreeChartFX using a similar adapter or replacement for Swing event mechanisms. Perhaps that is what the interaction package linked above is doing.

JavaFX graphics: How to change Paint settings?

Nearly a month ago I was testing JavaFX graphics API, however I was worried about performance. The performance problem is that I had to pass a Paint object for colorizing shapes.
Issue: it's impossible to change settings of an existing Paint
I just need to update a Paint, for example, Color, but there are no methods like Color#setRed, Color#setGreen, Color#setBlue and Color#setOpacity. There are no fields, too (javafx.scene.paint.Color).
Performance
The unique way I can try to change settings of a Paint is to construct another Paint instead, however this will involve on running the garbage collector. Is that correct?
The compiler should optimize these objects automatically depending on how they're entirely used. But Oracle said nothing about that on its compiler, at least for me, so far.
If they say garbage collection is good, the fault isn't mine. I didn't mention anything about garbage collection being helpful.
So is it a good solution to re-construct a Paint? Do you guys also know of JavaFX alternatives that handle what I want?
The issue you are concerned with doesn't really exist. Since Paint is an immutable class (which is a good design choice for a number of reasons), the correct way to update the color of something in JavaFX is to create a new Paint instance and set it as a property where needed. Previously-used instances which no longer have references to them will be cleaned up - efficiently - by the garbage collector at some point in the future.
Here is a simple demo which creates new Paint instances every time the scene is rendered (also new Background and BackgroundFill instances). It prints warnings if the frame rendering takes more than 25ms (an arbitrary threshold). On my system, it warns for the first couple of frames, and sometimes warns when the window is hidden and completely reshown.
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class UpdateColorContinuously extends Application {
#Override
public void start(Stage primaryStage) {
Pane root = new Pane();
root.setMinSize(600, 600);
new AnimationTimer() {
private long start = -1 ;
private long lastUpdate ;
#Override
public void handle(long now) {
if (start < 0) {
start = now ;
lastUpdate = now ;
}
long elapsed = now - start ;
double elapsedSeconds = elapsed / 1_000_000_000.0 ;
Color newColor = Color.hsb(elapsedSeconds * 5, 1.0, 1.0);
BackgroundFill fill = new BackgroundFill(newColor, CornerRadii.EMPTY, Insets.EMPTY);
Background bg = new Background(fill);
root.setBackground(bg);
if (now - lastUpdate > 25_000_000) {
System.err.println("Warning: frame rendering took "+ (now-lastUpdate)/1_000_000 + " ms");
}
lastUpdate = now ;
}
}.start();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
It's possible I haven't run this long enough for the GC to need to run; a conservative estimate of the amount of memory a Paint instance needs is 50 bytes, so you would need to create of the order of 10^7 such instances to use 500MB (a reasonably small amount of RAM on a modern desktop system, which would likely be equivalent to one GC sweep). So, ballpark figures, this would generate one GC sweep in two days.

Prefuse example graph partly outside of JPanel

I want to use Prefuse to visualise a graph. I followed their tutorial and tried their sample application. Its sourcecode can be found here
However, even if I simply copy the full code, the resulting graph does not look as displayed in the tutorial. It is only half visible, stuck in JPanel's upper left corner. Some parts of it are missing becuase they would have to be displayed outside the panel.
I tried with some graphs of my own, but I keep running into the same phenomenon.
I suppose this is not expected behaviour, but I have no idea where to hunt for the problem. I don't know if this is a problem with Swing(x) or prefuse or ... ?
Update:
This is the revised code. I did not change much from the example, only added what trashgod suggested.
package visualise;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.DataColorAction;
import prefuse.action.layout.graph.ForceDirectedLayout;
import prefuse.activity.Activity;
import prefuse.controls.DragControl;
import prefuse.controls.PanControl;
import prefuse.controls.ZoomControl;
import prefuse.data.Graph;
import prefuse.data.io.DataIOException;
import prefuse.data.io.GraphMLReader;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.LabelRenderer;
import prefuse.util.ColorLib;
import prefuse.visual.VisualItem;
public class PrefuseExample {
public static void main(String[] argv) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// -- 1. load the data ------------------------------------------------
// load the socialnet.xml file. it is assumed that the file can be
// found at the root of the java classpath
Graph graph = null;
try {
graph = new GraphMLReader().readGraph("../../resources/visualisation/prefuse/Prefuse-master/data/socialnet.xml");
} catch ( DataIOException e ) {
e.printStackTrace();
System.err.println("Error loading graph. Exiting...");
System.exit(1);
}
// -- 2. the visualization --------------------------------------------
// add the graph to the visualization as the data group "graph"
// nodes and edges are accessible as "graph.nodes" and "graph.edges"
Visualization vis = new Visualization();
vis.add("graph", graph);
vis.setInteractive("graph.edges", null, false);
// -- 3. the renderers and renderer factory ---------------------------
// draw the "name" label for NodeItems
LabelRenderer r = new LabelRenderer("name");
r.setRoundedCorner(8, 8); // round the corners
// create a new default renderer factory
// return our name label renderer as the default for all non-EdgeItems
// includes straight line edges for EdgeItems by default
vis.setRendererFactory(new DefaultRendererFactory(r));
// -- 4. the processing actions ---------------------------------------
// create our nominal color palette
// pink for females, baby blue for males
int[] palette = new int[] {
ColorLib.rgb(255,180,180), ColorLib.rgb(190,190,255)
};
// map nominal data values to colors using our provided palette
DataColorAction fill = new DataColorAction("graph.nodes", "gender",
Constants.NOMINAL, VisualItem.FILLCOLOR, palette);
// use black for node text
ColorAction text = new ColorAction("graph.nodes",
VisualItem.TEXTCOLOR, ColorLib.gray(0));
// use light grey for edges
ColorAction edges = new ColorAction("graph.edges",
VisualItem.STROKECOLOR, ColorLib.gray(200));
// create an action list containing all color assignments
ActionList color = new ActionList();
color.add(fill);
color.add(text);
color.add(edges);
// create an action list with an animated layout
ActionList layout = new ActionList(Activity.INFINITY);
layout.add(new ForceDirectedLayout("graph"));
layout.add(new RepaintAction());
// add the actions to the visualization
vis.putAction("color", color);
vis.putAction("layout", layout);
// -- 5. the display and interactive controls -------------------------
Display d = new Display(vis);
d.setSize(720, 500); // set display size
// drag individual items around
d.addControlListener(new DragControl());
// pan with left-click drag on background
d.addControlListener(new PanControl());
// zoom with right-click drag
d.addControlListener(new ZoomControl());
// -- 6. launch the visualization -------------------------------------
// create a new window to hold the visualization
JFrame frame = new JFrame("prefuse example");
// ensure application exits when window is closed
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(d);
frame.pack(); // layout components in window
frame.setVisible(true); // show the window
// assign the colors
vis.run("color");
// start up the animated layout
vis.run("layout");
}
});
}
}
I'm new to Prefuse, but a number of common errors can contribute to the observed problem. Looking at the example,
As discussed here, don't use setSize() on the Display when you really mean to override getPreferredSize().
Swing GUI objects should be constructed and manipulated only on the event dispatch thread‌​.
The initial clustering is an artifact of the graph's origin falling on the top-left corner of the Display component at the point (0, 0). Having chosen a preferred size, one can pan() to the center.
private static final int W = 640;
private static final int H = 480;
…
Display d = new Display(vis) {
#Override
public Dimension getPreferredSize() {
return new Dimension(W, H);
}
};
d.pan(W / 2, H / 2);

Categories

Resources