scala勉強会第6回

継続して参加中のscala勉強会第六回もいってきました。
今日は、ゆろよろさんのエントリの真似して、そのときの@uchida75cmさんのコードをリファクタリングしてみようと思います


もとのコード

まずは、

  • 余計なimport、余計な型宣言削除
  • MouseListenerインターフェイス実装してるけど、結局オーバーライドした中身空なので、削除
  • なぜか関数オブジェクトでvalとして保存されてるものが複数あるけど、一見するとわかりずらいのでdefに変更
  • 逆に、毎回評価する必要ないdefをvalに変更
  • 変更されていないvarをvalに
  • コンパニオンオブジェクト作って、純粋な関数と、定数をそっちに移動
import java.awt.{ List => _ , _}//scalaのPredefのListとかぶるので、こっちのListを除外
import java.awt.event._

object Draw{
  
  val mes = List( "0","1","2","3","4","5","6","7" )
  var siz: List[ Double ] = List( 1, 1, 1 )
    
  val org: List[List[ Double ]] = List(
      List( -100, 100, -100, 1 ),
      List( 100, 100, -100, 1 ),
      List( 100, -100, -100, 1 ),
      List( -100, -100, -100, 1 ),
      List( -100, 100, 100, 1 ),
      List( 100, 100, 100, 1 ),
      List( 100, -100, 100, 1 ),
      List( -100, -100, 100, 1 )
  )

  //つかってない?
  val surfeces = List(
      List( 0, 1, 2, 3 ),
      List( 4, 5, 6, 7 ),
      List( 0, 4, 5, 1 ),
      List( 1, 5, 6, 2 ),
      List( 2, 6, 7, 3 ),
      List( 3, 7, 4, 0 )
  )
  
  val determinantSize: List[List[ Double ]] = List(
      List( siz(0), 0, 0, 0 ),
      List( 0, siz(1), 0, 0 ),
      List( 0, 0, siz(2), 0 ),
      List( 0, 0, 0, 1 )
  )

  def sin( d: Double ) = Math.sin( Math.toRadians( d ))
  def cos( d: Double ) = Math.cos( Math.toRadians( d ))
  
}
class Draw extends Frame with Runnable with MouseMotionListener {
  import Draw._
  
  {
    setTitle( "Polygon" )
    setSize( 400, 400 )
    setBackground( Color.black )
    setVisible( true )
    addMouseMotionListener( this )
    addWindowListener( new WindowAdapter {
      override def windowClosing( e: WindowEvent ): Unit = {
        System.exit( 0 )
      }
    })
  }
  
  var angle: List[ Double ] = List( 0.0, 0.0, 0.0 )
  var position: List[ Double ] = List( 0, 0, 0 )
  var eye: List[ Double ] = List( 0, 0, 400 )

  def determinantX(): List[List[ Double ]] = List(
      List( 1, 0, 0, 0 ),
      List( 0, cos(angle(0)), sin(angle(0)), 0 ),
      List( 0, -sin(angle(0)), cos(angle(0)), 0 ),
      List( 0, 0, 0, 1 )
  )

  def determinantY(): List[List[ Double ]] = List(
      List( cos(angle(1)), 0, -sin(angle(1)), 0 ),
      List( 0, 1, 0, 0 ),
      List( sin(angle(1)), 0, cos(angle(1)), 0 ),
      List( 0, 0, 0, 1 )
  )

  def determinantZ(): List[List[ Double ]] = List(
      List( cos(angle(2)), sin(angle(2)), 0, 0 ),
      List( -sin(angle(2)), cos(angle(2)), 0, 0 ),
      List( 0, 0, 1, 0 ),
      List( 0, 0, 0, 1 )
  )

  def determinantWorld(): List[List[ Double ]] = List(
      List( 1, 0, 0, 0 ),
      List( 0, 1, 0, 0 ),
      List( 0, 0, 1, 0 ),
      List( position(0), position(1), position(2), 1 )
  )

  def changeDirection( p3d: List[List[ Double ]]):List[List[ Double ]] =
    ( 0 to 3 ).toList.map( i => ( 0 to 3 ).toList.map( j => p3d( j )( i ) ))

