Play with Lissajous curves

Simple swing app emulating an Harmonograph. It allows to adjust each parameter to play with the curves.

import javax.swing._
import java.awt._
import java.awt.event.{ActionEvent, ActionListener}
import scala.Some
import java.awt.geom.GeneralPath

implicit class TextFieldAdds(tf: JTextField) {
  def getValue =
    for {
      v <- Option(tf.getText)
      if v.trim.nonEmpty
      d = java.lang.Double.parseDouble(v.trim)
    } yield d
  def setValue(f: Double => Double) {
    tf.setText(""+ f(getValue.getOrElse(0d)))
  }
}
val frame = {
  val f = new JFrame("Test it")
  f.setSize(500, 600)
  f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)
  f.getContentPane.setLayout(new BorderLayout())
  f
}
class Pendulum {
  val tA = new JTextField("60")
  val a = new JTextField("1")
  val phi = new JTextField("0")
  val gamma = new JTextField("0.01")
  def getCoor(t: Double) = {
    val A = tA.getValue.getOrElse(200d)
    math.exp(- gamma.getValue.getOrElse(0d) * t) *
    A * math.sin(a.getValue.getOrElse(0d) * t + math.Pi * phi.getValue.getOrElse(0d))
  }
  def addToPanel(p: JPanel) {
    p.add(tA); p.add(a); p.add(phi); p.add(gamma)
  }
  def setRandom() {
    a.setValue(_ + math.random / 50)
    phi.setValue(_ => math.random * 2)
    // gamma.setValue(_ => math.random / 25)
  }
  def setP(p: Pendulum) {
    tA.setValue(_ => p.a.getValue.get)
    a.setValue(_ => 2.1d + p.a.getValue.get)
    phi.setValue(_ => math.Pi * p.phi.getValue.get)
    gamma.setValue(_ => p.gamma.getValue.get)
  }
}
class Pendulum1 {
  val p1 = new Pendulum
  val p2 = new Pendulum

  def getCoor(t: Double) = {
    p1.getCoor(t) + p2.getCoor(t)
  }
  def addToPanel(p: JPanel) {
    p1.addToPanel(p); p2.addToPanel(p)
  }
  def setRandom() {
    p1.setRandom(); p2.setRandom()
  }
  def setP(p: Pendulum1) {
    p1.setP(p.p1); p2.setP(p.p2)
  }
}
val pendulumX = new Pendulum1
val pendulumY = new Pendulum1

val res = new JTextField("0.001")
val range = new JTextField("40")
val button = new JButton("OK")
val random = new JButton("Random")
val controls = {
  val p = new JPanel(new GridLayout(0,4))
  p.add(new JLabel("A"))
  p.add(new JLabel("a:"))
  p.add(new JLabel("phi:"))
  p.add(new JLabel("gamma:"))
  pendulumX.addToPanel(p)
  pendulumY.addToPanel(p)
  p.add(res)
  p.add(range)
  p.add(button)
  p.add(random)
  frame.getContentPane.add(p, BorderLayout.NORTH)
  p
}
def timeStream: Stream[Double] = {
  def time(s: Double):Stream[Double] = s #:: time(s+res.getValue.getOrElse(0.002))
  time(- range.getValue.getOrElse(-20d)).takeWhile(_ <= range.getValue.getOrElse(20d))
}
val plot = {
  val p = new JPanel() {
    setBackground(Color.WHITE)
    override def paintComponent(g: Graphics) = {
      super.paintComponents(g)
      val size = this.getSize()
      g.setColor(Color.WHITE)
      g.fillRect(0, 0, size.getWidth.toInt, size.getHeight.toInt)
      val fixpoint = (size.getWidth / 2, size.getHeight / 2)
      g.setColor(Color.BLUE)
      g.translate(fixpoint._1.toInt, fixpoint._2.toInt)
      val g2 = g.asInstanceOf[Graphics2D]
      val path = new GeneralPath()
      val h = timeStream.head
      path.moveTo(pendulumX.getCoor(h), pendulumY.getCoor(h))
      timeStream.drop(1).foreach { t =>
        val x = pendulumX.getCoor(t)
        val y = pendulumY.getCoor(t)
        path.lineTo(x, y)
      }
      g2.draw(path)
    }
  }
  frame.getContentPane.add(p, BorderLayout.CENTER)
  p
}

button.addActionListener(new ActionListener {
  def actionPerformed(e: ActionEvent) = plot.repaint()
})

random.addActionListener(new ActionListener {
  def actionPerformed(e: ActionEvent) = {
    pendulumX.setRandom()
    pendulumY.setRandom()
    // pendulumX.p1.phi.setValue(_ + 0.025)
    plot.repaint()
  }
})
frame.setVisible(true)

Date: [2014-01-31 Fr]

Created: 2015-05-25 Mo 21:35

Validate