Data Visualization with Java and VTK

Introduction

In this post we’ll show you how to develop a simple Java application for visualizing scientific data. We will useThe Visualization Toolkit (VTK) for the actual data analysis and rendering and Java Swing libraries for a simple GUI that will allow us to modify the plotted output.

Scientific Data Visualization

The typical output from a simulation code is a large file full of numbers. By itself, such a result is rather meaningless. These numbers correspond to the results at various spatial locations. Scientific visualization is the art of converting these numbers into a graphical format that can be understood and aid us in obtaining a new understanding about some physical problem. For example, consider a CAD/flow simulation of a rocket plume. The simulation grid may consist of millions of nodes and on each node we will have a value corresponding to the local plume density. But we may only be interested in the shape of the plume. We can easily visualize the plume by plotting theisosurface of some critical minimum value.

There are both commercial and open-source solutions for performing data visualization. Of the commercial kind, a popular program isTecplot. The free alternatives include VisIt from Livermore, and Paraview from Kitware, Inc., the creators of theVisualization Toolkit, VTK. However, sometimes these options may not be quite adequate. For instance, you may be developing your own simulation software and would like to bundle the solver with a simple visualization output that will allow the user to study the results in real-time. This is where VTK comes in. The Visualization Toolkit is an amazing collection ofC++ classes for processing and visualizing data. It is the engine that does the actual work. VTK is built on the concept of avisualization pipeline. The pipeline starts at one end with the source data and ends at the other end with an actor that correspond to the data’s visual representation. Along the way, in the pipeline, various operations are applied to obtain the output of interest. In order to add visualization to your program, you simply need to provide the algorithm to build the pipeline. The actual data processing and plotting is performed within the VTK library.

VTK provides a simple graphical window that can be used for plotting and interacting with the data (rotation, panning, zoom, etc..). Such an approach may be sufficient for simple console-based applications. But in order to develop an interactive end-user program, you’ll need to incorporate VTK within the GUI of your application. Luckily for us, VTK ships with a built-in support for bothQT and Java. The QT/VTK combination was the basis for our simple visualization program, capVTE. Developed in 2003, capVTE attempted to merge a simplified user interface with the open-source nature of VTK, while at the same time supporting new concepts such as immersed and collaborative visualization. You can read more about capVTEhere andhere (the latter is the draft version but has color pictures). We are currently rewriting the software, however this time we are developing the code in Java. As discussed previously in our article on Java multithreading, the great benefit of Java is that it comes bundled with a large API. Instead of downloading, configuring, and compiling QT (a task for multiple hours), GUI support is natively built into Java via the Swing library.

VTK JAVA Wrappers

So how do you couple VTK with Java? It’s very easy. VTK comes with support for Java in the form of Java wrappers. In the following divs we show you how to install and configure the VTK / Java development environment and how to use it to build a simple Java GUI-driven application.