  def fusion( a:List[ Double ], b:List[ Double ] ):Double =
    ( 0 to 3 ).toList.map( idx => a( idx ) * b( idx ) ).foldLeft( 0.0 )( _ + _ )

  def affine( determinant: List[List[ Double ]], org: List[List[ Double ]] ): List[List[ Double ]] = {
    val idx = ( 0 to 3 ).toList
    org.map( i => idx.map( j => fusion( i, changeDirection( determinant )( j ) )))
  }

  def to2d(p3d: List[List[ Double ]]): List[List[ Int ]] =
    p3d.map( i => ( 0 to 1 ).toList.map( idx => ( 200. + 320.*i( idx ) / ( eye(2) + i(2) ) ).toInt ) )

  override def paint( g: Graphics ){
    val p3d = affine( determinantX, affine( determinantY, affine( determinantZ, affine( determinantSize, affine( determinantWorld, org )))))
    g.setColor(Color.white)
    for { i <- 0 to 7 }
      g.drawString( String.valueOf( mes(i) ), to2d( p3d )( i )( 0 ), to2d( p3d )( i )( 1 ) )
  }

  def mouseMoved( e: MouseEvent ){
    angle = List( e.getY()-200, -( e.getX()-200 ), 0 )
  }
  def mouseDragged( e: MouseEvent ){
    position = List( e.getX()-200, e.getY()-200, 0 )
  }

  def run(){
    try {
      while( true ) {
        repaint
        Thread.sleep( 30 )
      }
    } catch {
      case e: InterruptedException =>
        println( e.getMessage )
    }
  }
  
}

つぎは、

  • List[List[ Double ]]とかちょっと長い、かつ何を表すのかわかりづらいので、aliasつける
  • メソッド同士の依存関係がわかりづらかったので、とりあえず、ローカル関数使えるところはすべてローカル関数にしてみた
import java.awt.{ List => _ , _}//scalaのPredefのListとかぶるので、こっちのListを除外
import java.awt.event._

object Draw{
  //行列
  type Matrix[A] = List[List[A]]
  
  val mes = List( "0","1","2","3","4","5","6","7" )//これもべつに保存しておく必要無さそう
  val siz: List[ Double ] = List( 1, 1, 1 )
    
  val org: Matrix[ Double ] = List(
      List( -100, 100, -100, 1 ),
      List( 100, 100, -100, 1 ),
      List( 100, -100, -100, 1 ),
      List( -100, -100, -100, 1 ),
      List( -100, 100, 100, 1 ),
      List( 100, 100, 100, 1 ),
      List( 100, -100, 100, 1 ),
      List( -100, -100, 100, 1 )
  )

  val surfeces = List(
      List( 0, 1, 2, 3 ),
      List( 4, 5, 6, 7 ),
      List( 0, 4, 5, 1 ),
      List( 1, 5, 6, 2 ),
      List( 2, 6, 7, 3 ),
      List( 3, 7, 4, 0 )
  )
  
  val determinantSize: Matrix[ Double ] = List(
      List( siz(0), 0, 0, 0 ),
      List( 0, siz(1), 0, 0 ),
      List( 0, 0, siz(2), 0 ),
      List( 0, 0, 0, 1 )
  )

  def sin( d: Double ) = Math.sin( Math.toRadians( d ))
  def cos( d: Double ) = Math.cos( Math.toRadians( d ))
  
}
class Draw extends Frame with Runnable with MouseMotionListener {
  import Draw._ //コンパニオンオブジェクトのメンバーをインポート
  
  //初期化
  {
    setTitle( "Polygon" )
    setSize( 400, 400 )
    setBackground( Color.black )
    setVisible( true )
    addMouseMotionListener( this )
    addWindowListener( new WindowAdapter {
      override def windowClosing( e: WindowEvent ): Unit = {
        System.exit( 0 )
      }
    })
  }
  
  //angleとpositionが、ユーザーから受け取ったmouseの情報保持してる変数?
  var angle: List[ Double ] = List( 0.0, 0.0, 0.0 )
  var position: List[ Double ] = List( 0, 0, 0 )
  
