Fundamentals of CS I (CS151 2002F)

Lab: Writing Script-Fu Procedures

Summary: In today's laboratory, you will write and install new procedures for use within Script-Fu and the Gimp.

Exercises

Exercise 1: Grouping Commands

As you should know from your long experience with Scheme, it is relatively easy to group a number of individual commands together into a procedure. Hence, rather than typing instructions one-at-a-time into the GIMP window, we can

  1. group the commands within a Scheme procedure;
  2. put the procedure definition in a file;
  3. load the file in the Script-Fu console; and
  4. execute the procedure.

For example, here is a simple procedure I wrote that draws the letter F and then puts a "No" sign around it.

;;; Procedure:
;;;   no-failure
;;; Parameters:
;;;   none
;;; Purpose:
;;;   Creates a new image with the universal sign for "No Fs"
;;; Produces:
;;;   Nothing.  You call this only for the side effect of
;;;   drawing a new image.
;;; Preconditions:
;;;   Must be called from within Script-Fu.
;;; Postconditions:
;;;   A new image appears on the screen.
(define no-failure
  (lambda ()
    (let* ((image-and-layer (gsfu-new-image 256 256))
           (image (car image-and-layer))
           (layer (cadr image-and-layer)))
      ; Draw everything on white
      (gimp-palette-set-background WHITE)
      ; Draw the silly letter F in blue
      (gimp-palette-set-foreground BLUE)
      (gimp-brushes-set-brush "Circle (11)")
      (gsfu-line layer 96 40 176 40) ; Top stroke
      (gsfu-line layer 96 40 96 216) ; Down stroke
      (gsfu-line layer 96 112 148 112) ; Middle stroke
      ; Draw a nice red circle.
      (gimp-palette-set-foreground RED)
      (gimp-brushes-set-brush "Circle (07)")
      (gimp-ellipse-select image 8 8 240 240 REPLACE 0 0 0)
      (gimp-edit-stroke layer)
      (gimp-selection-none image)
      ; Now we're ready to draw the slash.  Basic math tells us that
      ; the endpoints are offset by radius/(sqrt 2) from the center 
      ; of the circle.
      (let* ((center 128)
             (radius 120)
             (offset (/ radius (sqrt 2))))
        (gsfu-line layer 
                   (- center offset) (- center offset)
                   (+ center offset) (+ center offset)))
      ; And display the image
      (gimp-display-new image))))

