Wednesday, March 2, 2011

SVG Fractal with Scala 3 : The Koch snowflakes

Result
Source Code
Following is a straight forward implementation pop up my mind.  I first, build a list of "angles" which defines direction from current point to the next point.  When it goes horizontally to the right, angle is 0, 90 for vertically up, 180 for horizontally left, 270 for vertically down and so on.  Start point is the peak of the triangle.  First 2 cases go like this.
  • Level 0 => List ( 300, 180,  60)

  • Level 1 => List ( 300, 0, 240, 300, 180, 240, 120, 180, 60, 120, 0, 60)


To get the list for level n form level n-1, we can do the following.
(level n-1 list).flatMap(x => List(x,x+60,x+300,x)).map(_%360)
Rest of the things I need to is to generate a list of (x,y) points, translate it into a string and put the string in  <pack><path d="M ... z"/></pack> statements.


class Koch (width: Double, level:Int) extends Fractal(width,(width*3*1.732/4+1).toInt) { 
 
 val scale = (width-20)/(2*math.pow(3,level))
 val level_0 = List(300,180,60)
 def recursive(current:List[Int], currlevel:Int): List[Int] = {
  if(currlevel == level)
  {
   current
  } else {
   recursive(current.flatMap(x => List(x,x+60,x+300,x)).map(_%360), currlevel+1)
  }
 }
 
 def angles2points(alist:List[Int]): List[(Double,Double)] = 
  (width/2,20.0) :: iter((width/2,20.0), alist, List())
  
 def iter(org:(Double,Double), alist:List[Int], plist:List[(Double,Double)])
 : List[(Double,Double)] = org match {
  case (x,y) => alist match {
   case a::as => 
    val next = a match {
     case 0 => (x+2*scale, y)
     case 60 => (x+scale, y-1.732*scale)
     case 120 => (x-scale, y-1.732*scale)
     case 180 => (x-2*scale, y)
     case 240 => (x-scale, y+1.732*scale)
     case 300 => (x+scale, y+1.732*scale)
    }
    iter(next, as, plist ++ List((next)))
   case _ => plist
  }
 }
 
 def points2string(plist:List[(Double,Double)]) : String = plist match {
  case (x,y)::ps => x.toString + "," + y.toString + " " + points2string(ps)
  case _ => ""
 }
 override def body = 
  <pack>
   <path stroke="blue" fill ="none"
    d= {"M"+ points2string(angles2points(recursive(level_0,0))) + "z"} />
  </pack>>
}

My concern with this code, from the beginning, was I might be using too much memories.  No wonder, I got OutOfMemoryError for level 6.

scala> val k = new Koch(400,6)
k: Koch = Koch@28fd3fba

scala> k.draw("koch.svg")     
java.lang.OutOfMemoryError: Java heap space
at scala.collection.mutable.ListBuffer.$plus$eq(ListBuffer.scala:119)
at scala.collection.mutable.ListBuffer.$plus$eq(ListBuffer.scala:42)
at scala.collection.generic.Growable$$anonfun$$plus$plus$eq$1.apply(Growable.scala:48)
at scala.collection.generic.Growable$$anonfun$$plus$plus$eq$1.apply(Growable.scala:48)
at scala.collection.LinearSeqOptimized$class.foreach(LinearSeqOptimized.scala:61)
at scala.collection.immutable.List.foreach(List.scala:45)
at scala.collection.generic.Growable$class.$plus$plus$eq(Growable.scala:48)
at scala.collection.immutable.List.$colon$colon$colon(List.scala:78)
at scala.collection.immutable.List.$plus$plus(List.scala:139)
at Koch.iter(:39)
at Koch.iter(:39)
at Koch.iter(:39)
...
scala> 

I guess I can write a point to file one by one as it being generated instead of preparing all the points in a list before writing them to the file all at once.  That way, I think, I can reduce the memory space and potentially generate any level of snowflakes.  But, doing that in obvious way, manually, normal way, whatever, does not sound so exciting.  I wonder if I could use lazy evaluation to achieve that, somehow.
That might be a next topic.  

Monday, February 28, 2011

SVG Fractal with Scala 2 : The Sierpinski gasket

Result

Source Code - first part

import java.io.FileWriter
import java.io.BufferedWriter