  var eye: List[ Double ] = List( 0, 0, 400 )

  //全体として、描画処理時にpaintが呼ばれるわけで、他のほとんどのメソッドは、paintから呼ばれるだけなので、こんな感じにローカル関数にできる
  override def paint( g: Graphics ){
    
    def to2d(p3d: Matrix[ Double ]): Matrix[ Int ] = {
      p3d.map( i => ( 0 to 1 ).toList.map( idx => ( 200. + 320.*i( idx ) / ( eye(2) + i(2) ) ).toInt ) )
    }
    
    def affine( determinant: Matrix[ Double ], org: Matrix[ Double ] ): Matrix[ Double ] = {
      
      //これも純粋?toList削れる?足すだけなら、sumっていうメソッドある
      def fusion( a:List[ Double ], b:List[ Double ] ):Double = {
        ( 0 to 3 ).toList.map( idx => a( idx ) * b( idx ) ).foldLeft( 0.0 )( _ + _ )
      }
      
      //これも純粋。行と列へんかんするなら、List objectに丁度いいメソッドあるみたい
      def changeDirection( p3d: List[List[ Double ]]):Matrix[ Double ] = {
        ( 0 to 3 ).toList.map( i => ( 0 to 3 ).toList.map( j => p3d( j )( i ) ))
      }
      
      val idx = ( 0 to 3 ).toList
      org.map( i => idx.map( j => fusion( i, changeDirection( determinant )( j ) )))
    }
    
    def determinantWorld(): Matrix[ Double ] = List(
      List( 1, 0, 0, 0 ),
      List( 0, 1, 0, 0 ),
      List( 0, 0, 1, 0 ),
      List( position(0), position(1), position(2), 1 )
    )
    
    def determinantZ(): Matrix[ Double ] = List(
      List( cos(angle(2)), sin(angle(2)), 0, 0 ),
      List( -sin(angle(2)), cos(angle(2)), 0, 0 ),
      List( 0, 0, 1, 0 ),
      List( 0, 0, 0, 1 )
    )
    
    def determinantY(): Matrix[ Double ] = List(
      List( cos(angle(1)), 0, -sin(angle(1)), 0 ),
      List( 0, 1, 0, 0 ),
      List( sin(angle(1)), 0, cos(angle(1)), 0 ),
      List( 0, 0, 0, 1 )
    )
    
    def determinantX(): Matrix[ Double ] = List(
      List( 1, 0, 0, 0 ),
      List( 0, cos(angle(0)), sin(angle(0)), 0 ),
      List( 0, -sin(angle(0)), cos(angle(0)), 0 ),
      List( 0, 0, 0, 1 )
    )
      
    val p3d = affine( determinantX, affine( determinantY, affine( determinantZ, affine( determinantSize, affine( determinantWorld, org )))))
    g.setColor(Color.white)
    for { i <- 0 to 7 }
      g.drawString( String.valueOf( mes(i) ), to2d( p3d )( i )( 0 ), to2d( p3d )( i )( 1 ) )
      
  }

  //マウス動かしたとき
  //angleを変更する
  def mouseMoved( e: MouseEvent ){
    angle = List( e.getY()-200, -( e.getX()-200 ), 0 )
  }
  
  //マウスドラック
  //positionをへんこうする
  def mouseDragged( e: MouseEvent ){
    position = List( e.getX()-200, e.getY()-200, 0 )
  }

  //実行
  def run(){
    try {
      while( true ) {
        repaint
        Thread.sleep( 30 )
      }
    } catch {
      case e: InterruptedException =>
        println( e.getMessage )
    }
  }
  
}

そして、次ですが、
determinantWorld() や determinantZ()は、副作用があります。(angleや、positionなどの、varを直接内部で参照している)
*1

なので、副作用のない関数にします。
あと、コメントに書いたような細かいところ変更

import java.awt.{ List => _ , _}//scalaのPredefのListとかぶるので、こっちのListを除外
import java.awt.event._

object Draw{
  //行列
  type Matrix[A] = List[List[A]]
  
