import java.awt._ import java.awt.event._ import java.beans._ import java.util.concurrent._ import javax.swing._ import scala.collection._ import scala.reflect._ class Number { @BeanProperty var value : Int = 0 @BeanProperty var x : Double = 0 @BeanProperty var y : Double = 0 @BeanProperty var size : Double = 1 @BeanProperty var red : Double = 0 @BeanProperty var green : Double = 0 @BeanProperty var blue : Double = 0 @BeanProperty var opacity : Double = 1 def draw(g2 : Graphics2D) { g2.setPaint(new Color((255 * red).asInstanceOf[Int], (255 * green).asInstanceOf[Int], (255 * blue).asInstanceOf[Int], (255 * opacity).asInstanceOf[Int])) g2.setFont(new Font("SansSerif", Font.PLAIN, (24 * size).asInstanceOf[Int])) g2.drawString(value.toString, x.asInstanceOf[Float], y.asInstanceOf[Float]) } override def toString = "" + value } object Animation { // This makes an implicit conversion from a function to an action listener implicit def makeAction(action : (ActionEvent)=>Unit) = new ActionListener { override def actionPerformed(event: ActionEvent) { action(event) } } val SLEEPTIME = 10 val DISTANCE = 40 val INTERVAL = 500 val FRAME_WIDTH = 600 val FRAME_HEIGHT = 200 val numbers = new scala.collection.mutable.ArrayBuffer[Number] val mainPanel = new JComponent { override def paintComponent(g : Graphics) { numbers foreach { _.draw(g.asInstanceOf[Graphics2D]) } } } def setProperty(obj : AnyRef, propertyName : String, from : Double, to : Double, duration : Int, comp : Component) { val props = Introspector.getBeanInfo(obj.getClass()).getPropertyDescriptors() props find { _.getName() == propertyName} match { case Some(prop) => val method = prop.getWriteMethod() for (i <- 1 to duration / SLEEPTIME) { Thread.sleep(SLEEPTIME) EventQueue.invokeLater(new Runnable { override def run { method.invoke(obj, new java.lang.Double(from + i * (to - from) / (duration / SLEEPTIME))) comp.repaint() } }) } } } def growShrink(num : Number) { setProperty(num, "size", 1, 2, INTERVAL / 2, mainPanel) setProperty(num, "size", 2, 1, INTERVAL / 2, mainPanel) } def move(num : Number, dx : Int, dy : Int) { if (dx != 0) setProperty(num, "x", num.x, num.x + dx * DISTANCE, INTERVAL, mainPanel) if (dy != 0) setProperty(num, "y", num.y, num.y + dy * DISTANCE, INTERVAL, mainPanel) } def markDone(num : Number) { setProperty(num, "red", 0, 1, INTERVAL, mainPanel) } def randomNumberArray(n : Int, max : Int) = { numbers.clear() val r = new Array[Number](n) for (i <- 0 until n) { val num = new Number num.value = (max * Math.random).asInstanceOf[Int] num.x = i * DISTANCE num.y = 50 r(i) = num numbers += num } r } def insertionSort(a : Array[Number]) { growShrink(a(0)) markDone(a(0)) for (i <- 1 until a.length) { println(a.mkString(",")) val next = a(i); growShrink(next) move(next, 0, 1) // Move all larger elements up var j = i; val tasks = new scala.collection.mutable.ArrayBuffer[()=>Unit] while (j > 0 && a(j - 1).value > next.value) { a(j) = a(j - 1) val num = a(j) move(num, 1, 0) j -= 1 } // Insert the element a(j) = next move(next, j - i, 0) move(next, 0, -1) markDone(next) } } def sort(e : ActionEvent) { val t = new Thread(new Runnable { override def run { val a = randomNumberArray(10, 100) insertionSort(a) } }) t.start() } def main(args : Array[String]) { val frame = new JFrame val buttonPanel = new JPanel frame.add(buttonPanel, BorderLayout.NORTH) val button = new JButton("Sort") buttonPanel.add(button) button.addActionListener(sort _) frame.add(mainPanel) frame.setSize(FRAME_WIDTH, FRAME_HEIGHT) frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) frame.show } }