I once found myself discussing in some forums about conversion of many pictures from one format to another. I knew that many image processors can work in batch mode. The first image processor I thaught of was GIMP. As expected, you can get help on this tool when run from the command-line with the option ‘–help’. In linux you can use ‘man gimp” to get more details about the tool. I found that ‘-b’ is the option to use to run GIMP in batch mode, and that the default scripting language is Script-Fu. Now, what is Script-Fu? From the menu bar: “Help -> GIMP-Online -> User Manual Web Site” leads to the following page:
Now, choose your language and version, and go to the chapter about scripting. Choose section ‘Using Script-Fu Scripts’. This language is based on Scheme, a language very similar to list. To work with Script Fu, choose ‘from the menu bar “Filters->Script-Fu”.
To load an image, use the function ‘gimp-file-load’.
Ti save a file, use the function ‘gimp-file-save’
.Well, this is not the tool I would use to convert image files.
I decided to challenge myself in drawing the Koch-Snowflake in Script-Fu. The following picture is what the fractal looks like:
Now, how to draw this fractal? Simply draw an equilateral triangle, and follow the following steps until you no longer have a polygon:
- Split each side of the polygon into three sections equal in length.
- Replace the middle section by two sides of an equilateral triangle, pointing outwards.
Now, how to do it with Script-Fu?
Copy the draw-line function from here.
define a function to draw the fractal, with an inner function to draw a side (origianlly, a side of a triangle).
This is the code:
(define (sqr x) (* x x)) (define (draw-line layer x-from y-from x-to y-to) (let* ( (points (cons-array 4 'double)) ) (aset points 0 x-from) (aset points 1 y-from) (aset points 2 x-to ) (aset points 3 y-to ) (gimp-pencil layer 4 points) ) ) (define (draw-koch-snowflake layer height width) (let* ( ; sin60, cos60 will be used for rotating lines 60 degrees (sin60 (/ (sqrt 3) 2)) ; sqrt(3)/2 - No operator precedence in LISP/Scheme (cos60 0.5) (min-size (min height width)) (side-length (* sin60 min-size)) ) (define (draw-side layer from-x from-y to-x to-y) (let* ( (diff-x (- to-x from-x)) (diff-y (- to-y from-y)) (third-diff-x (/ diff-x 3)) (third-diff-y (/ diff-y 3)) (squared-length (+ (sqr diff-x) (sqr diff-y))) ) (if (<= squared-length 1) ;; The (squared) distance is less than or equal to 1 pixel? (draw-line layer from-x from-y to-x to-y) ;; Yes, just draw the line (begin ;; No, recursively split the side and draw it. ;; recursively draw the 1st section (define end-section-1-x (+ from-x third-diff-x)) (define end-section-1-y (+ from-y third-diff-y)) (draw-side layer from-x from-y end-section-1-x end-section-1-y) ;; create a new vertex, the 2nd section is replaced by 2 sides ;; of an equilateral triangle ;; This is how you rotate a vector 60 degrees counterclockwise. (define new-vertex-x (+ end-section-1-x (* cos60 third-diff-x) (* sin60 third-diff-y)) ); define new-vertex-y (define new-vertex-y (+ end-section-1-y (* (- sin60) third-diff-x) (* cos60 third-diff-y)) ); define new-vertex-y (draw-side layer end-section-1-x end-section-1-y new-vertex-x new-vertex-y) ;; recursively draw the last section ;; and the section from the new vertex to the beginning of ;; the last section. (define start-section-3x (- to-x third-diff-x)) (define start-section-3y (- to-y third-diff-y)) (draw-side layer new-vertex-x new-vertex-y start-section-3x start-section-3y) (draw-side layer start-section-3x start-section-3y to-x to-y) ) ; End of command block );if );let* in draw-side ); define draw-side (define first-vertex-x (/ (- width side-length) 2)) (define first-vertex-y (- (- height (* side-length sin60)) (/ (- height min-size) 2))) (define start-x first-vertex-x) (define start-y first-vertex-y) (define end-x (+ start-x side-length)) (define end-y start-y) (draw-side layer start-x start-y end-x end-y) (set! start-x end-x) (set! start-y end-y) (set! end-x (- end-x (* cos60 side-length))) (set! end-y (+ end-y (* sin60 side-length))) (draw-side layer start-x start-y end-x end-y) (set! start-x end-x) (set! start-y end-y) (set! end-x first-vertex-x) (set! end-y first-vertex-y) (draw-side layer start-x start-y end-x end-y) ); let* in draw-koch-snowflake )
; The caller function:
(define (koch-caller) (let* ( (my-image (car (gimp-image-new 600 600 RGB))) (my-layer (car (gimp-layer-new my-image 600 600 RGB-IMAGE "my layer" 100 NORMAL))) ;; You add a layer because you cannot draw directly on the image object. ) (gimp-image-add-layer my-image my-layer -1) ; Add the layer to the image (gimp-context-set-background '(255 255 255)) ; White background (gimp-context-set-foreground '(000 000 000)) ; Black foreground (gimp-context-set-opacity 100) ; So you can see the picture (gimp-drawable-fill my-layer BACKGROUND-FILL) ; First, fill the layer with background color. (gimp-brushes-set-brush "Circle (01)") ; The thinnest line. (draw-koch-snowflake my-layer 600 600) ; Draw a 600 X 600 snowflake (gimp-display-new my-image) ; Physically display the result ) ) Now, to better understand how to rotate a vector, refer to this Wikipedia entry.