  val siz: List[ Double ] = List( 1, 1, 1 )
    
  val org: Matrix[ Double ] = List(
      List( -100, 100, -100, 1 ),
      List( 100, 100, -100, 1 ),
      List( 100, -100, -100, 1 ),
      List( -100, -100, -100, 1 ),
      List( -100, 100, 100, 1 ),
      List( 100, 100, 100, 1 ),
      List( 100, -100, 100, 1 ),
      List( -100, -100, 100, 1 )
  )

  val surfeces = List(
      List( 0, 1, 2, 3 ),
      List( 4, 5, 6, 7 ),
      List( 0, 4, 5, 1 ),
      List( 1, 5, 6, 2 ),
      List( 2, 6, 7, 3 ),
      List( 3, 7, 4, 0 )
  )
  
  val determinantSize: Matrix[ Double ] = List(
      List( siz(0), 0, 0, 0 ),
      List( 0, siz(1), 0, 0 ),
      List( 0, 0, siz(2), 0 ),
      List( 0, 0, 0, 1 )
  )
  
  //determinantWorldやdeterminantZは引数を1つ加えた
  
  def determinantWorld(vector :List[Double] ): Matrix[ Double ] = List(
    List( 1, 0, 0, 0 ),
    List( 0, 1, 0, 0 ),
    List( 0, 0, 1, 0 ),
    List( vector(0), vector(1), vector(2), 1 )
  )
  
  def determinantZ(vector :List[Double] ): Matrix[ Double ] = List(
    List( cos(vector(2)), sin(vector(2)), 0, 0 ),
    List( -sin(vector(2)), cos(vector(2)), 0, 0 ),
    List( 0, 0, 1, 0 ),
    List( 0, 0, 0, 1 )
  )
  
  def determinantY(vector :List[Double] ): Matrix[ Double ] = List(
    List( cos(vector(1)), 0, -sin(vector(1)), 0 ),
    List( 0, 1, 0, 0 ),
    List( sin(vector(1)), 0, cos(vector(1)), 0 ),
    List( 0, 0, 0, 1 )
  )
  
  def determinantX(vector :List[Double] ): Matrix[ Double ] = List(
    List( 1, 0, 0, 0 ),
    List( 0, cos(vector(0)), sin(vector(0)), 0 ),
    List( 0, -sin(vector(0)), cos(vector(0)), 0 ),
    List( 0, 0, 0, 1 )
  )

  def sin( d: Double ) = Math.sin( Math.toRadians( d ))
  def cos( d: Double ) = Math.cos( Math.toRadians( d ))
  
}

class Draw extends Frame with Runnable with MouseMotionListener {
  import Draw._ //コンパニオンオブジェクトのメンバーをインポート
  
  //初期化
  {
    setTitle( "Polygon" )
    setSize( 400, 400 )
    setBackground( Color.black )
    setVisible( true )
    addMouseMotionListener( this )
    addWindowListener( new WindowAdapter {
      override def windowClosing( e: WindowEvent ): Unit = {
        System.exit( 0 )
      }
    })
  }
  
  //angleとpositionが、ユーザーから受け取ったmouseの情報保持してる変数?
  var angle: List[ Double ] = List( 0.0, 0.0, 0.0 )
  var position: List[ Double ] = List( 0, 0, 0 )
  
  var eye: List[ Double ] = List( 0, 0, 400 )//eyeって一箇所で、しかも参照しかしてない?