We are assuming you already have JDK and a Java development environment such as Netbeans or Eclipse installed. In addition you will need to download the VTK source and build it. This requires a working C++ compiler. On Windows, you can obtain theMicrosoft Visual Studio Express Edition for free. Then follow these steps to get started:

    Download CMake from cmake.org. CMake is a cross-platform configuration tool for VTK. It will query your system for the available compilers and settings, and will generate the appropriate project files needed to build VTK.Download VTK source from vtk.org. Make sure to download the Source package and not the Windows Installer.Unzip the source to some temporary directory.Configure your project files by running CMake. Specify the location to the source files you just unzipped and also a different directory where to build the files. This build folder is used during the compilation stage, however at the end, files will be installed into the appropriate system folders. Click Configure to start the process. CMake will run for a while performing various system tests. It will finish with another screen with additional options highlighted in red.Enable Java Wrapping by selecting the appropriate check box (see Figure 1). You will also need to enable theshared libraries. I generally uncheck build tests / examples to reduce the compilation time. PressConfigure again. If everything went well, you will see the Generate button become available. Press this button to generate the project files.(如果第一次操作尽量按照这个里面的来,我第一次MAKE时,选项过多,编译了很长时间)Figure 1. Enable “VTK_WRAP_JAVA” during the CMake Configure processCompile VTK by launching the solution file located in the build folder. If you are using Visual Studio on Microsoft Windows, right click onALL_BUILD and then select Build. The compiler with churn for a while. Depending on your machine, this may take multiple hours. If you are usingmake, use the appropriate make command (most likely make all but this was not tested).Install the system libraries, assuming no errors were encountered, by right clicking onINSTALL and selecting Build. Make sure you run Visual Studio as an administrator for this step. If you do encounter linker errors in the Java wrappers, make sure you are linking against the correct version of Java. My machine is running a 64-bit version of Windows 7. However, I only have the 32-bit C++ compilers. My default Java JDK is the 64-bit version, which resulted in an unresolved symbol “__imp__JAWT_GetAWT@8″ linker error in vtkRenderingJava. If you do encounter these types of errors, make sure to download the 32-bit version of Java JDK and link against it by adjusting the input path under Linker->Input property page for the appropriate project.Check your PATH. The final piece, and likely the biggest headache, is making sure that your Java program can find the required VTK DLLs. These may in turn depend on other DLLs that may not be in the path. Great tool for checking DLL dependencies is Dependency Walker. It took me a while to get everything set up, and in the end, the following directories did the trick. Of course, your setup will likely differ.(路径一定要对,可惜在Netbeans里我整了几次都没有做好,后来直接把bin路径下的dll文件都拷贝到netbeans工程目录下,在Eclipse中设置好就行了,参考http://dev.artenum.com/projects/cassandra/forum/how-to-use-eclipse-and-vtk)C:\Windows\System32;C:\Program Files (x86)\VTK\bin;C:\Program Files (x86)\Java\jdk1.6.0_30\lib;C:\Program Files (x86)\Java\jre6\bin;C:\Program Files (x86)\Java\jre6\bin\client;C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\redist\x86\Microsoft.VC90.CRT;Copy vtk.jar to somewhere safe. This file is located in the bin directory in your VTK build folder. It is better to move it out of here in case you later decide to delete the build directory. It is over 1Gb after all and you don’t need it post Install unless you actually plan to modify the VTK libraries themselves.

Your first VTK/Java Application

VTK comes with an example code for using Java called SimpleVTK.java which you can also find online on VTK’sJava wrapping page. But we have something better for you. DownloadDemoJavaVTK.java here or see the source listing below. This example is based on the SimpleVTK code but extends its functionality. Instead of showing just a simple cylinder, we combine a surface geometry mesh with isosurfaces and cutting planes that can be toggled on and off. This example represents a common scientific visualization task: show and analyze the computed field due to some prescribed boundary.

Using Netbeans, do the following:

    Create a new Java Project with Existing Sources. On the Name and Location screen, uncheck “Create Main Class”.Copy DemoJavaVTK.java into the src folder in your project folder. The file should automatically appear in Netbeans under your new project.Include vtk.jar in the project libraries by right-clicking on “Libraries” and selecting “Add JAR/Folder”. Select thevtk.jar file from step 9 above.Verify the JDK version. It must match the JDK used to build the wrappers. This should only be an issue if you are mixing 64-bit and 32-bit compilers.Run your first Java VTK application. If everything goes well, you will see a window similar to the one in Figure 2 below. On the other hand, if you see messages such as “vtkCommonJava not loaded” there is an issue with your PATH settings. Check and adjust your system PATH per step 8 above. In the window, you can rotate the view by holding down the left button, pan by holding down the middle button, and zoom with the right button.

Figure 2. You can show isosurfaces or the cutting planesFigure 3. Or both. And if you like, neither.Details of the Java VTK Demo Code – Including VTK in a Swing Application

You will find the complete source listing below. The code begins with the static block on line 50,

 static     {        if (!vtkNativeLibrary.LoadAllNativeLibraries()) {          ....}        vtkNativeLibrary.DisableOutputWindow(null);    }

This block calls a function in the VTK Java wrapper that attempts to load all required shared libraries (.DLLs). If any library fails to load, the code displays the name of the library, as well as the PATH that is being used in the search. The final call in this block disables the console window that is used by VTK to print various diagnostic messages.

The code next launches the Main function on line 215. This main creates a new Swing JFrame and populates the frame with a single jPanel-derived widget,DemoJavaVTK. This panel is the actual meat of our application. The classDemoJavaVTK is defined as

public class DemoJavaVTK extends JPanel implements ActionListener {  ...}

The JFrame creation is wrapped inside SwingUtilities’ InvokeLater command, as shown below. This is required since we are directly accessing the GUI in the creation of the VTK panel. This syntax makes sure that our code is executed only once the window manager is running and ready for GUI updates.

SwingUtilities.invokeLater(new Runnable() {@Override    public void run()     {        JFrame frame = new JFrame("Java and VTK Demo");        ...        frame.getContentPane().add(new DemoJavaVTK(), BorderLayout.CENTER);        ...     }});

Building a VTK Java Panel

The real work is being done in the DemoJavaVTK constructor starting on line 67. We first specifyBorderLayout. This layout contains a large stretching central widget and small areas around all four borders that can be populated with small widgets like buttons. We will end up placing the interactor window in the central area and will create another panel containing control buttons along the bottom.

We next start building the pipelines. We are generating three actors: isosurfaces, cutting planes, and a surface geometry. We first generate the data source in 1)

