Reactive Programming in Java:
sine+cosine=circle

 

Two examples are presented: in the first one, a ball draws a circle as result of being moved by two distinct Junior reactive programs; the second example shows the use of broadcast events and of dynamic creation of objects. These two examples are also coded using fair threads; you can click here to see what the two examples coded in FairThreads look like.


Demo1

A ball is moved by two reactive behaviors; one follows a sine path, and the other a cosine path. A reactive applet embeds a reactive machine and call the paint method at each instant. At each instant, color of the ball changes, to visualize the ball moves. One gets a circle; this is basically because the two reactive behaviors share common instants.

The ball

class Ball
{    
    double x, y, angleX = 0, angleY = 0;
    int radius = 8, steps = 100, scale = 5;
    Color color;
    
    public Ball(int x,int y,Color color){ 
       this.x = x; this.y = y; this.color = color;
    }
    public Ball(int x,int y){ this(x,y,ColorBall.nextColor()); }
        
    public void paint(Graphics g){
       g.setColor(color);
       g.fillOval((int)x-radius,(int)y-radius,radius*2,radius*2);
    }
    
    public void sine(){ 
       angleY += (2*Math.PI/steps);
       y += scale*Math.sin(angleY); 
    }
    public void cosine(){
       angleX += (2*Math.PI/steps);
       x += scale*Math.sin(angleX+Math.PI/2); 
    }
}

Sine action

class Sin implements Action
{
    public void execute(Environment env){
       Ball ball = (Ball)env.linkedObject();
       ball.sine();
    }
}

The reactive applet

class ReactiveApplet extends DoubleBufferedApplet implements Runnable
{    
    Thread thread;
    Machine machine = Jr.SafeMachine();
 
    public void init(){ thread = new Thread(this); thread.start(); }
 
    public void run(){
       while(true){
          try{ Thread.sleep(1); }catch(Exception e){}
          machine.react();
          paint(getGraphics());
       }    
    }
}

The applet

public class SinCos extends ReactiveApplet
{
    Ball ball = new Ball(startx,starty);
 
    public void paint(Graphics g) { 
       super.paint(g);
       ball.paint(g);
       ball.color = ColorBall.nextColor();
    } 
        
    Program sine(Ball ball){
       return Jr.Link(ball,Jr.Loop(Jr.Seq(Jr.Atom(new Sin()),Jr.Stop())));
    }
    Program cosine(Ball ball){
       return Jr.Link(ball,Jr.Loop(Jr.Seq(Jr.Atom(new Cos()),Jr.Stop())));
    }
    Program circle(Ball ball){
       return Jr.Par(sine(ball),cosine(ball));
    }
    
    public void init(){
       super.init();
       machine.add(circle(ball));
    }
}

result

 


Adding a new behavior that move the ball following a third path, one gets a Lissajous figure:

 
public void init(){
   super.init();
   machine.add(cosine(ball));
}

result

 


Dynamic Addings


Demo2

Now, balls are dynamically created; balls have two associated behaviors which are waiting for one event to move (following a sine or a cosine path). A button is used to create new balls at random positions; the sine button broadcasts the sine event to all balls; the cosine button broadcasts the cosine event; the sine+cosine button broadcasts both sine and cosine events.


Mouse click -> event

public class GenerateAdapter extends MouseAdapter
{
   Machine machine;
   Vector events;
   public GenerateAdapter(Machine machine,Vector events){ 
      this.machine = machine; this.events = events; 
   }
 
   public void mouseClicked(MouseEvent evt){ 
      Enumeration enum = events.elements();
      while(enum.hasMoreElements()){
         String event = (String)enum.nextElement();
         machine.generate(Jr.StringIdentifier(event));
      } 
   }
}
 

The applet

 
public class DynSinCos extends ReactiveApplet
{
    int rand(int n){ return (int)(Math.random()*1000)%n; }  
  
    Vector items = new Vector();
      
    public void paint(Graphics g){
       synchronized(items){
          Enumeration enum = items.elements();
          while(enum.hasMoreElements()) ((Ball)enum.nextElement()).paint(g);
       }
    } 
 
    void createBall(){ 
       Ball ball = 
         new Ball(rand(size().width),rand(size().height));
       ball.radius = 5;
       items.addElement(ball);
       
       Program sin = 
           Jr.Seq(Jr.Await("sin"),
                  Jr.Loop(Jr.Seq(Jr.Atom(new Sin()),Jr.Stop())));
       Program cos = 
           Jr.Seq(Jr.Await("cos"),
                  Jr.Loop(Jr.Seq(Jr.Atom(new Cos()),Jr.Stop())));
              
       machine.add(Jr.Link(ball,Jr.Par(sin,cos)));
    } 
 
    public void init(){
       super.init();
       Button sin = new Button("sin");
       Button cos = new Button("cos");
       Button sincos = new Button("sin+cos");
       Button start = new Button("new ball");
       
       Vector params = new Vector();
       params.addElement("sin");
       sin.addMouseListener(new GenerateAdapter(machine,params));
        
       params = new Vector();
       params.addElement("sin");
       params.addElement("cos");
       sincos.addMouseListener(new GenerateAdapter(machine,params));
       
       params = new Vector();
       params.addElement("cos");   
       cos.addMouseListener(new GenerateAdapter(machine,params));
    
       start.addMouseListener(new CreateBall(this));
       
       Panel p = new Panel();
       p.setBackground(Color.lightGray);
       setLayout(new BorderLayout());
       p.setLayout(new GridLayout(1,5,5,20));
       add("South",p);	
      
       p.add(sin);
       p.add(cos);
       p.add(sincos);
       p.add(start);
       
       createBall();
    }
}

result



Click here to get the demos source code