a. Create a new Scheme file to hold that procedure. You can use gedit or DrScheme (just don't expect to be able to execute the code within DrScheme). Since the procedure depends on the Glimmer Script-Fu utilities (gsfu), you should also make sure the file loads those utilities by including the following at the top of the file.

(load "/home/rebelsky/Web/Glimmer/ScriptFu/Code/gsfu.scm")

(If you like, you can also just make a copy of sfp-01.scm.)

b. Load the file into the Script-Fu console by typing (load filename). For example, if you've stored your stuff as images.scm in your public_html directory, you should use

(load "/home/username/public_html/images.scm")

c. Type (no-failure) in the Script-Fu console and see what happens.

Exercise 2: Writing Your Own Procedure

Using no-failure as a template, write and test your own procedure to draw an interesting picture. Depending on your skill level, you might draw a smiley face, a stick figure, or something else.

Exercise 3: Adding Parameters

One disadvantage of no-failure is that it always draws an image of the same size. What if we want a bigger or smaller image? One possibility is to make the image size a parameter to no-failure and then make the various numbers depend on the size. Since our image is naturally square, the width and height should probably be the same. Here's what I've come up with:

;;; Procedure:
;;;   new-no-failure
;;; Parameters:
;;;   side, an integer
;;;   color, an RGB list
;;; Purpose:
;;;   Creates a new image with the universal sign for "No Fs".
;;;   The image has width side and height side.
;;;   The F in the image has the specified color.
;;; Produces:
;;;   Nothing.  You call this only for the side effect of
;;;   drawing a new image.
;;; Preconditions:
;;;   Must be called from within Script-Fu.
;;; Postconditions:
;;;   A new image appears on the screen.
(define new-no-failure
  (lambda (side color)
    (let* ((image-and-layer (gsfu-new-image side side))
           (image (car image-and-layer))
           (layer (cadr image-and-layer))
           (unit (/ side 32)))
      ; Draw everything on white
      (gimp-palette-set-background WHITE)
      ; Draw the silly letter F in blue
      (gimp-palette-set-foreground color)
      (gimp-brushes-set-brush "Circle (11)")
      (gsfu-line layer (* unit 12) (* unit 5) 
                       (* unit 20) (* unit 5))
      (gsfu-line layer (* unit 12) (* unit 5)
                       (* unit 12) (* unit 27))
      (gsfu-line layer (* unit 12) (* unit 14)
                       (* unit 18) (* unit 14))
      ; Draw the nice red circle.
      (gimp-palette-set-foreground RED)
      (gimp-brushes-set-brush "Circle (07)")
      (gimp-ellipse-select image unit unit
                                 (- side unit unit) (- side unit unit)
                                 REPLACE 0 0 0)
      (gimp-edit-stroke layer)
      (gimp-selection-none image)
      ; Now we're ready to draw the slash.  Basic math tells us that
      ; the endpoints are offset by radius/(sqrt 2) from the center 
      ; of the circle.
      ; direction.
      (let* ((center (/ side 2))
             (radius (- center unit))
             (offset (/ radius (sqrt 2))))
        (gsfu-line layer 
                   (- center offset) (- center offset)
                   (+ center offset) (+ center offset)))
      ; Display the image
      (gimp-display-new image)
      ; And return the image and layer
      image-and-layer)))

a. Either add new-no-failure to your file from the first exercise or create a new file to contain it. You can also copy sfp-03.scm.

b. Load the file into the Script-Fu console.

c. Test the procedure by typing each of the following commands in the Script-Fu console.

(new-no-failure 256 GREEN)
(new-no-failure 100 PURPLE)

Exercise 4: Generalizing Your Procedure

Generalize your procedure from exercise 2 so that it takes the image size as a parameter. You may choose to build square images (as I did) and take only the length of a side as a parameter or you may choose to build rectangular images and take both width and height as parameters.

Exercise 5: Telling DrScheme About Procedures

With the Script-Fu that you know up to this point, all the cool Script-Fu commands you write are only available to people who are able to use the Script-Fu console. It would also be nice to add Script-Fu commands to the GIMP's various menus. Fortunately, you only need to follow a few simple steps.

1. Write Scheme code that tells the GIMP to add a procedure to the menu. Here, you need to tell the GIMP what parameters your procedure takes so that it can ask the user for those parameters.

2. Put the Scheme code in a place the GIMP can find it. Typically, you'll put your Scheme code in /home/username/.gimp-1.2/scripts. (Yes, the period in the .gimp-1.2 is important.) You'll only have to do this once per script. My experience suggests that you should end the file name with .scm rather than .ss.

3. Tell the GIMP that you've added the script by selecting Refresh from the Script-Fu submenu of the Xtns menu. (You should only have to do this when you add or modify the script in the middle of a session. In the future, the GIMP should load your script automatically.)

Steps 2 and 3 are straightforward, so let's consider the first step in more detail. You add a procedure with the script-fu-register procedure. That procedure takes a large number of parameters.

For each parameter for your procedure, you'll need three additional parameters to script-fu-register:

For SF-IMAGE and SF-DRAWABLE, the value should be 0; the GIMP will fill them in with the current image and layer. For SF-VALUE, the default value should be in quotation marks, even when it's a number. For SF-COLOR, the color should be a list of three integers (as you explored in the previous lab).

For example, here's what I might use for my no-failure procedure.

(script-fu-register
   "new-no-failure"
   "<Toolbox>/Xtns/Script-Fu/Sample/No Failure"
   "Draws a \"No Failure\" logo"
   "Samuel A. Rebelsky"
   "Copyright (c) 2001 Samuel A. Rebelsky.  All Rights Reserved"
   "Thursday, 5 April 2001"
   "RGB"
   SF-VALUE "Side Length" "256"
   SF-COLOR "Color" BLUE)

I've put this complete example in the file no-failure.scm.

a. Make a copy of the file in your Gimp scripts directory.

b. Tell Gimp to refresh its Script-Fu list by selecting Refresh from the Script-Fu menu in the Xtns menu.

c. You should now be able to select No Failure from a new Sample submenu of the Script-Fu submenu of the Xtns menu. Try it and see what happens.

d. Register your own script from the previous exercise. See if you can get it to run.

Exercise 6: Writing Helper Procedures

When doing more complex drawings, you will often find that you draw the same thing again and again at different places and in different sizes. Hence, it is sometimes useful to write helper procedures that can do the precise drawing for you. For example, I decided that I often make the "No" sign, and wrote a helper procedure for that sign. Here's my code.

;;; Procedure:
;;;   forbidden
;;; Parameters:
;;;   image, an image
;;;   layer, a layer associated with that image
;;;   x, the x coordinate of the center of the sign
;;;   y, the y coordinate of the center of the sign
;;;   radius, the radius of the sign
;;; Purpose:
;;;   Draws a "forbidden" sign (a circle with a slash) in
;;;   red centered at the specified location.
;;; Preconditions:
;;;   image and layer are initialized.
;;;   x, y, and radius are not negative.
;;; Postconditions:
;;;   The image now contains the specified sign.
;;;   The brush and foreground color may have changed.
(define forbidden
  (lambda (image layer x y radius)
    ; Choose a color and paintbrush that seem appropriate.
    (gimp-palette-set-foreground RED)
    (gimp-brushes-set-brush "Circle (05)")
    ; Select the ellipse for the circle.  Do the math to see why.
    ; the particular values were set.
    (gimp-ellipse-select image
                         (- x radius) (- y radius)
                         (* 2 radius) (* 2 radius)
                         REPLACE
                         0 0 0)
    ; Draw the nice red circle.
    (gimp-edit-stroke layer)
    ; Now we're ready to draw the slash.  Some math tells us that
    ; it's offset by radius/(sqrt 2) from the center (in each
    ; direction).
    (let ((offset (/ radius (sqrt 2))))
      (gsfu-line layer
                 (- x offset) (- y offset)
                 (+ x offset) (+ y offset)))
    ; Unselect all
    (gimp-selection-none image)
    ; Flatten the image
    (gimp-image-flatten image)
  ))