  //全体として、描画処理時にpaintが呼ばれるわけで、他のほとんどのメソッドは、paintから呼ばれるだけなので、こんな感じにローカル関数にできる
  override def paint( g: Graphics ){
    
    def to2d(p3d: Matrix[ Double ]): Matrix[ Int ] = {
      p3d.map( i => ( 0 to 1 ).toList.map( idx => ( 200. + 320.*i( idx ) / ( eye(2) + i(2) ) ).toInt ) )
    }
    
    def affine( determinant: Matrix[ Double ], org: Matrix[ Double ] ): Matrix[ Double ] = {
      
      def fusion( a:List[ Double ], b:List[ Double ] ):Double = {
        ( 0 to 3 ).map( idx => a( idx ) * b( idx ) ).sum
      }
            
      val idx = ( 0 to 3 ).toList
      org.map( i => idx.map( j => fusion( i, determinant.transpose.apply( j ) )))
    }
    
    //インデントしても、どっちにしろ見づらいw
    //というか、ここを括り出して、べつにしたほうがいいか
    val p3d = affine(
            determinantX(angle),
            affine(
              determinantY(angle),
              affine(
                determinantZ(angle),
                affine(
                  determinantSize,
                  affine( determinantWorld(position), org )
                )
              )
            )
          )
          
    g.setColor(Color.white)
    for { i <- 0 to 7 }
      g.drawString( i.toString , to2d( p3d )( i )( 0 ), to2d( p3d )( i )( 1 ) )
      
  }

  //マウス動かしたとき
  //angleを変更する
  def mouseMoved( e: MouseEvent ){
    angle = List( e.getY()-200, -( e.getX()-200 ), 0 )
  }
  
  //マウスドラック
  //positionをへんこうする
  def mouseDragged( e: MouseEvent ){
    position = List( e.getX()-200, e.getY()-200, 0 )
  }

  //実行
  def run(){
    try {
      while( true ) {
        repaint
        Thread.sleep( 30 )
      }
    } catch {
      case e: InterruptedException =>
        println( e.getMessage )
    }
  }
  
}

さらに副作用なくしたりとか、共通部分くくりだしたりとか、インデントちょっと変更とか


import java.awt.{ List => _ , _}//scalaのPredefのListとかぶるので、こっちのListを除外
import java.awt.event._

object Draw{
  //行列
  type Matrix[A] = List[List[A]]
  
  val siz: List[ Double ] = List( 1, 1, 1 )
    
  //ここは拡張性考えると、Intをひとつ取る関数にしたほうがいいっぽい
  //100ってどこから出てきた数?
  val org: Matrix[ Double ] = List(
      List( -100, 100, -100, 1 ),
      List( 100, 100, -100, 1 ),
      List( 100, -100, -100, 1 ),
      List( -100, -100, -100, 1 ),
      List( -100, 100, 100, 1 ),
      List( 100, 100, 100, 1 ),
      List( 100, -100, 100, 1 ),
      List( -100, -100, 100, 1 )
  )

  val surfeces = List(
      List( 0, 1, 2, 3 ),
      List( 4, 5, 6, 7 ),
      List( 0, 4, 5, 1 ),
      List( 1, 5, 6, 2 ),
      List( 2, 6, 7, 3 ),
      List( 3, 7, 4, 0 )
  )
  
  val determinantSize: Matrix[ Double ] = List(
      List( siz(0), 0, 0, 0 ),
      List( 0, siz(1), 0, 0 ),
      List( 0, 0, siz(2), 0 ),
      List( 0, 0, 0, 1 )
  )
  
  
  def determinantWorld(vector :List[Double] ): Matrix[ Double ] = List(
    List( 1, 0, 0, 0 ),
    List( 0, 1, 0, 0 ),
    List( 0, 0, 1, 0 ),
    List( vector(0), vector(1), vector(2), 1 )
  )
  
  def determinantZ(vector :List[Double] ): Matrix[ Double ] = List(
    List( cos(vector(2)), sin(vector(2)), 0, 0 ),
    List( -sin(vector(2)), cos(vector(2)), 0, 0 ),
    List( 0, 0, 1, 0 ),
    List( 0, 0, 0, 1 )
  )
  
  def determinantY(vector :List[Double] ): Matrix[ Double ] = List(
    List( cos(vector(1)), 0, -sin(vector(1)), 0 ),
    List( 0, 1, 0, 0 ),
    List( sin(vector(1)), 0, cos(vector(1)), 0 ),
    List( 0, 0, 0, 1 )
  )
  
  def determinantX(vector :List[Double] ): Matrix[ Double ] = List(
    List( 1, 0, 0, 0 ),
    List( 0, cos(vector(0)), sin(vector(0)), 0 ),
    List( 0, -sin(vector(0)), cos(vector(0)), 0 ),
    List( 0, 0, 0, 1 )
  )

