What's new? | Help | Directory | Sign in
Google
heron-language
Heron is an object-oriented programming language for model driven architecture
  
  
  
  
    
Search
for
Updated Feb 20, 2008 by cdiggins
Labels: Example
BouncingBallsExample  
Shows a simple example of Heron, using the examples of bouncing balls.

Bouncing Balls Example

The following code animates four circles that bounce off the walls of a box but ignore each other. Using the Heron to Java compiler an applet was created which you can see at http://www.heron-language.com/demo.html.

The class and state diagram can be found at http://code.google.com/p/heron-language/wiki/BouncingBallDiagrams

domain Demo
{
  imports {
    Math;
    Graphics;
  }

  attributes {
    painter : Painter;
    walls : Collection<Wall>;
    balls : Collection<Ball>;
  }

  operations {
    initialize() {
      var x0 : int = 100;
      var y0 : int = 100;
      var x1 : int = 300;
      var y1 : int = 300;
      walls = new Collection<Wall>();
      balls = new Collection<Ball>();
      walls.add(new Wall(x0,y0,x1,y0));
      walls.add(new Wall(x1,y0,x1,y1));
      walls.add(new Wall(x1,y1,x0,y1));
      walls.add(new Wall(x0,y1,x0,y0));
      balls.add(new Ball(new Point(200,200), new Vector(200,120), 10));
      balls.add(new Ball(new Point(150,150), new Vector(100,60), 15));
      balls.add(new Ball(new Point(250,150), new Vector(-50,-5), 20));
      balls.add(new Ball(new Point(250,250), new Vector(-100,0), 25));
      balls.add(new Ball(new Point(150,250), new Vector(0,100), 30));
      painter = new Painter();
      painter.generateNextEvent();
    }

    distance(first : Point, second : Point) : Real {
      return sqrt(sqr(first.x - second.x) + sqr(first.y - second.y));
    }
    sqrt(x : Real) : Real {
      return Math.sqrt(x);
    }
    sqr(x : Real) : Real {
      return x * x;
    }
    acos(x : Real) : Real {
      return Math.acos(x);
    }
    min(x : Real, y : Real) : Real {
      return Math.min(x, y);
    }
    floor(x : Real) : int {
      return (int)Math.floor(x);
    }
    updateBallPositions(elapsedMSec : Real) {
      foreach (ball : Ball in Demo.balls) {
        ball.updatePosition(elapsedMSec);
      }
    }
  }

  classes {
    class Point {
      attributes {
        x : Real;
        y : Real;
      }
      operations {
        constructor(x : Real, y : Real) {
          this.x = x;
          this.y = y;
        }
        translate(v : Vector) : Point {
          Point result = new Point(x + v.x, y + v.y);
          return result;
        }
        difference(pt : Point) : Vector {
          return new Vector(pt.x - x, pt.y - y);
        }
        distance(pt : Point) : Real {
          return difference(pt).length();
        }
        asVector() : Vector {
          return new Vector(x, y);
        }
      }
    }

    class Vector {
      attributes {
        x : Real;
        y : Real;
      }
      operations {
        constructor(x : Real, y : Real) {
          this.x = x;
          this.y = y;
        }
        constructor(line : Line) {
          this.x = line.begin.x - line.end.x;
          this.y = line.begin.y - line.end.y;
        }
        add(v : Vector) : Vector {
          return new Vector(x + v.x, y + v.y);
        }
        sub(v : Vector) : Vector {
          return new Vector(v.x - x, v.y - y);
        }
        scale(s : Real) : Vector {
          return new Vector(x * s, y * s);
        }
        dot(v : Vector) : Real {
          return x * v.x + y * v.y;
        }
        normal() : Vector {
          return new Vector(-y, x);
        }
        normalize() : Vector {
          return new Vector(x / length(), y / length());
        }
        length() : Real {
          return Demo.sqrt(Demo.sqr(x) + Demo.sqr(y));
        }
        theta(v : Vector) : Real {
          return Demo.acos(dot(v) / (length() * v.length()));
        }
        tangent() : Vector {
          return new Vector(y, -x);
        }
        proj(v : Vector) : Vector {
          return v.scale(dot(v) / Demo.sqr(v.length()));
        }
      }
    }

    class Line {
      attributes {
        begin : Point;
        end : Point;
      }
      operations {
        constructor(x0 : Real, y0 : Real, x1 : Real, y1 : Real) {
          begin = new Point(x0, y0);
          end = new Point(x1, y1);
        }
        length() : Real {
          return Demo.distance(begin, end);
        }
      }
    }

    class Wall {
      attributes {
        line : Line;
      }
      operations {
        constructor(x0 : Real, y0 : Real, x1 : Real, y1 : Real) {
          line = new Line(x0, y0, x1, y1);
        }
      }
    }

    class Ball {
      attributes {
        pos : Point;
        vec : Vector;
        radius : Real;
      }
      operations {
        constructor(pos : Point, vec : Vector, radius : Real) {
          this.pos = pos;
          this.vec = vec;
          this.radius = radius;
        }
        computeCollisionEvent(wall : Wall) : CollisionEvent
        {
          var x0 : Real = pos.x;
          var y0 : Real = pos.y;
          var xt : Real = vec.x;
          var yt : Real = vec.y;
          var x1 : Real = wall.line.begin.x;
          var y1 : Real = wall.line.begin.y;
          var x2 : Real = wall.line.end.x;
          var y2 : Real = wall.line.end.y;
          var den : Real = Demo.sqrt(Demo.sqr(x2 - x1) + Demo.sqr(y2 - y1));
          var dx : Real = x2 - x1;
          var dy : Real = y2 - y1;

          var t1 : Real = ((radius * den) - (dx * y1) + (dx * y0) + (dy * x1) - (dy * x0)) / (-dx * yt + dy * xt);
          var t2 : Real = (-(radius * den) - (dx * y1) + (dx * y0) + (dy * x1) - (dy * x0)) / (-dx * yt + dy * xt);
          var t : Real = Demo.min(t1, t2);
          if (t1 <= 0.0) {
            if (t2 <= 0.0) {
              return null;
            }
            else {
              t = t2;
            }
          }
          else {
            if (t2 < 0) {
              t = t1;
            }
            else {
              t = Demo .min (t1 , t2 ) ;
            }
          }
          CollisionEvent ret = new CollisionEvent(Demo.floor(t * 1000), this, wall);
          return ret;
        }
        computeCollisionEvents() : Collection<CollisionEvent> {
          Collection<CollisionEvent> result = new Collection<CollisionEvent>();
          foreach (wall : Wall in Demo.walls) {
            CollisionEvent collision = computeCollisionEvent(wall);
            if (collision != null) {
              result.add(collision);
            }
          }
          return result;
        }
        bounceOffWall(wall : Wall) {
            var w : Vector = new Vector(wall.line);
            var n : Vector = w.normal();
            var pn : Vector = vec.proj(n);
            var pw : Vector = vec.proj(w);
            var r : Vector = pn.scale(-1.0).add(pw);
            vec.x = r.x;
            vec.y = r.y;
        }
        updatePosition(elapsedMSec : Real) {
          pos = pos.translate(vec.scale(elapsedMSec / 1000.0));
        }
      }
    }

    class CollisionEvent {
      attributes {
        timeElapsed : Real;
        ball : Ball;
        wall : Wall;
      }
      operations {
        constructor(timeElapsed : Real, ball : Ball, wall : Wall) {
          this.timeElapsed = timeElapsed;
          this.ball = ball;
          this.wall = wall;
        }
      }
    }

    class PaintEvent {
      attributes {
        timeElapsed : Real;
      }
      operations {
        constructor(timeElapsed : Real) {
          this.timeElapsed = timeElapsed;
        }
      }
    }

    class Painter {
      attributes {
        paintFrequency : Real;
        timeToNextPaint : Real;
      }
      operations {
        constructor() {
          paintFrequency = 50.0;
          timeToNextPaint = paintFrequency;
        }
        generateNextEvent()  {
          generateNextEvent(null, null);
        }
        generateNextEvent(lastBall : Ball, lastWall : Wall)  {
          var next : CollisionEvent;
          var q : Collection<CollisionEvent> = new Collection<CollisionEvent>();

          // Computer all possible CollisionEvents
          q.clear();
          foreach (ball : Ball in Demo.balls) {
            q.concat(ball.computeCollisionEvents());
          }

          // Exclude last ball/wall CollisionEvent
          q.filter((x : CollisionEvent)
            => { return (x.ball != lastBall) || (x.wall != lastWall); });

          // Get the next earliest CollisionEvent
          next = q.min((x : CollisionEvent) => { return x.timeElapsed; });

          if (next != null) {
            if (next.timeElapsed < timeToNextPaint) {
              timeToNextPaint = timeToNextPaint - next.timeElapsed;
              this.sendIn(next, (int)next.timeElapsed);
            }
            else {
              this.sendIn(new PaintEvent(timeToNextPaint), (int)timeToNextPaint);
            }
          }
          else {
            Demo.error("could not compute next collision");
          }
        }
      }
      states {
        initial() {
          transitions {
            CollisionEvent -> onCollision;
            PaintEvent -> onPaint;
          }
        }
        onCollision(col : CollisionEvent) {
          entry {
            Demo.updateBallPositions(col.timeElapsed);
            col.ball.bounceOffWall(col.wall);
            generateNextEvent(col.ball, col.wall);
          }
          transitions {
            CollisionEvent -> onCollision;
            PaintEvent -> onPaint;
          }
        }
        onPaint(evt : PaintEvent)  {
          entry {
            timeToNextPaint = paintFrequency;
            Demo.clear();
            Demo.updateBallPositions(evt.timeElapsed);

            foreach (wall : Wall in Demo.walls) {
              Demo.drawLine(wall.line.begin.x, wall.line.begin.y, wall.line.end.x, wall.line.end.y);
            }

            foreach (ball : Ball in Demo.balls) {
              Demo.drawCircle(ball.pos.x, ball.pos.y, ball.radius);
            }

            Demo.render();
            generateNextEvent();
          }
          transitions {
            CollisionEvent -> onCollision;
            PaintEvent -> onPaint;
          }
        }
      }
    }
  }
}

Sign in to add a comment