ppm-image-import-with-sprites_2-1


Information

Created with NetLogo version NetLogo 3.1.1
Running with NetLogoLite.jar version 314.


TO DO


-
Needs more comments in the code. WAAAAAY more.

WHAT IS IT?


-------
This code example is a heavy modification of the PPM image import example from the models library.
This code example shows how to import and manipulate bitmap images with NetLogo. It reads ASCII format PPM (Portable GrayMap) files. PPM, along with its cousins PGM and PPM, is a very simple format for bitmap images.

HOW IT WORKS


--------
A PGM file is a text file with a simple header followed by pixel values. Values are separated by whitespace. Anything on a line after a "#" is a comment. Here is an example PGM file:
P2 # magic number
24 7
15
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 3 0 0 0 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 15 15 15 0
0 3 0 0 0 0 0 7 0 0 7 0 0 11 0 0 0 0 0 15 0 0 15 0
0 3 0 0 0 0 0 7 0 0 7 0 0 11 0 11 11 0 0 15 0 0 15 0
0 3 0 0 0 0 0 7 0 0 7 0 0 11 0 0 0 0 0 15 0 0 15 0
0 3 3 3 3 0 0 7 7 7 7 0 0 11 11 11 11 0 0 15 15 15 15 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
The first value is a "magic number" that specifies the format of the file: "P2" indicates a gray-scale file with ASCII values. (This model can only read "P2" PGM files.) The second and third values are the image's X and Y dimensions. The fourth value is the highest gray value which can appear -- the value which indicates white. The remainder of the file is a list of gray-scale values, ranging from 0 to the white value and separated by whitespace. Any text after a "#" is a comment. In the example, one line of the file corresponds to one row of the image, but that isn't a requirement.
The model uses NetLogo's file I/O commands to read the PPM file.
First a new sprite is created and saved in a variable. Then the sprite turtle is told to load in an image file. When it does so, it creates a pixel turtle for every pixel in the image. The pixel turtles read in the RGB values for their pixel.
To move a sprite, you move the sprite turtle, then issue the sprite-paint command.
This makes all the pixel turtles for that sprite move to the correct location. I have provided normal and no-wrap versions of both.
Both paint commands support transparancy. In this demp, the transparent color is always white. You can implement any scheme you like to set the transparant color. For example, the lower-left pixel defines the trans. color. Or perhaps you may wish to load in a another PPM or PGM file to use as a trans mask. That's up to you!

HOW TO USE IT


---------
Use the IMPORT PPM INTO TURTLES button to import a sample PPM file.
Click DEMO to show the sprite, and start it moving around. You can import more than one image.
The transparent? switch turns transparency on and off.
The stamp? switch makes the sprites periodically stamp their colors into the patches.

THINGS TO TRY


---------
Try importing one of your own images. You'll have to convert the image beforehand to ASCII format PPM. There are many applications, both free and commercial, that convert image files to PPM. For example, XnView is a free, multi-platform graphics converter available at http://www.xnview.com. If you use XnView, be sure to check the "ASCII" checkbox when saving PPM files.
Once you have a PPM file ready, you may wish to edit the graphics window to be large enough to accomodate your image. If you don't do that, the image will get cut off. When you're editing the graphics window, remember that "screen edge x" and "screen edge y" are the distance from the center of the screen to the edge, so you should enter values that are half of one less than the size of your image. For example, entering 250 and 250 will produce a world 501x501 patches in size. (The current version of NetLogo only supports odd world dimensions, not even.) It's OK if the graphics window is a little bit too big for your image. Note that in the current version of NetLogo is not possible to set screen-edge-x and screen-edge-y from code; they can only be set manually, by the user.
Note that it is possible to scale the painting of the sprite. The sprite-paint-scale command shows one way to do that.

CREDITS AND REFERENCES


------------------
For more information on the PGM format, see the Linux manual page for "pgm", available at http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi?coll=linux&db=man&fname=/usr/share/catman/man5/pgm.5.html

Procedures

NetLogo Version: NetLogo 3.1.1

;;;; SUMMARY
;; Import PPM images into turtle-based sprites
;;;; Copyright
;; Copyright (C) 2004/2005 James P. Steiner
;; Portions (C) 2004 Uri Wilensky
;; See code for licensing and usage.
;;
globals
[ ]

breed [ pixels ]
breed [ sprites ]


sprites-own
[ spr-height
  spr-width
  
  spr-max-value
  spr-first-pixel
  spr-pixel-list
  spr-pixel-set
  spr-name
  spr-scale
  spr-trans-color ;; the transparent color
  
]