  def sin( d: Double ) = Math.sin( Math.toRadians( d ))
  def cos( d: Double ) = Math.cos( Math.toRadians( d ))
  
  def affine( determinant: Matrix[ Double ], org: Matrix[ Double ] ): Matrix[ Double ] = {
  
    def fusion( a:List[ Double ], b:List[ Double ] ):Double = {
      ( 0 to 3 ).map( idx => a( idx ) * b( idx ) ).sum
    }
        
    val idx = ( 0 to 3 ).toList
    org.map( i => idx.map( j => fusion( i, determinant.transpose.apply( j ) )))//勉強会でも話しがでたが、transpose使ってみた
  
  }
  
  def p3d(ang:List[ Double ],po:List[ Double ]):Matrix[Double] = {
    
    affine(
            determinantX(ang),
            affine(
              determinantY(ang),
              affine(
                determinantZ(ang),
                affine(
                  determinantSize,
                  affine( determinantWorld(po), org )//このorgは直接参照していいのか?これも引数に加えるべきなのか?
                )
              )
            )
          )
  }
}

class Draw extends Frame with Runnable with MouseMotionListener {
  import Draw._ //コンパニオンオブジェクトのメンバーをインポート
  
  //初期化
  {
    setTitle( "Polygon" )
    setSize( 400, 400 )
    setBackground( Color.black )
    setVisible( true )
    addMouseMotionListener( this )
    addWindowListener( new WindowAdapter {
      override def windowClosing( e: WindowEvent ): Unit = {
        System.exit( 0 )
      }
    })
  }
  
  //angleとpositionが、ユーザーから受け取ったmouseの情報保持してる変数?
  var angle: List[ Double ] = List( 0.0, 0.0, 0.0 )
  var position: List[ Double ] = List( 0, 0, 0 )
  
  override def paint( g: Graphics ){
    
    //0 to 1ってわかりづらいな・・・ その下のfor式のなかの、0と1が対応しているっぽい?
    def to2d(p3d: Matrix[ Double ]): Matrix[ Int ] = {
      p3d.map( i => 
        ( 0 to 1 ).toList.map( idx => 
          ( 200. + 320.*i( idx ) / ( 400 + i(2) ) ).toInt 
        ) 
      )
    }
    
    g.setColor(Color.white)
    
    //for式の中で、2回評価するの無駄なのでくくりだし。変数名適当。ひどいwww
    val tmp = to2d( p3d( angle , position ) )
    
    for{ i <- 0 to 7 
         j = tmp(i) //ここも同じことやってるので、くくりだした
      }{
        g.drawString( i.toString , j(0) , j(1) )
    }
  }

  //マウス動かしたとき
  //angleを変更する
  def mouseMoved( e: MouseEvent ){
    angle = List( e.getY()-200, -( e.getX()-200 ), 0 )//この辺の200ってどこから出てくるの?400の半分か?
  }
  
  //マウスドラック
  //positionをへんこうする
  def mouseDragged( e: MouseEvent ){
    position = List( e.getX()-200, e.getY()-200, 0 )
  }

  //実行
  def run(){
    try {
      while( true ) {
        repaint //repaintってpublicだから、そとから呼べるし、このクラスは、Runnable継承しないほうがいいのかな?
        Thread.sleep( 30 )
      }
    } catch {
      case e: InterruptedException =>
        println( e.getMessage )
    }
  }
  
}