abstract class Fractal(width: Double, height: Double) {

 val head = """<?xml version="1.0" standalone="no" ?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">"""

 def body: xml.Elem 

 def root = body match {
  case <pack>{inner @ _*}</pack> =>
  <svg width={width.toString} height={height.toString}
   xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink">
   {inner}
  </svg>
 }

 def draw(filename: String): Unit = {
  val fw = new BufferedWriter(new FileWriter(filename))
  fw.write(head)
  fw.newLine()
  fw.write(root.toString)
  fw.close()
 }
}

This is an abstract class which provides draw() function to its descendants.  body() should generate <pack> figure </pack> form of XML code and root() changes it to <svn> figure </svn>.  And, then, draw() write it to a file.

Things I am so sure
  • Is this the best way to writing to the file?  Doesn't Scala really have its own write functions?
  • Maybe I should use Traits, whatever it is, rather than abstract class.  That might be more Scala way?
  • I defined head as a String because defining it as a xml.Elem gave me an error at <!DOCTYPE.  Compiler said it did not expected ! after <.  But, is this really?  Isn't <! kind a common in XML?  
  • I use following to substitute <body> with <scala>.  Is this, again, a best practice?
    def root = body match {
        case <pack>{inner @ _*}</pack> =>
        <svg>{inner} </svg>
        }
Source Code - second part

class Triangle(width: Double, level:Int) extends 
      Fractal(width,(width*1.732/2+1).toInt) {
 
    val scale = width/2 - 20
    val base = <pack><path id="level_0" fill="blue" 
                          d="M0,0 2,0 1,1.732 z"/></pack>
 
    def recursive(current:xml.Elem, currlevel:Int): xml.Elem = {
  if(currlevel == level)
  {
   current match {
    case <pack>{in @ _*}</pack> =>
    <pack>
    <g transform={"translate(20,20) scale("+scale+")"}>
     {in}
    </g>
    </pack>
   }
  } else {
   val next = current match {
    case <pack>{in @ _*}</pack> =>
    <pack>
    <g transform="translate(0,0) scale(0.5)">
     {in}
    </g>
    <g transform="translate(1,0) scale(0.5)">
     {in}
    </g>
    <g transform="translate(0.5,0.866) scale(0.5)">
     {in}
    </g>
    </pack>
   }
   recursive(next, currlevel+1)
  }
 }

 override def body = recursive(base, 0)
}

This is the real generator class of the Sierpinski gasket.  Take figure width and recursive level as parameters and draw() output a SVG file.  Basic idea is that at every recursive process, current level of figure is copied 3 times, shrink 50%, and then located to the position so that 3 of them combined form a big triangle.

Observations

  • My Macbook Pro runs this script up to level 11.  Level 12 gives me a stack over flow (Scala 2.8.1.final Interpreter).
  • I first wrote a code which generates SVG code just like this web page .  But, obviously, these style requires more processing power when drawing.  My Chrome gave up at level 8 and showed this.

Sunday, February 27, 2011

SVG Fractals with Scara

This is my first blogging ever.  I started learning a programming language called Scala a couple of weeks ago.  I thought it was a good timing for me to start blogging to keep track of my progress on learning and give some motivations at the same time.   But, gee, I even do not know how to put my source code here, not to mention about SVG.  Well, basically, I am learning two things at once, please be patient.

References
Following great URLs helped me a lot.

  • The Beauty of SVG
  • Free tutorials for graphics  Great tutorials for Postscript and SVG (in Japanese)
  • NikkeiBP article on Scala Great introduction to Scala (in Japanese)
    • Although my native tang is Japanese, I usually find tutorials in English better to understand.   I went through Haskell and Schema before, and they have very good English tutorials.  Somehow, however, it worked differently for me with Scala...
Goal
Write a scala program, or script?, which outputs a SVG file of a fractal diagam such as Koch snowflake, or this.
I wrote this triangle as SVG and converted it into PNG with Incscape.  I fond this post and tried, but did not work with my file.  I am pretty sure that that is my SVG file that is not following the convention strictly enough that his program works.  But, since Chrome and Firefox can show the picture, at least in my environment, I am satisfied with my SVG, at least for now...

Well, that it for today.  I wil post my source code next time (tomorrow, or next weekend, or might be tonight).