Scalaで図形言語

Scalaで図形言語に挑戦してみた。まだ、Scalaってコードではないけど…。
まずはベクトルClass

// Vector2d Class
// newを省くためにcase classにする。
case class Vector2d(xVal: Double, yVal: Double) {
  val x: Double = xVal
  val y: Double = yVal
  override def toString = "("+x+","+y+")"  
  def this(v:Double) = this(v,v) //s*(x,y)みたいなScaler積表現のため
  def + (that: Vector2d): Vector2d = 
    new Vector2d(x+that.x, y+that.y)
  def - (that: Vector2d): Vector2d = 
    new Vector2d(x-that.x, y-that.y)
  def scala (s: Double): Vector2d = 
    new Vector2d(s*x, s*y)
  def * (that: Vector2d): Vector2d = 
    new Vector2d(x*that.x, y*that.y) //s*(x,y)みたいなScaler積表現のため
  def * (s: Double): Vector2d = 
    new Vector2d(s*x, s*y) //s*(x,y)みたいなScaler積表現のため
}

次に、肝になるPainterの関連

import scala.swing._
import java.awt.Graphics2D

class Frame(o:Vector2d, e1:Vector2d, e2:Vector2d) {
  val origin:Vector2d = o
  val edge1 :Vector2d = e1
  val edge2 :Vector2d = e2
  override def toString = "("+o+","+e1+","+e2+")"
}

case class Segment(s:Vector2d, e:Vector2d) {
  val start:Vector2d = s
  val end  :Vector2d = e
  override def toString = "("+start+","+end+")" 
}

// objectでPainter操作の集合を定義
object painterComponent {
  type Painter = (Frame) => (Graphics2D) => Unit

  //s*(x,y)みたいなScaler積表現のため
  implicit def doubleToVector2d(x: Double) = new Vector2d(x)

  def mapVectorToFrameCoord(f:Frame)(v:Vector2d):Vector2d = {
    f.origin + v.x * f.edge1 + v.y * f.edge2
  }

  def segmentsToPainter(segments:List[Segment]):Painter = (f:Frame) =>   
    (g:Graphics2D) => {
      for(segment <- segments) {
	val start = mapVectorToFrameCoord(f)(segment.start)
	val end   = mapVectorToFrameCoord(f)(segment.end)	
	g.drawLine(start.x.toInt, start.y.toInt, end.x.toInt, end.y.toInt)
      }
  }

  def transformPainter(painter:Painter, origin:Vector2d, corner1:Vector2d, corner2:Vector2d):Painter = 
    (baseFrame:Frame) => {
      val mapToBaseFrame = mapVectorToFrameCoord (baseFrame) _
      val newOrigin = mapToBaseFrame (origin)
      val newEdge1  = mapToBaseFrame (corner1) - newOrigin
      val newEdge2  = mapToBaseFrame (corner2) - newOrigin
      val newFrame = new Frame(newOrigin, newEdge1, newEdge2)
      painter (newFrame)
    }
  
  def flipVert(painter:Painter):Painter =
    transformPainter(painter, 
		     Vector2d(0.0, 1.0), 
		     Vector2d(1.0, 1.0), 
		     Vector2d(0.0, 0.0))

  def flipHoriz(painter:Painter):Painter =
    transformPainter(painter, 
		     Vector2d(1.0, 0.0), 
		     Vector2d(0.0, 0.0), 
		     Vector2d(1.0, 1.0))
  
  def shrinkToUpperRight(painter:Painter):Painter =
    transformPainter(painter, 
		     Vector2d(0.5, 0.5), 
		     Vector2d(1.0, 0.5), 
		     Vector2d(0.5, 1.0))

  def rotate90(painter:Painter):Painter =
    transformPainter(painter, 
		     Vector2d(1.0, 0.0), 
		     Vector2d(1.0, 1.0), 
		     Vector2d(0.0, 0.0))

  def rotate180(painter:Painter):Painter =
    rotate90(rotate90(painter))

  def rotate270(painter:Painter):Painter =
    rotate180(rotate90(painter))

  def beside(leftp:Painter, rightp:Painter):Painter = (f:Frame) => (g:Graphics2D) => {
    val splitPoint = Vector2d(0.5,0.0)
    val shrinkedLeftp = 
	transformPainter(leftp, Vector2d(0.0, 0.0), splitPoint, Vector2d (0.0, 1.0))
    val shrinkedRightp = 
	transformPainter(rightp, splitPoint, Vector2d (1.0, 0.0), Vector2d(0.5, 1.0))
    shrinkedLeftp(f)(g)
    shrinkedRightp(f)(g)
  }
  
  def below(bottomp:Painter, topp:Painter):Painter = (f:Frame) => {
    rotate90 (beside(rotate270(bottomp), rotate270(topp))) (f)
  }

  def identiy(painter:Painter):Painter = painter

  def squareOfFour(tl:Painter => Painter, 
		   tr:Painter => Painter,
		   bl:Painter => Painter, 
		   br:Painter => Painter)
		   (painter:Painter):Painter = {
    val top:Painter    = beside(tl(painter), tr(painter))
    val bottom:Painter = beside(bl(painter), br(painter))
    below (bottom, top)
  }