a. Once again, store that code in a file.

b. Create a new image programmatically and use forbid to draw on that image in various places and sizes.

c. Write your own helper procedure to draw a rectangle.

d. Write your own helper procedure to draw a circle.

Exercise 7: Gimp Menus, Revisited

When we write procedures like forbid, we probably want to add them to the menu for the current image, rather than the more general toolbox menu. Here's the command that I've written to add the forbid sign to the popup image menu.

(script-fu-register
   "forbidden"
   "<Image>/Script-Fu/Sample/Forbidden Sign"
   "Draws the legendary forbidden sign"
   "Samuel A. Rebelsky"
   "Copyright (c) 2001 Samuel A. Rebelsky.  All Rights Reserved"
   "Tuesday, 3 April 2001"
   "RGB"
   SF-IMAGE "Image" 0
   SF-DRAWABLE "Drawable" 0
   SF-VALUE "Center X" "100"
   SF-VALUE "Center Y" "100"
   SF-VALUE "Radius" "50")

You can find everything in forbid.scm.

Install and test it. You may find that you have to click on the image after running the script. I'm still working on figuring out that bug. Let me know if you figure anything out.

 

History

Wednesday, 4 April 2001 [Samuel A. Rebelsky]

Thursday, 5 April 2001 [Samuel A. Rebelsky]

Tuesday, 29 October 2002 [Samuel A. Rebelsky]

 

Disclaimer: I usually create these pages on the fly, which means that I rarely proofread them and they may contain bad grammar and incorrect details. It also means that I tend to update them regularly (see the history for more details). Feel free to contact me with any suggestions for changes.

This document was generated by Siteweaver on Mon Dec 2 09:19:20 2002.
The source to the document was last modified on Wed Oct 30 08:13:12 2002.
This document may be found at http://www.cs.grinnell.edu/~rebelsky/Courses/CS151/2002F/Labs/script-fu-procs.html.

You may wish to validate this document's HTML ; Valid CSS! ; Check with Bobby

Samuel A. Rebelsky, rebelsky@grinnell.edu