pixels-own
[ pix-x
  pix-y
  pix-r ; original r value
  pix-g ; original b value
  pix-b ; original g value
  pix-c ; original color value
  pix-spr-id
]


to import-sprite
   ask new-sprite
   [ sprite-import-ppm sprite-file-name ]
   clear-output
   output-print "Sprites:"
   ask sprites
   [ output-print self ]
end


to spin-sprites
   no-display
   ask sprites
   [ home
     set heading heading + 5 + 5 * who mod 3
     jump max-pxcor * .66
     sprite-paint-nowrap
   ]
   every 1 / stamp-rate [ if stamp?[  stamp-sprites ]]
   display
end

to stamp-sprites
   ask sprites
   [ sprite-to-patches ]
end
   

   

;; the sprite turtle is used to manage the pixel turtles
to-report new-sprite
   let results nobody
   create-custom-sprites 1
   [ set results self ]
   report results
end

;; these procedures import a "plain" (ascii) PPM (Portable Pixel Map).
;; a sprite turtle is created to manage the pixel turtles.

to sprite-import-ppm [ pathname  ]
  ;; error trapping wrapper for sprite-import-ppm-contents
  ;; reports a turtle of breed sprite
  ;; USER-CHOOSE-FILE returns false if the user cancels, so this
  ;; check is here to handle that case, or if the path is blank
  if not is-string? pathname or pathname = ""
  [ stop ]
  
  carefully
  [
    file-open (word "data/" pathname )
    sprite-import-ppm-contents
  ] 
  ;; error handler for "carefully"
  [
    user-message (word pathname
                       " is not a valid ASCII format PPM file: ("
                       error-message ")." )
  ]
  file-close
end

to sprite-to-patches
   ;; run by a sprite turtle
   ;; causes the pixel turtles to imprint their color into the pcolor of the patch.
   ;; if patches own ppix-r, ppix-g and ppix-b, will copy pix-r, -g, -b into the patch, too.
   ;; this preserves the original color values, rather than the "covertered" values
   ;; one might get using "extract-rgb" on the pcolor.
   ifelse is-patch-variable? "ppix-r"
   [ ask pixels with [   pix-spr-id = myself and not (transparent? and color = spr-trans-color-of myself )]
     [ set pcolor color 
       run "set ppix-r pix-r  set ppix-g pix-g  set ppix-b pix-b"
     ]
   ]
   [ ask pixels with [   pix-spr-id = myself and not (transparent? and color = spr-trans-color-of myself )]
    [ set pcolor color
    ]
  ]
end  
    
       


to-report is-patch-variable? [ varname-string ]
   let is-a-variable? false  
   carefully
   [ ask patch 0 0 
     [ set is-a-variable? run-result "ppix-r = ppix-r" ] 
     set is-a-variable? true
   ]
   [ set is-a-variable? false 
   ]
   report is-a-variable?
end   
   
to sprite-import-ppm-contents
  ;; check magic number
  if read-pnm-entry != "P3"
  [
    user-message "This is not a valid ASCII format PPM file (magic number is not P3)"
    stop
  ]
  ;; assumes this code is run by a turtle.
  ;; any turtle that runs this code becomes a sprite
    set breed sprites 
    set shape "blank"
    set color white
    set heading 0
    set spr-scale 1
    set hidden? true
    
    ;; get width, height, and white value
    set spr-width read-from-string read-pnm-entry
    set spr-height read-from-string read-pnm-entry
    set spr-max-value read-from-string read-pnm-entry
    
    let max-value spr-max-value
    ;; adjust x and y to new origin.
    ;; sprite x y is origin
    let x-offset int ( spr-width / -2 )
    let y-offset int ( spr-height / 2 )

    ;; read the actual pixel values
    ;; create pixel turtles, assign to sprite
    let x 0
    let y 0
    while [y < spr-height]
    [
      set x 0
      while [x < spr-width]
      [  ;; convert from sprite coordinates to relative patch-at coordinates
         ;; (in patch-at coordinates, the origin is in the center,
         ;; not the top left, and y coordinates increase going up,
         ;; not going down)
          hatch 1
          [ set breed pixels
            set heading 0
            set shape "square-pixel"
            ;; read R G B values from file
            set pix-r ( read-from-string read-pnm-entry ) / max-value
            set pix-g ( read-from-string read-pnm-entry ) / max-value
            set pix-b ( read-from-string read-pnm-entry ) / max-value
            ;; set location
            set pix-x x-offset + x
            set pix-y y-offset - y
            ;; assign to sprite turtle
            set pix-spr-id myself
            set pix-c rgb pix-r pix-g pix-b
            set color pix-c
            setxy pix-x pix-y
            set hidden? true
          ]
          set x x + 1
        ]
      set y y + 1
    ]
    ;; assign pixel agentset to sprite
    set spr-pixel-set pixels with [ pix-spr-id = myself ]
    set spr-trans-color 9.9