あとは、ちゃんとアフィン変換とか、コードの意味調べないと、どこが可変なのかどうかわからないとこが・・・(´・ω・`)
(windowのサイズと、それに関連する値とか、マウスの動きの大きさにあわせてどれくらい回転するとかなどなど・・・)

一応、もとのコードと同じ動きをするのは確かめたつもりだけど、間違ってたら突っ込んでください。

とりあえずここまで。もしかしたらこの続きやるかも


さらに変更してみた↓
明らかな改良になってるところもあるけど、
単になんとなく違う書き方してみたとか、
パフォーマンスとか考えれば、むしろ改悪になってる部分もあるかもw
書き方による、読みさすさなんていうのも、人それぞれだしね

object Draw{
  
  /**
   * Tuple4のListを、Matrix(ListのListに変換)
   * 単にListって何度も書くと、見づらくなるから
   * @param tupleList 4つの要素をもつTuple4
   * @return 行列(行列の型変数は、必ず指定しないといけない)
   */
  def makeMatrix4[X](tupleList:Tuple4[_,_,_,_] *):Matrix[X] = {
    tupleList.map{ t => t.productElements.map{_.asInstanceOf[X]}.toList }.toList
    
    // [X,X<:A,X<:B,X<:C,X<:D](tupleList:Tuple4[A,B,C,D] *):Matrix[X]
    // ってしようかと思ったけど、IntとDoubleは、暗黙には変換されるけど、継承関係はないので、こんな感じで無理やりキャスト
    // しかしこれだと、実行時エラーになる可能性があるか?

  }

  //行列
  type Matrix[A] = List[List[A]]
  
  val siz: List[ Double ] = List( 1, 1, 1 )
    
  /**
   * ここは、Intをひとつ取る関数(から生成)したほうがいい?
   * 
   * それぞれの中のListが、点を表す?
   * この場合立方体の頂点?
   * 基準となる、x座標、y座標、z座標の位置
   * 
   * ここは、ListよりSetの方がいいか?
   * むしろそれぞれの点に、あえて名前つけたほうがいい?
   */
  val org = makeMatrix4[Double](
     ( -100,  100, -100, 1 ),
     (  100,  100, -100, 1 ),
     (  100, -100, -100, 1 ),
     ( -100, -100, -100, 1 ),
     ( -100,  100,  100, 1 ),
     (  100,  100,  100, 1 ),
     (  100, -100,  100, 1 ),
     ( -100, -100,  100, 1 )
  )

  /**
   * 拡大縮小。
   * 行列だけ用意されてるけど、実際ユーザーが、拡大縮小を行うための手段がなく、使ってない?
   */
  val determinantSize = makeMatrix4[Double](
      ( siz(0), 0, 0, 0 ),
      ( 0, siz(1), 0, 0 ),
      ( 0, 0, siz(2), 0 ),
      ( 0, 0, 0, 1 )
  )
  
  /** ワールド座標に平行移動?
   * @param vector 要素数3のベクトル
   * @return 4×4の行列
   */
  def determinantWorld(vector :List[Double] ): Matrix[ Double ] = makeMatrix4(
    ( 1, 0, 0, 0 ),
    ( 0, 1, 0, 0 ),
    ( 0, 0, 1, 0 ),
    ( vector(0), vector(1), vector(2), 1 )
  )
  
  /** Z軸中心に回転
   * @param vector 次元(dimension)は必ず3? しかも3番目しか使わない?
   * @return 4×4の行列
   */
  def determinantZ(vector :List[Double] ): Matrix[ Double ] = makeMatrix4(
    ( cos(vector(2)), sin(vector(2)), 0, 0 ),
    ( -sin(vector(2)), cos(vector(2)), 0, 0 ),
    ( 0, 0, 1, 0 ),
    ( 0, 0, 0, 1 )
  )
  
  
  /** Y軸中心に回転
   * @param vector 次元(dimension)は必ず3? しかも2番目しか使わない?
   * @return 4×4の行列
   */
  def determinantY(vector :List[Double] ): Matrix[ Double ] = makeMatrix4(
    ( cos(vector(1)), 0, -sin(vector(1)), 0 ),
    ( 0, 1, 0, 0 ),
    ( sin(vector(1)), 0, cos(vector(1)), 0 ),
    ( 0, 0, 0, 1 )
  )
  
  /** X軸中心に回転
   * @param vector
   * @return 4×4の行列
   */
  def determinantX(vector :List[Double] ): Matrix[ Double ] = makeMatrix4(
    ( 1, 0, 0, 0 ),
    ( 0, cos(vector(0)), sin(vector(0)), 0 ),
    ( 0, -sin(vector(0)), cos(vector(0)), 0 ),
    ( 0, 0, 0, 1 )
  )

  /**
   * @param d 角度
   * @return 計算したsin
   */
  def sin( d: Double ) = Math.sin( Math.toRadians( d ))

  /**
   * @param d 角度
   * @return 計算したcos
   */
  def cos( d: Double ) = Math.cos( Math.toRadians( d ))
  
  /** 2つの行列から1つの行列出力
   * @param determinant 4×4の行列
   * @param o 4×Nの行列。それぞれの行が、(x,y,zで表された)点の座標を表す?
   * @return 4×4の行列
   */
  def affine( determinant: Matrix[ Double ], o: Matrix[ Double ] ): Matrix[ Double ] = {
  
    /**
     * @param xs ベクトル
     * @param ys ベクトル
     * @return それぞれの値をかけて、さらにそれをすべて足した値
     */
    def fusion(xs:List[ Double ], ys:List[ Double ] ):Double = {
      assert( xs.size == ys.size , "ベクトルのサイズ違う")
      (xs,ys).zipped.map( _ + _ ).sum
    }

    //determinantをtransposeしたListのサイズから、算出できるようなので、
    //    ( 0 to 3 ).toList というのも消した

    o.map{ i =>
      determinant.transpose.map( fusion( i,  _  ) )
    }
  }
  
  /**
   * @param ang ?
   * @param po ?
   * @param o 変換前の点の座標のList
   * @return 変換後の点の座標を表すListのList
   */
  def p3d( ang:List[ Double ] , po:List[ Double ] , o:Matrix[Double] ):Matrix[Double] = {
    
    List(
      determinantWorld(po) ,determinantSize ,
      determinantZ(ang) ,determinantY(ang) ,determinantX(ang) 
    
    ).foldLeft(o){ affine( _ , _ ) } //affineを何度も適用してるので、一度List作ってから、foldLeft使ってみた

  }
}

//Runableと、MouseMotionListener実装するのやめた
class Draw extends Frame{
  import Draw._ //コンパニオンオブジェクトのメンバーをインポート
  
  //初期化。コンストラクタにするべき?
  {
    setTitle( "Polygon" )
    setSize( 400, 400 )
    setBackground( Color.black )
    setVisible( true )
    addMouseMotionListener( 
      //あまり意味ないけど、MouseMotionListenerも継承せずに、無名クラスにしてみた
        
      new MouseMotionListener{    
        //マウス動かしたとき。angleを変更する
        def mouseMoved( e: MouseEvent ){
          angle = List( e.getY()-200, -( e.getX()-200 ), 0 )//この辺の200ってどこから出てくるの?400の半分か?
        }
        
        //マウスドラック。positionを変更する
        def mouseDragged( e: MouseEvent ){
          position = List( e.getX()-200, e.getY()-200, 0 )
        }
        
      }
    )
    addWindowListener( new WindowAdapter {
      override def windowClosing( e: WindowEvent ) = System.exit( 0 )
    })
  }
  
  //angleとpositionが、ユーザーから受け取ったmouseの情報保持してる変数っぽい
  var angle: List[ Double ] = List( 0.0, 0.0, 0.0 )
  var position: List[ Double ] = List( 0, 0, 0 )
  
  override def paint( g: Graphics ){
    
    def to2d(p3d: Matrix[ Double ]): Matrix[ Int ] = {
      p3d.map{ i => 
        ( 0 to 1 ).toList.map( idx => 
          ( 200. + 320.*i( idx ) / ( 400 + i(2) ) ).toInt //ここの200と320と400って何表してる数?
        ) 
      }
    }
    
    g.setColor(Color.white)
    
    val tmp = to2d( p3d( angle , position , org ) )
    
    for{//0 to 7 っていうのは、立方体の頂点の数みたいだから、zipWithIndex使って、Matrixの行数から算出
       (j,i) <- tmp.zipWithIndex
      }{
      g.drawString( i.toString , j(0) , j(1) )
    }
  }

}

*1:そういえば、副作用があるものには、(引数0でも)カッコを付ける慣習があるので、最初なかったけどつけてます