Easy to Learn Java: Programming Articles, Examples and Tips

Start with Java in a few days with Java Lessons or Lectures


Code Examples

Java Tools

More Java Tools!

Java Forum

All Java Tips


Submit News
Search the site here...

Swing Chapter 8. (The basics) Split Panes. Easy for reading, Click here!

Custom Search
Swing Chapter 8. (The basics) Split Panes. Easy for reading, Click here!

[ Return to Swing (Book) ]

Page: 3/3 

Previous Page Previous Page (2/3)
Subpages: 1. Split Panes: JSplitPane 
Basic split pane example 
3. Gas model simulation using a split pane 

8.3    Gas model simulation using a split pane

In this section we'll use JSplitPane for an interesting scientific experiment: a simulation of the gas model. Left and right components represent containers holding a two-dimensional "ideal gas." The JSplitPane component provides a moveable divider between them. By moving the divider we observe how gas reacts when its volume is changed. Online educational software is ever-increasing as the internet flourishes, and here we show how Swing can be used to demonstrate one of the most basic laws of thermodynamics!

Note: As you may remember from a physics or chemistry course, an ideal gas is a physical model in which gas atoms or molecules move in random directions bouncing elastically from a container's bounds. Mutual collisions are negligible. The speed of the atoms depends on gas temperature. Several laws can be demonstrated with this model. One states that under the condition of constant temperature, multiplication of pressure P and volume V is constant: PV = const.

To model the motion of atoms we'll use threads. So this example also gives a good example of using several threads in Swing.

Figure 8.2 Gas model simulation showing moving atoms.

<<file figure8-2.gif>>

The Code: Split.java