  def rightSplit(painter:Painter, n:Int):Painter = 
    if(n > 0) {
      val smaller = rightSplit(painter, n-1)
      beside(painter, below(smaller, smaller))
    }
    else {
      painter
    }

  def upSplit(painter:Painter, n:Int):Painter = 
    if(n > 0) {
      val smaller = upSplit(painter, n-1)
      below(painter, beside(smaller, smaller))
    }
    else {
      painter
    }
  
  def cornerSplit(painter:Painter, n:Int):Painter =
    if(n > 0){
      val up          = upSplit(painter, n-1)
      val right       = rightSplit(painter, n-1)
      val topLeft     = beside(up, up)
      val bottomRight = below(right, right)
      val corner      = cornerSplit(painter, n-1)
      beside (below(painter,topLeft), below(bottomRight, corner))
    }
    else {
      painter
    }

  def splitLimit(painter:Painter, n:Int):Painter = {
    val combine = squareOfFour(flipHoriz, identity, rotate180, flipVert) _
    combine(cornerSplit(painter, n))
  }

}

// Swingの部品。Painterをとって実際に実行
class canvasComponent(width:Int, height:Int, p:painterComponent.Painter) extends Component {
  
  peer.setPreferredSize(new Dimension(width, height))
  
  override protected def paintComponent(g: Graphics2D){
    //frameを設定するためにComponentの大きさを取る
    val currentSize:Dimension = peer.getSize()
    val origin = new Vector2d(0, currentSize.height)
    val edge1  = new Vector2d(currentSize.width,  0)
    val edge2  = new Vector2d(0, -currentSize.height)
    val componetFrame:Frame = new Frame(origin, edge1, edge2)

    //白で塗りつぶし
    g.setPaint(java.awt.Color.WHITE)
    g.fillRect(0, 0, currentSize.width, currentSize.height)
    //線の色は黒
    g.setColor(java.awt.Color.BLACK)
    p(componetFrame)(g)
  }    
}

で、splitLimit 4を行うmainのオブジェクト

import scala.swing._

object wave extends SimpleSwingApplication {
  //名前空間を省略するためのalias
  type Painter = painterComponent.Painter
  def segmentsToPainter(segments:List[Segment]):Painter = 
    painterComponent.segmentsToPainter(segments)
  def flipVert(painter:Painter):Painter =
    painterComponent.flipVert(painter)
  def flipHoriz(painter:Painter):Painter =
    painterComponent.flipHoriz(painter)
  def shrinkToUpperRight(painter:Painter):Painter = 
    painterComponent.shrinkToUpperRight(painter)
  def beside(leftp:Painter, rightp:Painter):Painter =
    painterComponent.beside(leftp, rightp)
  def below(bottomp:Painter, topp:Painter):Painter =
    painterComponent.below(bottomp, topp)
  def rightSplit(painter:Painter, n:Int):Painter = 
    painterComponent.rightSplit(painter, n)
  def upSplit(painter:Painter, n:Int):Painter = 
    painterComponent.upSplit(painter, n)
  def cornerSplit(painter:Painter, n:Int):Painter = 
    painterComponent.cornerSplit(painter, n)
  def splitLimit(painter:Painter, n:Int):Painter =
    painterComponent.splitLimit(painter, n)

  val waveSegment = 
    List(Segment(Vector2d(0.0,  0.65), Vector2d(0.15, 0.4)),
	 Segment(Vector2d(0.15, 0.4),  Vector2d(0.3,  0.6)),
         Segment(Vector2d(0.3,  0.6),  Vector2d(0.35, 0.55)),
	 Segment(Vector2d(0.35, 0.55), Vector2d(0.25, 0.0)),
         Segment(Vector2d( 0.4, 0.0),  Vector2d( 0.5, 0.3)),
         Segment(Vector2d( 0.5, 0.3),  Vector2d( 0.6, 0.0)),
         Segment(Vector2d( 0.75,0.0),  Vector2d( 0.6, 0.5)),
         Segment(Vector2d( 0.6, 0.5),  Vector2d( 1.0, 0.15)),
         Segment(Vector2d( 1.0, 0.35), Vector2d( 0.75,0.65)),
         Segment(Vector2d( 0.75,0.65), Vector2d( 0.6, 0.65)),
         Segment(Vector2d( 0.6, 0.65), Vector2d( 0.65,0.85)),
         Segment(Vector2d( 0.65,0.85), Vector2d( 0.6, 1.0)),
         Segment(Vector2d( 0.4, 1.0),  Vector2d( 0.35,0.85)),
         Segment(Vector2d( 0.35,0.85), Vector2d( 0.4, 0.65)),
         Segment(Vector2d( 0.4, 0.65), Vector2d( 0.3, 0.65)),
         Segment(Vector2d( 0.3, 0.65), Vector2d( 0.15,0.6)),
         Segment(Vector2d( 0.15,0.6),  Vector2d( 0.0, 0.85)))
  val wave = segmentsToPainter(waveSegment)

  def top = new MainFrame {
    title = "wave"
    contents = new BorderPanel {
      add(new canvasComponent(400, 400, splitLimit(wave,4)), BorderPanel.Position.Center)
    }
  }
}

参考にしたページ
Scala Componentメモ(Hishidama's Scala Swing Component Memo)