; step-transform.scm:
;
; Transform selection / layer with a combination of scaling, rotation and offset,
; optionally merge with a copy of another layer above and / or a layer below, -
; each step in a new layer, or separate file (by extension).
;
; The optional filename is required to end in a period followed by exactly
; three characters to identify the file format.  Frame numbers are then added
; between name and extension.
;
; This is free software, with NO warranty at all.
; See the GNU General Public License for details:
; http://www.gnu.org/copyleft/gpl.html
;
; Casper Maarbjerg,  http://www.nyx.net/~cmaarbj/
; December 1998
;
(define (script-fu-step-transform image src fgnd fg-on bgnd bg-on
                                  steps scale degrees x-offset y-offset wrap filename)

(let*
  ((new 0)
   (next 0)
   (selection 0)
   (newfile "")
   (basename filename)
   (ext filename)
   (base-length 0)
   (x-adj 0)
   (y-adj 0)
   (x-offs x-offset)
   (y-offs y-offset)
   (step-scale 1.0)
   (name (car (gimp-layer-get-name src)))
   (type (car (gimp-image-base-type image)))
   (mode (car (gimp-layer-get-mode src)))
   (fg-image (car (gimp-drawable-image fgnd)))
   (bg-image (car (gimp-drawable-image bgnd)))
   (opacity (car (gimp-layer-get-opacity src)))
   (comp-opacity (car (gimp-layer-get-opacity fgnd)))
   (comp-mode (car (gimp-layer-get-mode fgnd)))
   (height (car (gimp-image-height image)))
   (width (car (gimp-image-width image)))
   (radians (* 6.28318530717959 (/ degrees 360.0)))
   (rot radians))

  (gimp-image-disable-undo image)
  (if (= (car (gimp-selection-is-empty image)) TRUE)
    (gimp-selection-all image)
    (begin
      (set! selection (car (gimp-selection-save image)))
      (gimp-image-set-active-layer image src)))

  (define (fit-layer n)  ; to restore image dimensions and align
    (set! x-adj (car (gimp-drawable-offsets n)))
    (set! y-adj (cadr (gimp-drawable-offsets n)))
    (gimp-layer-resize n width height x-adj y-adj))

  (if (> (length filename) 4)
    (begin
      (set! base-length (length filename))  ; split filename 4 characters from end
      (set! ext (substring filename  (- base-length 4) base-length))
      (set! basename (substring filename 0 (- base-length 3)))
      (define (save-frame n)
        (fit-layer new)
        (set! newfile (string-append basename (number->string n) ext)) ; compose filename
        (gimp-file-save 1 image new newfile newfile)
        (gimp-image-free-shadow image)))
  ; for layered animations, replace works better than combine
    (define (save-frame n)
      (gimp-layer-set-name new (string-append name " " (number->string n) " (replace)"))))

  (define (set-visible state)
  (let* ((layers (gimp-image-get-layers image))
         (layer-count (car layers)))
    (set! layers (cadr layers))
    (while (> layer-count 0)
      (set! layer-count (- layer-count 1))
      (gimp-layer-set-visible (aref layers layer-count) state))))

  (if (or (> bg-on 0) (> fg-on 0))
    (set-visible FALSE))

  (if (= bg-on 0)
    (define (add-bgnd) '())
    (define (add-bgnd)
      (gimp-selection-none image)
      (gimp-edit-copy bg-image bgnd)
      (set! comp (car (gimp-edit-paste image new 0))) ; 0 = clear selection
      (gimp-floating-sel-to-layer comp)
      (gimp-image-lower-layer image comp)))  ; put background behind

  (if (= fg-on 0)
    (define (add-fgnd) '())
    (define (add-fgnd)
      (gimp-selection-none image)
      (gimp-edit-copy fg-image fgnd)
      (set! comp (car (gimp-edit-paste image new 0)))
      (gimp-floating-sel-to-layer comp)))

  (if (= (+ bg-on fg-on) 0)
    (define (do-merge) '())
    (define (do-merge)
      (gimp-layer-set-mode comp comp-mode)
      (gimp-layer-set-opacity comp comp-opacity)
      (gimp-layer-set-mode new mode)
      (gimp-layer-set-opacity new opacity)
      (plug-in-align-layers image new width height 1 1) ; link-after-alignment use-bottom
      (set! new (car (gimp-image-merge-visible-layers image CLIP-TO-IMAGE)))
      (gimp-layer-set-visible new FALSE)))

  (if (or (= degrees 0.0) (= degrees 360.0))
    (define (do-rotate) '())
    (define (do-rotate)
      (set! new (car (gimp-rotate image new TRUE rot))) ; TRUE to enable interpolation
      (if (> (car (gimp-layer-is-floating-sel new)) 0)
        (gimp-floating-sel-to-layer new))
      (set! rot (+ rot radians))
      (fit-layer new)))

  (if (and (= x-offs 0) (= y-offs 0))
    (define (do-offset) '())
    (define (do-offset)
      (if (> (car (gimp-layer-is-floating-sel new)) 0)
        (gimp-floating-sel-to-layer new))
      (gimp-selection-none image)
      (gimp-channel-ops-offset image new wrap 1 x-offs y-offs) ; 1 = transparent fill
      (set! x-offs (+ x-offs x-offset))
      (set! y-offs (+ y-offs y-offset))))

  (if (or (= scale 1.0) (= scale 0.0))
    (define (do-scale) '())
    (define (do-scale)
      (gimp-selection-none image)
      (fit-layer new)
      (set! step-scale (* step-scale scale))
      (gimp-layer-scale new (* width step-scale) (* height step-scale) 1) ; 1 = image origin
      (fit-layer new)))

  (while (> steps 0)
    (if (> selection 0)
      (gimp-selection-load image selection)
      (gimp-selection-all image))
    (gimp-edit-copy image src)
    (set! new (car (gimp-layer-new image width height type "Frame" opacity mode)))
    (set! new (car (gimp-edit-paste image new 0)))
    (do-rotate)
    (do-scale)
    (if (> (car (gimp-layer-is-floating-sel new)) 0)
      (gimp-floating-sel-to-layer new))
    (do-offset)
    (add-bgnd)
    (add-fgnd)
    (do-merge)
    (set! steps (- steps 1))
    (set! next (+ next 1))
    (save-frame next))
  (if (> selection 0) ; cleanup
    (begin
      (gimp-selection-load image selection)
      (gimp-image-remove-channel image selection)))
  (if (or (> bg-on 0) (> fg-on 0))
    (set-visible TRUE))
  (gimp-displays-flush)
  (gimp-image-enable-undo image)))

(script-fu-register
                    "script-fu-step-transform"
                    "<Image>/Script-Fu/Animators/Transform"
                    "Transform selection/layer a number of times, each step in a new layer/file"
                    "Casper Maarbjerg"
                    "Casper Maarbjerg, http://www.nyx.net/~cmaarbj/"
                    "20-Dec-1998"
                    "RGB RGBA"
                    SF-IMAGE "Image"  0
                    SF-DRAWABLE "Drawable" 0
                    SF-DRAWABLE "Foreground" 0
                    SF-TOGGLE "Merge Foreground" 0
                    SF-DRAWABLE "Background" 0
                    SF-TOGGLE "Merge Background" 0
                    SF-VALUE "Number of steps" "1"
                    SF-VALUE "Scale (> 0.0)" "1.0"
                    SF-VALUE "Rotation/Step" "0.0"
                    SF-VALUE "X-offset" "0"
                    SF-VALUE "Y-offset" "0"
                    SF-TOGGLE "Offset wraparound" 1
                    SF-VALUE "Filename.typ" "\"\"")