see \Chapter8\2

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
public class Split extends JFrame implements Runnable{
  protected GasPanel m_left;
  protected GasPanel m_right;
  public Split() {
    super("Gas Pressure [Split Pane]");
    setSize(600, 300);
    ImageIcon ball1 = new ImageIcon("ball1.gif");
    m_left = new GasPanel(30, ball1.getImage());

    ImageIcon ball2 = new ImageIcon("ball2.gif");

    m_right = new GasPanel(30, ball2.getImage());

    JSplitPane sp = new JSplitPane(

      JSplitPane.HORIZONTAL_SPLIT, m_left, m_right);



    getContentPane().add(sp, BorderLayout.CENTER);

    WindowListener wndCloser = new WindowAdapter() {

      public void windowClosing(WindowEvent e) {






    new Thread(m_left).start();

    new Thread(m_right).start();

    new Thread(this).start();


  public void run() {

    while (true) {

      int p1  = (int)m_left.m_px2;

      int pv1 = p1*m_left.getWidth();

      int p2 = (int)m_right.m_px1;

      int pv2 = p2*m_right.getWidth();

      System.out.println("Left: p="+p1+"\tpv="+pv1+

        "\tRight: p="+p2+"\tpv="+pv2);



      try {



      catch(InterruptedException e) {}



  public static void main(String argv[]) { new Split(); }


class GasPanel extends JPanel implements Runnable


  protected Atom[] m_atoms;

  protected Image  m_img;

  protected Rectangle m_rc;

  public double m_px1 = 0;

  public double m_px2 = 0;

  public double m_py1 = 0;

  public double m_py2 = 0;

  public GasPanel(int nAtoms, Image img) {



    m_img = img;

    m_atoms = new Atom[nAtoms];

    m_rc = new Rectangle(getPreferredSize());

    for (int k=0; k<nAtoms; k++) {

      m_atoms[k] = new Atom(this);



  public Dimension getPreferredSize() {

    return new Dimension(300, 300);


  public void run() {

    while (true) {

      for (int k=0; k<m_atoms.length; k++)



      try {



      catch(InterruptedException e) {}



  public void paintComponent(Graphics g) {


    g.fillRect(m_rc.x, m_rc.y, m_rc.width, m_rc.height);

    for (int k=0; k<m_atoms.length; k++)

      g.drawImage(m_img, m_atoms[k].getX(),

        m_atoms[k].getY(), this);


  protected void processComponentEvent(ComponentEvent e) {

    if (e.getID() == ComponentEvent.COMPONENT_RESIZED) {


      for (int k=0; k<m_atoms.length; k++)




  public void clearCounters() {

    m_px1 = 0;

    m_px2 = 0;

    m_py1 = 0;

    m_py2 = 0;



class Atom


  protected double m_x;

  protected double m_y;

  protected double m_vx;

  protected double m_vy;

  protected GasPanel m_parent;

  public Atom(GasPanel parent) {

    m_parent = parent;

    m_x = parent.m_rc.x + parent.m_rc.width*Math.random();

    m_y = parent.m_rc.y + parent.m_rc.height*Math.random();

    double angle = 2*Math.PI*Math.random();

    m_vx = 10*Math.cos(angle);

    m_vy = 10*Math.sin(angle);


  public void move(Rectangle rc) {

    double x = m_x + m_vx;

    double y = m_y + m_vy;

    int x1 = rc.x;

    int x2 = rc.x + rc.width;

    int y1 = rc.y;

    int y2 = rc.y + rc.height;

    for (int bounce = 0; bounce<2; bounce++) {

      if (x < x1) {

        x += 2*(x1-x);

        m_vx = - m_vx;

        m_parent.m_px1 += 2*Math.abs(m_vx);


      if (x > x2) {

        x -= 2*(x-x2);

        m_vx = - m_vx;

        m_parent.m_px2 += 2*Math.abs(m_vx);


      if (y < y1) {

        y += 2*(y1-y);

        m_vy = - m_vy;

        m_parent.m_py1 += 2*Math.abs(m_vy);


      if (y > y2) {

        y -= 2*(y-y2);

        m_vy = - m_vy;

        m_parent.m_py2 += 2*Math.abs(m_vy);



    m_x = x;

    m_y = y;


  public void ensureInRect(Rectangle rc) {

    if (m_x < rc.x)

      m_x = rc.x;

    if (m_x > rc.x + rc.width)

      m_x = rc.x + rc.width;

    if (m_y < rc.y)

      m_y = rc.y;

    if (m_y > rc.y + rc.height)

      m_y = rc.y + rc.height;


  public int getX() { return (int)m_x; }

  public int getY() { return (int)m_y; }


Understanding the Code

Class Split

The constructor of the Split frame creates two instances of the GasPanel class (which models a gas container, see below) and places them in a JSplitPane. All other code requires little discussion, but we must comment on one thing. Both the Split and GasPanel classes implement the Runnable interface, so threads are created and started to run all three instances.

Reminder: The Runnable interface should be implemented by classes which do not intend to use any Thread functionality other than the run() method. In such a case we don't have to sub-class the Thread class. Instead we can simply implement Runnable and define the run() method. In this method we can use the static Thread.sleep() method to yield control to other threads for a specified amount of time.

The run() method of our Split class periodically interrogates the pressure on the divider from the left and right containers, as well as each container's width, which is proportional to the container's volume. Then it prints out the results of our measurements, clears the counters of the containers (see below) and sleeps for another 20 seconds.

Note: Because of the random nature of the gas model all observations are statistical. Each time you run this simulation you're likely to observe a slightly different results. The more atoms that constitute the gas, the more accurate the results achieved will be. A real gas has about 1022 atoms/cm3, and fluctuations in its parameters are very small. In our model only a few dozen "atoms" are participating, so fluctuations are considerable, and we have to wait a bit before we can obtain meaningful measurements (through averaging several results).

Class GasPanel

Class GasPanel models a two-dimensional gas container. It implements the Runnable interface to model the random motion of its contained atoms. Seven instance variables have the following meaning:

Atom[] m_atoms: array of Atom instances hosted by this container.

Image m_img: image used to draw atoms.

Rectangle m_rc: container's rectangular bounds.

double m_px1: counter to measure pressure on the left wall.

double m_px2: counter to measure pressure on the right wall.

double m_py1: counter to measure pressure on the top wall.

double m_py2: counter to measure pressure on the bottom wall.

The GasPanel constructor takes a number of atoms to be created as a parameter as well as a reference to the image to represent them. Method enableEvents() is called to enable the processing of resize events on this component. Finally, an array of atoms is created (see the Atom class below).

The getPreferredSize() method returns the preferred size of this component (used by our split pane to determine the initial position of its divider).

The run() method activates the gas container. For each child atom it invokes the Atom move() method (see below) which changes an Atom's coordinates. The component is then repainted and the calling thread sleeps for 100 ms to provide smooth continuous motion.

The paintComponent() method is overridden to draw each child atom. It clears the component's area and, for each atom, draws the specified image (typically a small ball) at the current atom location.

The processComponentEvent() method is overridden to process resizing events. It updates the m_rc rectangle (used to limit atom motion) and calls the ensureInRect() method for all child atoms to force them to stay inside this component's bounds. A check for the COMPONENT_RESIZED ID is done to skip the processing of COMPONENT_MOVED events which are also delivered to this component even though we've explicitly asked for only COMPONENT_RESIZED events (see enableEvents() call in constructor).

The clearCounters() method clears the counters used to measure the pressure on each of the walls.

Class Atom

An Atom represents a single object moving within a specific rectangular region and bouncing elastically from its walls. Instance variables:

double m_x: current x-coordinate.

double m_y: current y-coordinate.

double m_vx: current x-component of velocity.

double m_vy: current y-component of velocity.

GasPanel m_parent: reference to parent container.

The Atom constructor takes a reference to a parent GasPanel as parameter. It initializes its coordinates randomly within the parent's boudning rectangle using Math.random() as a random number generator. An atom's velocity vector is assigned a fixed absolute magnitude (10) and a random orientation in the x-y plane.

Note: In a more realistic model, velocity would be a normally distributed random value. However, this is not very significant for our purposes.

The move() method moves an atom to a new position and is called during each time the parent GasPanel's run() loop is executed. When new coordinates x, y are calculated, this method checks for possible bounces from this container's walls:

  public void move(Rectangle rc) {

    double x = m_x + m_vx;

    double y = m_y + m_vy;

    int x1 = rc.x;

    int x2 = rc.x + rc.width;

    int y1 = rc.y;

    int y2 = rc.y + rc.height;

    for (int bounce = 0; bounce<2; bounce++) {

      // pseudo-code

      if (x < x1) { // bounce off of left wall... }

      if (x > x2) { // bounce off of right wall... }

      if (y < y1) { // bounce off of top wall... }

      if (y > y2) { // bounce off of bottom wall... }


    m_x = x;

    m_y = y;


If a new point lies behind one of four walls, a bounce occurs, which changes the coordinate and velocity vector. This contributes to the pressure on the wall the bounce occurred on (as an absolute change in the velocity's component), which is accumulated in the parent GasPanel. Note that bouncing is checked twice to take into account the rare case that two subsequent bounces occur in a single step. That can occur near the container's corners, when, after the first bounce, the moving particle is repositioned beyond the nearest perpendicular wall.

The final methods of our Atom class are fairly straightforward. The ensureInRect() method is called to ensure that an Atom's coordinates lie within the given rectangle, and the getX() and getY() methods return the current coordinates as integers.

Running the Code

Note how the gas reacts to the change in the parent container's volume by adjusting the position of the split pane divider. Also try adjusting the size of the application frame.

The following are some P and PV measurements we obtained when experimenting with this example:

Left: p=749     pv=224700       Right: p=996    pv=276888

Left: p=701     pv=210300       Right: p=1006   pv=279668

Left: p=714     pv=214200       Right: p=1028   pv=285784

Left: p=770     pv=231000       Right: p=1018   pv=283004

Left: p=805     pv=241500       Right: p=1079   pv=299962

Left: p=1586    pv=190320       Right: p=680    pv=311440

Left: p=1757    pv=210840       Right: p=594    pv=272052

Left: p=1819    pv=218280       Right: p=590    pv=270220

Left: p=1863    pv=223560       Right: p=573    pv=262434

Left: p=1792    pv=215040       Right: p=621    pv=284418

We can see tell at a certain time the divider had been moved from right to left by the increase in pressure on the left side, and a decrease in pressure on the right side. However, the PV value (in arbitrary units) remains practically unchanged.

[ Return to Swing (Book) ]

Top 10 read Java Articles
 Get free "1000 Java Tips eBook"

 Java Calendar and Date: good to know facts and code examples

 Array vs ArrayList vs LinkedList vs Vector: an excellent overview and examples

 How can I convert any Java Object into byte array? And byte array to file object

 The Java Lesson 1: What is Java?

 How do I compare two dates and times, date between dates, time between times and

 Maven vs Ant or Ant vs Maven?

 How to open, read, write, close file(s) in Java? Examples on move, rename and de

 Java Array

 Java: JLabel font and color

[ More in News Section ]
Java Lessons

The Java Lesson 1:
What is Java?
The Java Lesson 2:
Anatomy of a simple Java program
The Java Lesson 3:
Identifiers and primitive data types
The Java Lesson 4:
Variables, constants, and literals
The Java Lesson 5:
Arithmetic operations, conversions, and casts
The Java Lesson 6:
Boolean expressions and operations
The Java Lesson 7:
Bitwise operations
The Java Lesson 8:
Flow control with if and else
The Java Lesson 9:
switch statements
The Java Lesson 10:
for, while, and do-while statements
The Java Lesson 11:
Using break and continue
The Java Lesson 12:
Class methods and how they are called
The Java Lesson 13:
Using the Math class
The Java Lesson 14:
Creating and calling custom class methods
The Java Lesson 15:
Overloading class methods
The Java Lesson 16:
An introduction to objects and object references
The Java Lesson 17:
The String class
The Java Lesson 18:
The StringBuffer class
The Java Lesson 19:
Initializing and processing arrays of primitives
The Java Lesson 20:
Initializing and processing arrays of objects
The Java Lesson 23:
Inheritance and overriding inherited methods
The Java Lesson 24:
abstract classes and polymorphism
The Java Lesson 25:
Interfaces, instanceof, and object conversion and casting
The Java Lesson 26:
Introduction to graphical programming and the java.awt packa
The Java Lesson 27:
The Component class
The Java Lesson 28:
Containers and simple layout managers
The Java Lesson 29:
The Color and Font classes
The Java Lesson 30:
Drawing geometric shapes
The Java Lesson 31:
Choice, List, and Checkbox controls
The Java Lesson 32:
Using the Scrollbar graphical control
The Java Lesson 33:
Menus and submenus
The Java Lesson 34:
An introduction to applets and the Applet class
The Java Lesson 35:
Essential HTML to launch an applet and pass it parameters
The Java Lesson 36:
Mouse event processing
Java Lesson 37:
Menus and submenus
Java Lesson 38:
The WindowListener interface and the WindowAdapter class
Java Lesson 39:
An introduction to GridBagLayout
Java Lesson 40:
An introduction to the Java Collections API
Java Lesson 41:
Exception handling with try, catch, and finally blocks
Java Lesson 42:
Claiming and throwing exceptions
Java Lesson 43:
Multithreading, the Thread class, and the Runnable interface
Java Lesson 44:
An introduction to I/O and the File and FileDialog classes
Java Lesson 45:
Low-level and high-level stream classes
Java Lesson 46:
Using the RandomAccessFile class
Java Lessons by
Joh Huhtala: Update

Latest articles
 Java Profiler JProbe to Resolve Performance Problems Faster

 SSL with GlassFish v2, page 5

 SSL with GlassFish v2, page 4

 SSL with GlassFish v2, page 3

 SSL with GlassFish v2, page 2

 The Java Lesson 2: Anatomy of a simple Java program, page 2

 New site about Java for robots and robotics: both software and hardware.

 Exceptions -III: What's an exception and why do I care?

 Exceptions -II: What's an exception and why do I care?

 Exceptions: What's an exception and why do I care?

 Double your Java code quality in 10 minutes, here is receipt

 Murach's Java Servlets and JSP

 How to get ascii code from a char in Java?

 Can we just try without catch? Yes!

 Make Tomcat page load faster

 Make your Tomcat More secure - limit network address for certain IP addresses

 New Java book online starts now here...

 Implementing RESTful Web Services in Java

 Firefox trimming from 1 GB to 40 Mb with many tabs opened

 SSL with GlassFish v2

 My request to replublish Tech Tips

 Search JavaFAQ.nu site here

 New Advanced Installer for Java 6.0 brings XML updates and imports 3rd party MSI

 EJB programming restrictions

 Maven vs Ant or Ant vs Maven?

 Why Java does not use default value which it should?

 How to unsign signed bytes in Java - your guide is here

 The Java Lesson 3: Identifiers and primitive data types. Page 2

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 4

 The Java Lesson 7: Bitwise operations with good examples, click here! Page 3

[ More in News Section ]

Home Code Examples Java Forum All Java Tips Books Submit News, Code... Search... Offshore Software Tech Doodling

RSS feed Java FAQ RSS feed Java FAQ News     

    RSS feed Java Forums RSS feed Java Forums

All logos and trademarks in this site are property of their respective owner. The comments are property of their posters, all the rest 1999-2006 by Java FAQs Daily Tips.

Interactive software released under GNU GPL, Code Credits, Privacy Policy