/**** 1) INPUT DATA: Sphere Implicit Function ****/vtkSphere sphere = new vtkSphere();sphere.SetRadius(radius); vtkSampleFunction sample = new vtkSampleFunction();sample.SetSampleDimensions(50,50,50);sample.SetImplicitFunction(sphere);

Here we are using an implicit function to define the data. An implicit function is a function of the form . In our case, we are using the “sphere” function, which evaluates the distance from the surface of a sphere, . This function will evaluate to zero on the surface, and to a non-zero value away from it. The SampleDataDimension command specifies the grid dimensions on which the function will be evaluated. In the general case, this source data would be the output from your simulation. VTK contains many file readers that can easily accomplish the file import.

We next start building the isosurface actor,

/**** 2) PIPELINE 1: Isosurface Actor ****//* contour filter - will generate isosurfaces from 3D data*/vtkContourFilter contour = new vtkContourFilter();contour.SetInputConnection(sample.GetOutputPort());contour.GenerateValues(3,0,1); /* mapper, translates polygonal representation to graphics primitives */vtkPolyDataMapper isoMapper = new vtkPolyDataMapper();isoMapper.SetInputConnection(contour.GetOutputPort()); /*isosurface actor*/isoActor = new vtkActor();isoActor.SetMapper(isoMapper);

We create a contour filter that basically reduces the dimensionality of the data. From the 3D volume, this filter will create isosurfaces. We are telling the filter to create 3 equidistant surfaces in the range [0,1]. Negative values correspond to the domain internal to the sphere, and so this range creates surfaces external and including the sphere. This div also introduces you to the concept of the visualization pipeline. We pipe different pieces together using thefilter2.SetInputConnection(filter1.GetOutputPort()) syntax. We next create aPolyDataMapper. This is a special algorithm that takes the underlying polygonal representation of the data and translates it into the basic graphics primitives supported by the graphics card. The mapper is next used to create theactor. The actor is the actual object that will be added to the render window to create our scene.

We next perform another similar operation in steps 3) to create the cutting planes. Here we are using an implicit function for a plane to define the cut surface. However, we evaluate the plane function at three values, -0.7, 0, and 0.7 to create three different cutting planes. We also create our own LookupTable (LUT). A LUT maps the actual data values to screen colors. We create a LUT using the default color scheme but with only 5 colors to create a banded effect. The rest of the code consists of building the mapper and the actor in a manner similar to the previous step. One difference is that we change the interpolation/shading method to flat. This will give us a nicer looking cutting plane – without this command the plane would look shiny and metallic.

