Saturday, August 10, 2013 / SVG, Groovy

Groovy で SVG を出力して、六角形のフラクタル図形を書いた

fractal-hexagons

少し長いのですが、ここにメモしておきます。

// --- svg --------------------------------------------------------------------------

class SvgUtils {
    def createXYWH = { bounds->
        def sb = ''<<''
        sb.append("x=\"").append( bounds.x ).append("\" ")
        sb.append("y=\"").append( bounds.y ).append("\" ")
        sb.append("width=\"").append( bounds.width ).append("\" ")
        sb.append("height=\"").append( bounds.height ).append("\" ")
        sb.toString()
    }

    def createViewBox = { bounds->
        def sb = ''<<''
        sb.append("viewBox=\"").append( bounds.x ).append(" ").append( bounds.y ).append(" ").append( bounds.width ).append(" ").append( bounds.height ).append("\"");
        sb.toString()
    }

    def createSVGHeader = { viewBoxBounds1,viewBoxBounds2->
        def br = System.getProperty('line.separator')

        def sb = ''<<''
        sb << "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
        sb << br
        sb << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">"
        sb << br
        sb << "<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" "
        sb << br
        sb << createXYWH(viewBoxBounds1)
        sb << " "
        sb << createViewBox(viewBoxBounds2)
        sb << ">"
        sb << "<g style=\"stroke:rgb(0,0,0)\" stroke-width=\"1\" fill=\"black\">"
        sb << "<rect x=\"${viewBoxBounds2.x}\" y=\"${viewBoxBounds2.y}\" width=\"${viewBoxBounds2.width}\" height=\"${viewBoxBounds2.height}\"/>"
        sb << "</g>"
        sb.toString()
    }

    def createSVGFooter = {
        '</svg>'
    }
}

class AwtUtils {
    def createRandomValue(){
        int max = 255
        Random random = new Random()
        int i = Math.abs(random.nextInt() % max)
        i
    }
    def createRandomColor(){
        int r = createRandomValue()
        int g = createRandomValue()
        int b = createRandomValue()
        [red:r,green:g,blue:b]
    }

    def createPoint = {x,y-> [x:x,y:y] }
    def createRectangle = { x,y,width,height-> [x:x,y:y,width:width,height:height] }

    def centerPoint = { rectangle->
        def x = rectangle.x + rectangle.width/2f
        def y = rectangle.y + rectangle.height/2f
        createPoint(x,y)
    }

    // calc each vertex of a honeycomb 
    def createPoints = { rectangle->
        def centerPt = centerPoint(rectangle) 

        def vertexes = 6
        def radius=Math.min(rectangle.width,rectangle.height)/2.0f
        def angle = 2 * Math.PI/vertexes

        def pointList = []
        (0..(vertexes-1)).each{ 
            def x = radius*Math.sin(angle*it)
            def y = radius*Math.cos(angle*it)*(-1)

            // translate
            x = x + centerPt.x
            y = y + centerPt.y

            pointList.add( createPoint(x,y) ) 
        }
        pointList
    }
}


// --- honeycomb -------------------------------------------------------------------------

class HoneycombsMaker {
    def sb
    def awtUtils
    HoneycombsMaker(){
        sb = ''<<''
        awtUtils=new AwtUtils()
    }

    // 指定した点を中心に半径r の(円に入る)六角形を描写 色は c で指定
    def void createHoneycomb( Map centerPt,float r, Map c,int loopCnt ){
        def myCreatePoints = { left,top,right,bottom->
            def x = left
            def y = top
            def w = right -left
            def h = bottom -top
            def points = awtUtils.createPoints( awtUtils.createRectangle( x,y,w,h ) )
            points
        }

        loopCnt = loopCnt+1

        def myR = r*0.42f

        def left   = centerPt.x -myR
        def top    = centerPt.y -myR
        def right  = centerPt.x +myR
        def bottom = centerPt.y +myR

        sb << "<g style=\"stroke:rgb(${c.red},${c.green},${c.blue})\" stroke-width=\"1\" fill=\"none\">"
        sb << "<path d=\""

        myCreatePoints(left,top,right,bottom).eachWithIndex{ pt,index->
            if( index==0 ) sb << "M${pt.x},${pt.y}"
            else sb << "L${pt.x},${pt.y}"
        }
        sb << ' z"/>' // z ... close path
        sb << '</g>'


        println loopCnt
        if( loopCnt<7 ){
            def left2   = centerPt.x -r
            def top2    = centerPt.y -r
            def right2  = centerPt.x +r
            def bottom2 = centerPt.y +r

            def c2 = awtUtils.createRandomColor()
            myCreatePoints(left2,top2,right2,bottom2).each { pt->
                sb << createHoneycomb( [x:pt.x,y:pt.y],(r*0.32f as float),c2,loopCnt )
            }
        }
    }    
}


def canvasW = 1000*1
def canvasH = 1000*1

def svgUtils = new SvgUtils()
def awtUtils = new AwtUtils()

def paperRect = awtUtils.createRectangle( 0,0,canvasW,canvasH )
def viewBoxRect = awtUtils.createRectangle( 0,0,canvasW,canvasH )

def br = System.getProperty('line.separator')
def sb = ''<<''
sb << svgUtils.createSVGHeader( paperRect,viewBoxRect )
sb << br

def c = awtUtils.createRandomColor()

def maker = new HoneycombsMaker()
maker.createHoneycomb( [x:canvasW/2f,y:canvasH/2f],200,c,1 )
sb << maker.sb.toString() 
sb << br
sb << svgUtils.createSVGFooter()
new File('r.svg').text = sb.toString()