end

to hide-pixel
   ifelse (transparent? and (color = spr-trans-color-of pix-spr-id) )
   [ if not hidden? [ hide-turtle ] ]
   [ if     hidden? [ show-turtle ] ]
end

to hide-pixel-nowrap [ wrapped? ]
   ifelse wrapped? or (transparent? and (color = spr-trans-color-of pix-spr-id) )
   [ if not hidden? [ hide-turtle ] ]
   [ if     hidden? [ show-turtle ] ]
end

to sprite-paint
     let ox xcor
     let oy ycor
     ask spr-pixel-set 
     [ setxy ( ox + pix-x )
             ( oy + pix-y )
       hide-pixel      
     ]
end

to sprite-paint-nowrap
   ifelse      xcor + .5 * spr-width  <=    max-pxcor 
          and  xcor - .5 * spr-width  >= min-pxcor
          and  ycor + .5 * spr-height <=    max-pycor 
          and  ycor - .5 * spr-height >= min-pycor
   [ sprite-paint ]
   [ let ox xcor
     let oy ycor
     ask spr-pixel-set 
     [ let new-x ox + pix-x
       let new-y oy + pix-y
       setxy new-x new-y
       hide-pixel-nowrap (new-x != xcor or new-y != ycor)
     ]
   ]
end

to sprite-paint-scale [ amount pix-amount ]
     ;; amount adjusts pixel spacing
     ;; pix-amount adjusts relative pixel size
     set spr-scale amount
     let ox xcor
     let oy ycor
     ask spr-pixel-set 
     [ let new-x ox + pix-x * amount
       let new-y oy + pix-y * amount
       set hidden? off-screen? new-x new-y
       setxy new-x new-y
       set size amount * pix-amount
     ]
end

to sprites-reset
   ask sprites [ sprite-paint-scale 1 1 ]
   ask pixels [ set heading 0 ]
end

to-report on-screen? [ x y ]
  report (abs x <= max-pxcor or abs y <= max-pycor)
end

to-report off-screen? [ x y ]
  report (abs x > max-pxcor or abs y > max-pycor)
end

to sprite-hide
   hide-turtle
   ask spr-pixel-set [ hide-turtle ]
end

to sprite-show
   show-turtle
   ask spr-pixel-set [ show-turtle ]
end

to sprite-die
   ask spr-pixel-set [ die ]
   die
end


to-report pix-real-x
   report (xcor-of pix-spr-id) + pix-x
end

to-report pix-real-y
   report (ycor-of pix-spr-id) + pix-y
end
 
 

;; reads the next entry in a "plain" (ascii) pnm file 
;; i.e. pgm P2, ppm P3
;; - an error occurs if there are no more entries
;; - entries are separated by arbitrary whitespace 
;; - characters on a line after "#" are comments
to-report read-pnm-entry
  ;; get next character
  let c file-read-characters 1
  ;; ignore leading whitespace
  while [whitespace-char? c]
    [ set c file-read-characters 1 ]
  ;; skip comments
  if c = "#" [
    ;; first skip the comment itself
    while [c != "\n" and c != "\r"] [
      set c file-read-characters 1
    ]
    ;; then skip linefeeds and/or newlines at end of comment
    while [c = "\n" or c = "\r"] [
      set c file-read-characters 1
    ]
  ]    
  ;; read the entry
  let str ""
  while [not whitespace-char? c]
    [ set str str + c
      set c file-read-characters 1 ]
  report str
end



;; reports true if c is a single whitespace character
to-report whitespace-char? [c]
  report c = " " or
         c = "\t" or   ;; tab
         c = "\n" or   ;; newline
         c = "\r"      ;; linefeed
end



; (C) 2004 James Steiner, Uri Wilensky.
; This code may be freely copied, distributed,
; altered, or otherwise used by anyone for any legal purpose.
; 
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
; "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
; LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
; A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
; OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
; LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
; DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
; OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

                    


Download Link

View or download the complete model file (to download: right-click, save-link-as):
-- Download ppm-image-import-with-sprites_2-1 --