/**** 3) PIPELINE 2: Cutting Plane Actor ****//* define a plane in x-y plane and passing through the origin*/vtkPlane plane = new vtkPlane();plane.SetOrigin(0,0,0);plane.SetNormal(0,0,1); /* cutter, basically interpolates source data onto the plane */vtkCutter planeCut = new vtkCutter();planeCut.SetInputConnection(sample.GetOutputPort());planeCut.SetCutFunction(plane);/*this will actually create 3 planes at the subspace where the implicit * function evaluates to -0.7, 0, 0.7 (0 would be original plane). In  * our case this will create three x-y planes passing through  * z=-0.7, z=0, and z=+0.7*/planeCut.GenerateValues(3,-0.7,0.7); /* look up table, we want to reduce number of values to get discrete bands */vtkLookupTable lut = new vtkLookupTable();lut.SetNumberOfTableValues(5); /* mapper, using our custom LUT */vtkPolyDataMapper cutMapper = new vtkPolyDataMapper();cutMapper.SetInputConnection(planeCut.GetOutputPort());cutMapper.SetLookupTable(lut); /* cutting plane actor, looks much better with flat shading */cutActor = new vtkActor();cutActor.SetMapper(cutMapper);cutActor.GetProperty().SetInterpolationToFlat();

We then create our final player, the surface geometry actor. This actor is being used to show the geometry of interest. This would typically be the object you are simulating, while the two previous actors correspond to the visualization of computed results. We create a coarse sphere and show the edges. Very often, the surface geometry is imported into the simulation code as a triangulated surface mesh. This visualization is intended to indicate this. We manipulate the actor properties to turn on the edges and set their color to dark gray. We again use the flat shading.

/**** 4) PIPELINE 3: Surface Geometry Actor ****//* create polygonal representation of a sphere */vtkSphereSource surf = new vtkSphereSource();surf.SetRadius(radius); /* another mapper*/vtkPolyDataMapper surfMapper = new vtkPolyDataMapper();surfMapper.SetInputConnection(surf.GetOutputPort()); /* surface geometry actor, turn on edges and apply flat shading*/vtkActor surfActor = new vtkActor();surfActor.SetMapper(surfMapper);surfActor.GetProperty().EdgeVisibilityOn();surfActor.GetProperty().SetEdgeColor(0.2,0.2,0.2);surfActor.GetProperty().SetInterpolationToFlat();

We then finally start building our display. The heart of this example, and of any Java / VTK integration, is thevtkPanel widget. This GUI component is the piece that does the actual rendering and user interaction. It corresponds to the RenderWindow from the C++ implementation. We access itsRenderer, and add the default actors. On startup we only show the geometry and the isosurface. We also change the default zoom and the background color.

/**** 5) RENDER WINDOW ****//* vtkPanel - this is the interface between Java and VTK */renWin = new vtkPanel(); /* add the surface geometry plus the isosurface */renWin.GetRenderer().AddActor(surfActor);renWin.GetRenderer().AddActor(isoActor); /* the default zoom is whacky, zoom out to see the whole domain */renWin.GetRenderer().GetActiveCamera().Dolly(0.2); renWin.GetRenderer().SetBackground(1, 1, 1);

We next shift gears a bit and create a standard Swing panel containing three buttons. We use the grid layout to stretch the buttons to fill the space. We toggle the isosurface button on by default since we are starting the simulation with the isosurface actor visible.

/**** 6) CREATE PANEL FOR BUTTONS ****/buttons  = new JPanel();buttons.setLayout(new GridLayout(1,0)); /* isosurface button, clicked by default */isoButton = new JToggleButton("Isosurfaces",true);isoButton.addActionListener(this); /* cutting planes button */slicesButton = new JToggleButton("Slices");slicesButton.addActionListener(this); /* exit button */exitButton = new JButton("Exit");exitButton.addActionListener(this); /* add buttons to the panel */buttons.add(isoButton); buttons.add(slicesButton);buttons.add(exitButton);

And finally, we fill the BorderLayout with the render window (in center) and the buttons (bottom/south).

/**** 7) POPULATE MAIN PANEL ****/add(renWin, BorderLayout.CENTER);add(buttons, BorderLayout.SOUTH);

Button Actions

The example is now almost complete. The only thing that remains is to add code for the buttons. The logic is very simple. We add an actor when the button is pressed down, and remove it when the button is toggled off. We also ask the render window to repaint itself to show the difference in the scene. Without this call, the window would not update until we interact with it.

/*cutting planes button, add or remove cutActor */if (e.getSource().equals(slicesButton)){     if (slicesButton.isSelected())          renWin.GetRenderer().AddActor(cutActor);     else          renWin.GetRenderer().RemoveActor(cutActor);      renWin.Render();}

