Charting in JME

Any idea on drawing following chart using JME



http://www.oniva.com/upload/1356/chart.jpg



with may some 3D background?



( chart feed with real time Y-axis data every 100 ms and moving to left )


What kind of information specifically are you looking for?  I'm doing something sort of similar for jME 2.0's stats view.

basically the information needs to be charted are sensor data ( plot in y axis )



x axis is sensor data sample every 100 ms

Ok, but what part of doing the charting are you stuck on?

just want to get some idea on any JME class make charting easy



or I have to use some Swing library like JFreeChart

http://www.jfree.org/jfreechart/



the best would made it looks like transparent graphical component instead of swing component.



May be the "com.jme.scene.Geometry" objects can make it really nice by render some texture for the simple 2D area chart.


stmarsp5910 said:

just want to get some idea on any JME class make charting easy

or I have to use some Swing library like JFreeChart
http://www.jfree.org/jfreechart/

the best would made it looks like transparent graphical component instead of swing component.

May be the "com.jme.scene.Geometry" objects can make it really nice by render some texture for the simple 2D area chart.

I ended up using JFreeChart.  I had to render the chart to a BufferedImage and then turn that into a ImageIcon to use it in JMEDesktop though.

any example code / psedo code on using JFreeChart with JME ?

Is it possible to have real time chart in JME ( chart data is moving like a stock quote )

I looked at the JME API

http://www.jmonkeyengine.com/doc/



I don't seem to see BufferedImage / ImageIcon objects

ImageGraphics.drawImage or TextureManager.loadTexture with an awt image argument.

If I keep repaint the ImageIcon to have a Real Time data chart (moving chart) every 50ms



Will JME screen have alot of flash?

  1. I think we can export Chart to a Java Image -

    public void update() {

    setIcon (new ImageIcon (pChart.createBufferedImage(nWidth, nHeight)));

    }


  2. then we can set the 2D plane with the texture ( Image )



    any JME class / code can help doing that?
Momoko_Fan said:

ImageGraphics.drawImage or TextureManager.loadTexture with an awt image argument.

Just generate the chart as vertex data. You can generate a triangle strip for the lower, filled part, and update that triangle strip whenever you get a new piece of data. Going through JMEDesktop and Swing and texture upload is likely to be slow; vertices are likely to be much faster.



Draw this in Ortho view, set Z to implement layering, and generate a tristrip across the screen. Should be pretty simple.

I can tell you that my method will probably lower the fps to 10.  Loading a bufferedImage into a texture takes a long time.

But I am new to JME, any source code i can modify from?



Or which part of the JME classes I need to look into?



Thanks again

Here is a sample program which generates a scrolling chart. It runs at about 1400 fps on my machine in 1024x768. I'm told that my machine is old enough that it's the minimum spec for new games these days (e g Crysis).


import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Random;
import java.util.prefs.Preferences;

import javax.swing.UIManager;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingSphere;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.scene.Node;
import com.jme.scene.SceneElement;
import com.jme.scene.TriMesh;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.LightState;
import com.jme.scene.state.TextureState;
import com.jme.system.GameSettings;
import com.jme.system.PreferencesGameSettings;
import com.jme.util.geom.BufferUtils;


public class TestScrollingChart extends SimpleGame {

   float[] samples;
   Random numbers;
   float current;
   TriMesh chart;
   FloatBuffer vertices;
   IntBuffer indices;
   float[] vertexData;
   int[] indexData;
   Node node;
   float time;

   @Override
   protected void simpleInitGame() {
      samples = new float[100];
      numbers = new Random();
      current = 0.5f;
      //   there's 200 verts, and 99 quads
      vertexData = new float[200*3];
      vertices = BufferUtils.createFloatBuffer(vertexData);
      indexData = new int[99*6];
      for (int i = 0; i < 99; ++i) {
         indexData[i*6] = i*2+1;            //   TL
         indexData[i*6+1] = i*2+0;         //   BL
         indexData[i*6+2] = i*2+3;         //   TR
         indexData[i*6+3] = i*2+3;         //   TR
         indexData[i*6+4] = i*2+0;         //   BL
         indexData[i*6+5] = i*2+2;         //   BR
      }
      indices = BufferUtils.createIntBuffer(indexData);
      chart = new TriMesh("Chart", vertices, null, null, null, indices);
      chart.setRenderQueueMode(Renderer.QUEUE_ORTHO);
      chart.setCullMode(SceneElement.CULL_NEVER);
      chart.setLightCombineMode(LightState.OFF);
      chart.setDefaultColor(new ColorRGBA(0.6f, 0.8f, 0.9f, 1.0f));
      chart.setModelBound(new BoundingSphere());
      chart.updateModelBound();
      node = new Node();
      node.setLocalTranslation(100, 100, 0);
      node.setLocalScale(2);
      TextureState ts = display.getRenderer().createTextureState();
      ts.setEnabled(true);
      node.setRenderState(ts);
      node.setTextureCombineMode(TextureState.REPLACE);
      node.attachChild(chart);
      node.setCullMode(SceneElement.CULL_NEVER);
      lightState.setEnabled(false);
      display.getRenderer().setBackgroundColor(new ColorRGBA(0.4f, 0.4f, 0.4f, 1));
      fpsNode.attachChild(node);
      fpsNode.setCullMode(SceneElement.CULL_NEVER);
      chart.updateGeometricState(0, true);
      chart.updateRenderState();
      time = timer.getTimeInSeconds();
   }

   protected void simpleUpdate() {
      float now = timer.getTimeInSeconds();
      if (now < time + 0.03) {
         return;
      }
      time = now;
      for (int i = 0, n = samples.length-1; i != n; ++i) {
         samples[i] = samples[i+1];
      }
      current = current + (numbers.nextFloat() - current) * 0.05f;
      samples[99] = current;
      float z = 0.5f;
      //   there's 200 verts, and 99 quads
      for (int i = 0; i < 100; ++i) {
         vertexData[i*6] = i * 4;            //   x0
         vertexData[i*6+1] = 0;               //   y0
         vertexData[i*6+2] = z;               //   z0
         vertexData[i*6+3] = i * 4;            //   x1
         vertexData[i*6+4] = samples[i] * 400;   //   y1
         vertexData[i*6+5] = z;               //   z1
      }
      vertices.rewind();
      vertices.put(vertexData);
   }

   /**
    * @param args
    */
   public static void main(String[] args) {
      try {

         UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());

         GameSettings gs = new PreferencesGameSettings(Preferences.userRoot());
         gs.setDepth(32);
         gs.setAlphaBits(8);
         gs.setStencilBits(8);
         gs.setDepthBits(8);
         gs.setFullscreen(false);
         gs.setVerticalSync(false);
         gs.setWidth(800);
         gs.setHeight(600);
         for (int i = 0; i < args.length; ++i) {
            int io = args[i].indexOf('=');
            if (io > 0) {
               String arg = args[i].substring(0, io);
               String value = args[i].substring(io+1);
               gs.set(arg, value);
            }
         }

         TestScrollingChart app = new TestScrollingChart(); // Create Object

         app.start(); // Start the program
      }
      catch (java.lang.Exception x) {
         System.out.println("Exception caught: " + x.getMessage() + "n");
      }
   }

}