; Fibonacci Spiral, V1.0
;
; AUTHOR: theilr (http://flickr.com/photos/theilr), (c) 2009
;
; This script was tested with GIMP 2.6.7
;
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License Version 3 as
; published by the Free Software Foundation.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License at http://www.gnu.org/licenses for
; more details.
;
; DESCRIPTION:
;
; Converts a square or rectangular image into a Fibonacci spiral
; (similar to a "golden spiral" -- the fine distinctions are discussed
; under the wikipedia entry on Golden spiral
; [http://en.wikipedia.org/wiki/Golden_spiral]) by making smaller
; copies of the image, rotating them, and arranging them into a spiral
; tiling. If the initial image is square, then the tiling has no
; overlap; for a rectangular image, the user can specify a blending mode
; for how the overlap is treated.
;
; Works best if the image has a Fibonacci number of pixels as width
; and height (and this is especially true if the original image is a
; square). The user can toggle an automatic rescaling to the nearest
; Fibonacci length (smaller than or equal to the original size).
;
; Located in menu " / Filters / theilr / Fibonacci Spiral"
;
; USAGE NOTES:
;
; The user can specify the angle (in 90 degree increments, measured
; clockwise) that the square is turned with each reduction in size.
; To get the spiral effect, you'll probably want to use the default
; value of 90, or possibly 270.
;
; The user can specify the quadrant where the center of the spiral
; well end up. Default is lower right.
;
; Much of the rescaling, etc, is based on image size, not layer size.
; So if canvas and current layer are not the same size, there's no
; telling what will actually happen.
;
; If a there is a selection, it is ignored.
;
; Each rescaled rectangle/square is in a different layer, so you'll
; probably want to follow up by merging all layers.
;
; BUGS:
; Although this permits various blending modes and opacities, I
; find I almost always prefer normal mode and 100-percent opacity.
; In other words, you might as well be working with square images.
;
; There's no good way to put borders around each of the squares that
; goes into the spiral; eg, if you give the original square a dark
; border, it gets lost in the cubic rescaling after just a few
; generations.
;
; SCRIPT SUMMARY:
; Prepare Image by making it Golden Landscape Rectangle
; Flip/flop image, if needed, so that spiral center
; ends up in desired quadrant
; Rotate, if necessary, so image is landscape
; Optionally rescale the image to have Fibonacci width and height
; If square, make into a landscape-mode golden rectangle
;
; Loop:
; Copy layer, then:
; reduce size by factor of golden ratio[*]
; possibly rotate layer
; move layer to appropriate position on the spiral
; Repeat until there's no more room.
;
; Un-rotate, and Un-flip/flop
;
; [*] note, this is not done by multiplying or dividing, but
; by continually subtacting short from long to get shorter,
; until the shorter is zero (or less). then you stop.
;
; Version 1.0
; ==========================================================================
;; this function is invoked if the user toggles "Rescale to Fibonacci"
(define (rescale-to-fibonacci inImage)
(let*
( ;define local variables
(theWidth (car (gimp-image-width inImage)))
(theHeight (car (gimp-image-height inImage)))
(currentSize 1)
(nextSize 1)
(tmpSize)
(theLongSize (max theWidth theHeight))
)
;; start generating Fibonacci numbers
;; until theLongSize is exceeded -- then scale back
;; by one Fibonacci number
(while (<= nextSize theLongSize)
(set! tmpSize nextSize)
(set! nextSize (+ currentSize nextSize))
(set! currentSize tmpSize)
)
(if (= theWidth theHeight)
(gimp-image-scale inImage currentSize currentSize)
(if (> theWidth theHeight)
(gimp-image-scale inImage currentSize (- nextSize currentSize))
(gimp-image-scale inImage (- nextSize currentSize) currentSize)
)
)
);; end let*
);; end define rescale-to-fibonacci
(define (flip-image-for-orientation inImage inLocateCenter)
;; Flip as appropriate to put spiral center in desired corner
(if (or (= inLocateCenter 1) (= inLocateCenter 2))
(gimp-image-flip inImage ORIENTATION-HORIZONTAL)
)
(if (or (= inLocateCenter 2) (= inLocateCenter 3))
(gimp-image-flip inImage ORIENTATION-VERTICAL)
)
);; end define flip-image-for-orientation
(define (script-fu-fibonacci-spiral inImage inLayer
inRescaleFibonacci
inLocateCenter
inRotate
inOpacity
inBlendMode)
(gimp-image-undo-group-start inImage)
(let*
( ;define local variables
(theWidth (car (gimp-image-width inImage)))
(theHeight (car (gimp-image-height inImage)))
(g (/ (+ (sqrt 5) 1) 2)) ;; Golden Ratio: 1.6180339887498949
(nextSize)
(currentSize)
(tmpSize)
(x 0)
(y 0)
(count 0)
(rotateAngle (+ 1 inRotate))
(cpyLayer)
(portraitMode FALSE)
)
;; clear any selections
(gimp-selection-none inImage)
;; determine if portrait or landscape
(if (> theHeight theWidth)
(set! portraitMode TRUE)
)
;; in portrait mode, the notion of upperleft, etc, is altered
;; 0 <-> 3, 1<->2
(if (= portraitMode TRUE)
(if (= (modulo inLocateCenter 2) 0)
(set! inLocateCenter (- inLocateCenter 1));; -1 if even
(set! inLocateCenter (+ inLocateCenter 1));; +1 if odd
)
)
(set! inLocateCenter (modulo (+ inLocateCenter 4) 4))
;; Do appropriate flipping to get spiral center in
;; the right place. We will unflip (which is the
;; same operation) at the end of the process.
(flip-image-for-orientation inImage inLocateCenter)
;; if portraitMode, then rotate to landscape
;; Ensure theWidth >= theHeight
(if (= portraitMode TRUE)
(gimp-image-rotate inImage ROTATE-90)
)
;; if toggle set, then reset image size so width and height
;; are Fibonacci numbers
(if (= inRescaleFibonacci TRUE)
(rescale-to-fibonacci inImage)
)
;; Recompute width and height, since the image has possibly
;; been rescaled and/or rotated at this point
(set! theWidth (car (gimp-image-width inImage)))
(set! theHeight (car (gimp-image-height inImage)))
;; If it's square, then add some empty width (to layer AND canvas)
;; To make it a golden rectangle.
(if (= theHeight theWidth)
(begin
(set! theWidth (round (* theHeight g)))
(gimp-layer-resize inLayer theWidth theHeight 0 0)
(gimp-image-resize-to-layers inImage)
)
)
;; By now, theWidth > theHeight, so
(set! nextSize theHeight) ;; shorter dimension
(set! currentSize theWidth) ;; longer dimension
;; Now start making scaled-down copies as new layers
(while (> (- currentSize nextSize) 0)
;; Compute next Fibonacci numbers ... backward
(set! tmpSize nextSize)
(set! nextSize (- currentSize nextSize))
(set! currentSize tmpSize)
;; Count iterations
(set! count (+ count 1))
;; Make new layer as copy of original layer
(set! cpyLayer (car (gimp-layer-copy inLayer TRUE))) ;copy of layer
(gimp-image-add-layer inImage cpyLayer -1)
(gimp-layer-set-mode cpyLayer inBlendMode)
(gimp-layer-set-opacity cpyLayer inOpacity)
;; Rescale new layer to appropriate size
(if (= (modulo rotateAngle 2) 1)
(gimp-layer-scale-full cpyLayer currentSize nextSize
TRUE INTERPOLATION-CUBIC)
(if (= (modulo count 2) 1)
(gimp-layer-scale-full cpyLayer nextSize currentSize
TRUE INTERPOLATION-CUBIC)
(gimp-layer-scale-full cpyLayer currentSize nextSize
TRUE INTERPOLATION-CUBIC)
)
)
;; Rotate new layer by the appropriate angle
(plug-in-rotate RUN-NONINTERACTIVE inImage cpyLayer
(* rotateAngle count) FALSE)
;; Compute and apply offsets
(if (= (modulo count 4) 1)
(set! x (+ x currentSize))
)
(if (= (modulo count 4) 2)
(set! y (+ y currentSize))
)
(gimp-layer-set-offsets cpyLayer x y)
);; endwhile
;; if we started out in portraitMode (theHeight > theWidth),
;; then rotate the whole image back to portraitMode
(if (= portraitMode TRUE)
(gimp-image-rotate inImage ROTATE-270)
)
;; Un-Flip as appropriate to put spiral center in desired corner
(flip-image-for-orientation inImage inLocateCenter)
);; end let*
(gimp-displays-flush)
(gimp-image-undo-group-end inImage)
);; end define
(script-fu-register "script-fu-fibonacci-spiral"
"/Filters/_theilr/_Fibonacci Spiral"
"Multiple smaller copies of image arranged in a spiral"
"theilr"
"(c) theilr"
"Dec 2009"
"RGB*"
SF-IMAGE "Image" 0
SF-DRAWABLE "Drawable" 0
SF-TOGGLE "Rescale to Fibonacci" FALSE
SF-OPTION "Where to put spiral center"
'("Lower Right"
"Lower Left"
"Upper Left"
"Upper Right")
SF-OPTION "Rotate with each smaller square"
'("90" "180" "270" "360")
SF-ADJUSTMENT "Opacity" '(100 0 100 1 10 0 SF-SLIDER)
SF-OPTION "Blending mode:"
'("NORMAL"
"DISSOLVE"
"BEHIND"
"MULTIPLY"
"SCREEN"
"OVERLAY"
"DIFFERENCE"
"ADDITION"
"SUBTRACT"
"DARKEN-ONLY"
"LIGHTEN-ONLY"
"HUE"
"SATURATION"
"COLOR"
"VALUE"
"DIVIDE"
"DODGE"
"BURN"
"HARDLIGHT"
"SOFTLIGHT"
"GRAIN-EXTRACT"
"GRAIN-MERGE"
"COLOR-ERASE"
"ERASE"
"REPLACE"
"ANTI-ERASE")
)