And that’s it. Your first Java / VTK application!

Complete Source Listing

39404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
import java.awt.BorderLayout;import java.awt.GridLayout;import java.awt.event.ActionEvent;import java.awt.event.ActionListener; import javax.swing.JButton;import javax.swing.JToggleButton;import javax.swing.JFrame;import javax.swing.JPanel;import javax.swing.SwingUtilities; import vtk.vtkNativeLibrary;import vtk.vtkPanel;import vtk.vtkActor;import vtk.vtkSphere;import vtk.vtkSphereSource;import vtk.vtkSampleFunction;import vtk.vtkContourFilter;import vtk.vtkPlane;import vtk.vtkCutter;import vtk.vtkLookupTable;import vtk.vtkPolyDataMapper; /* ************************************************************ * Demo applications showcasing how to use VTK with Java *  * Based on SimpleVTK.java example distributed with VTK *  * For more information see: * http://www.particleincell.com/2011/vtk-java-visualization *  * Information about VTK can be found at: * http://vtk.org/ *  * ***********************************************************/ public class DemoJavaVTK extends JPanel implements ActionListener {    private static final long serialVersionUID = 1L;    private vtkPanel renWin;    private vtkActor cutActor;    private vtkActor isoActor;     private JPanel buttons;    private JToggleButton slicesButton;    private JToggleButton isoButton;    private JButton exitButton;     /* Load VTK shared librarires (.dll) on startup, print message if not found */    static     {        if (!vtkNativeLibrary.LoadAllNativeLibraries()) {       for (vtkNativeLibrary lib : vtkNativeLibrary.values()) {                if (!lib.IsLoaded()) System.out.println(lib.GetLibraryName() + " not loaded");    } System.out.println("Make sure the search path is correct: ");System.out.println(System.getProperty("java.library.path"));        }        vtkNativeLibrary.DisableOutputWindow(null);    }     /* Constructor - generates visualization pipeline and adds actors*/    public DemoJavaVTK()     {        super(new BorderLayout()); /* large center and small border areas*/         double radius = 0.8;   /*sphere radius*/ /**** 1) INPUT DATA: Sphere Implicit Function ****/vtkSphere sphere = new vtkSphere();sphere.SetRadius(radius); vtkSampleFunction sample = new vtkSampleFunction();sample.SetSampleDimensions(50,50,50);sample.SetImplicitFunction(sphere); /**** 2) PIPELINE 1: Isosurface Actor ****/ /* contour filter - will generate isosurfaces from 3D data*/vtkContourFilter contour = new vtkContourFilter();contour.SetInputConnection(sample.GetOutputPort());contour.GenerateValues(3,0,1); /* mapper, translates polygonal representation to graphics primitives */vtkPolyDataMapper isoMapper = new vtkPolyDataMapper();        isoMapper.SetInputConnection(contour.GetOutputPort()); /*isosurface actor*/        isoActor = new vtkActor();        isoActor.SetMapper(isoMapper); /**** 3) PIPELINE 2: Cutting Plane Actor ****/ /* define a plane in x-y plane and passing through the origin*/vtkPlane plane = new vtkPlane();plane.SetOrigin(0,0,0);plane.SetNormal(0,0,1); /* cutter, basically interpolates source data onto the plane */vtkCutter planeCut = new vtkCutter();planeCut.SetInputConnection(sample.GetOutputPort());planeCut.SetCutFunction(plane);/*this will actually create 3 planes at the subspace where the implicit * function evaluates to -0.7, 0, 0.7 (0 would be original plane). In  * our case this will create three x-y planes passing through  * z=-0.7, z=0, and z=+0.7*/planeCut.GenerateValues(3,-0.7,0.7); /* look up table, we want to reduce number of values to get discrete bands */vtkLookupTable lut = new vtkLookupTable();lut.SetNumberOfTableValues(5); /* mapper, using our custom LUT */vtkPolyDataMapper cutMapper = new vtkPolyDataMapper();        cutMapper.SetInputConnection(planeCut.GetOutputPort());cutMapper.SetLookupTable(lut); /* cutting plane actor, looks much better with flat shading */cutActor = new vtkActor();        cutActor.SetMapper(cutMapper);cutActor.GetProperty().SetInterpolationToFlat(); /**** 4) PIPELINE 3: Surface Geometry Actor ****/ /* create polygonal representation of a sphere */vtkSphereSource surf = new vtkSphereSource();surf.SetRadius(radius); /* another mapper*/vtkPolyDataMapper surfMapper = new vtkPolyDataMapper();surfMapper.SetInputConnection(surf.GetOutputPort()); /* surface geometry actor, turn on edges and apply flat shading*/vtkActor surfActor = new vtkActor();surfActor.SetMapper(surfMapper);surfActor.GetProperty().EdgeVisibilityOn();surfActor.GetProperty().SetEdgeColor(0.2,0.2,0.2);surfActor.GetProperty().SetInterpolationToFlat(); /**** 5) RENDER WINDOW ****/ /* vtkPanel - this is the interface between Java and VTK */renWin = new vtkPanel(); /* add the surface geometry plus the isosurface */renWin.GetRenderer().AddActor(surfActor);renWin.GetRenderer().AddActor(isoActor); /* the default zoom is whacky, zoom out to see the whole domain */        renWin.GetRenderer().GetActiveCamera().Dolly(0.2); renWin.GetRenderer().SetBackground(1, 1, 1); /**** 6) CREATE PANEL FOR BUTTONS ****/buttons  = new JPanel();buttons.setLayout(new GridLayout(1,0));         /* isosurface button, clicked by default */isoButton = new JToggleButton("Isosurfaces",true);        isoButton.addActionListener(this); /* cutting planes button */        slicesButton = new JToggleButton("Slices");        slicesButton.addActionListener(this); /* exit button */exitButton = new JButton("Exit");        exitButton.addActionListener(this); /* add buttons to the panel */buttons.add(isoButton); buttons.add(slicesButton);buttons.add(exitButton);  /**** 7) POPULATE MAIN PANEL ****/        add(renWin, BorderLayout.CENTER);        add(buttons, BorderLayout.SOUTH);    }     /* ActionListener that responds to button clicks     * Toggling iso/slices buttons results in addition or removal     * of the corresponding actor */    public void actionPerformed(ActionEvent e)     {/*cutting planes button, add or remove cutActor */if (e.getSource().equals(slicesButton)){if (slicesButton.isSelected())renWin.GetRenderer().AddActor(cutActor);elserenWin.GetRenderer().RemoveActor(cutActor); renWin.Render();}/*isosurface button, add or remove isoActor */else if (e.getSource().equals(isoButton)){if (isoButton.isSelected())renWin.GetRenderer().AddActor(isoActor);elserenWin.GetRenderer().RemoveActor(isoActor);renWin.Render();}/*exit button, end application */else if (e.getSource().equals(exitButton)) {            System.exit(0);        }    }     /* main, creates a new JFrame and populates it with the DemoJavaVTK panel */    public static void main(String s[])     {        SwingUtilities.invokeLater(new Runnable() {            @Override            public void run()     {                JFrame frame = new JFrame("Java and VTK Demo");                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);                frame.getContentPane().setLayout(new BorderLayout());                frame.getContentPane().add(new DemoJavaVTK(), BorderLayout.CENTER);                frame.setSize(400, 400);                frame.setLocationRelativeTo(null);                frame.setVisible(true);            }        });    }}

You can also download the code here: DemoJavaVTK.java. Please feel free to leave a comment or contact us if you have any questions. One of the areas we specialize in here at PIC-C is development oflight-weight visualization solutions, and we will gladly work with your company to develop one for your solver.

Tweet

Did you find this article useful? If so, please share the link with your friends, coworkers, colleagues, students, professors, or your favorite social network. And don’t forget tosubscribe to the newsletter and follow us on Google+ and Twitter.

Posted in: Visualization, 4 comments on Data Visualization with Java and VTK

Data Visualization with Java and VTK

充满了恐惧的声音,一种不确定的归宿的流动。

Data Visualization with Java and VTK

相关文章:

你感兴趣的文章:

标签云: