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.

No comments:

Post a Comment