quick-draw_2009


Information

Created with NetLogo version NetLogo 4.0.4
Running with NetLogoLite.jar version 404.


WHAT IS IT?


-------
A simple drawing tool as a proof-of-concept of using turtles as toolbar buttons in an in-view GUI.

HOW TO USE IT


---------
Click GO (go will turn black, and remain pressed) to activate the drawing panel and the buttons.
Click GO again (go will return to it's not-pressed state) to deactivate the drawing panel.
Click on a tool bar button to set the color, alter the color tint, set line thickness, save, import, or erase the drawing, or move the tool-bar itself. Click and drag in the drawing area to draw with the current color and line thickness.

HOW DOES IT WORK?


------------
The model defines a breed a breed of turtles called "hot-spots."
Hotspots have location and an extent (the hot-spot-top, -bottom, -left, -right) that can be independent of the turtle size. Hot spots also have properties Name, Shape (of course), Sticky?, value, and tool-tip
There is also a turtle breed called pointers.
A single pointer turtle follows the mouse cursor. It could be hidden, or as in this case, be used to show additional information about the pointer (in this case, color and brush size).
The pointer monitors and records changes in the mouse button state--the pointer state can be hover, click (mouse button just pressed), drag (mouse button continues to be pressed), and up (mouse button just released)
The hotspots all monitor the pointer location to keep track of if/when the pointer enters and leaves the hotspot, and the state of the buttons while there. The hotspots respond to these events.
In this case, the hotspots are rendered as buttons, and the buttons move a little when the pointer hovers over them, display a tool-tip if the hover lasts long enough, and move even more (a "button click" effect) when the mouse button is pressed.
The hotspots behave much like GUI buttons in other interfaces: If the mouse moves off the hotspot while the mouse button is pressed down, the hotspot button remains down. If the mouse button is released while the pointer is off the button, the button popps up, but no action occurs. Button actions occur when the mouse button is released and the pointer is still over the button.
In-view graphical interfaces also mean that the interface can be hidden when not needed, or changed, depending on activity, and this could be handy for educational models where a cluttered panel of traditional static NetLogo controls is undesireable. For example, in Network model, when a node is selected, only general, and node-specific controls would be visible, but link-specific controls (e.g. delete link) would be disabled, or hidden.
------

COPYRIGHT


------
Copyright (C) 2007, James P. Steiner

Procedures

NetLogo Version: NetLogo 4.0.4

globals
[ tool-tip-delay ;; delay between pointer entering a hot-spot and the tool-top appearing in the pointer.
  observer-message ;; way for pointer turtle to tell the observer to do things only the observer can do
  toolbar-top ;; top pycor of the tool-bar
  messages ;; displayed by a monitor to show non-popup messages to the user
]

breed [ hot-spots hot-spot ] ;; turtles that are hot (active) for mouse events

breed [ pointers pointer ] ;; turtles that track the mouse

;; Note, there is only ever one turtle of breed pointer, but will still say "ask pointers ..."

pointers-own
[ click  ;; true false-- used to track the progress of a click event
  state ;; hover, down, drag, up
  ox oy ;; old mouse x, old mouse y, used to determine if the pointer has moved.
]

hot-spots-own
[ name         ;; friendly name of the hot spot
  hot-zone-top ;; top ycor of the active area of the hot-spot
  hot-zone-right ;; right xcor ...
  hot-zone-bottom ;; bottom ycor ...
  hot-zone-left ;;; left xcor ...
  sticky? ;; when clicked, this button stays clicked (i.e. it is a toggle)
  value   ;; many buttons set a value (ie color or line width)--this is the value
          ;; also used when a set of similar buttons do slightly different things
          ;; e.g. setting line thickness
  tool-tip ;; helpful text that appears next to button and in "messages" area
  arrived ;; time that the pointer arrived in the button
  state   ;; up or down -- only important when sticky? is true
          ;; tells the current state of the button
  action  ;; string or number that identifies action (in actions procedure) this hotspot causes
          ;; in this model, actions are named so that their first letters are distinct,
          ;; so that the action-detecting logic can be abbreviated.
  order   ;; the order the button appears in the tool-bar
  click   ;; goes true when mouse button down occurs inside the hot-spot
  mouse-here ;; flag for the pointer's entry and exit into the hot-spot: enter here exit 
]

patches-own
[
]

to startup
   ;; run setup when the model loads.
   setup
end

to setup
   no-display
   ca
   set toolbar-top max-pycor
   set messages "Welcome to quick-draw, by James P. Steiner!"
   setup-hot-spots
   setup-pointers
   display
end

to setup-pointers
   ;; make exactly one pointer turtle
   create-pointers 1
   [ set shape "--paintblob"
     set color white
     set state "h"
     set click false
     set heading 0
     set-drawing-line-thickness 1.0
   ]
   set-pointer-color grey
   set-pointer-color-tint 0
end   

to setup-hot-spots

   ;; delay in seconds before showing tool-tip
   set tool-tip-delay 1.0 
   
   ;; setup set of hot patches
   ;; ie patches that are active for mouse events
   
   ;; the list is an easy structure to use to layout the toolbar
   ;; and define the properties of the hot-spots.
   ;; it allows us to take shortcuts, for example,
   ;; for the most part, the action of the hot-spot
   ;; is the same as the name, so we can put "" for the action, 
   ;; and the button-creator will automatically copy the name into the action.
   ;; likewise, if properties are not defined in the list, 
   ;; the default values are used when the hot-spot is created.
   
   let hot-spot-list
   [ ;; name shape tip value action sticky?
     
     [ "open"   "button-file-open" "Open" ]
     [ "save"   "button-file-save" "SAVE" ] 
     
     [ "line-1" "button-line-1" "Size=0.10"   .10  ] 
     [ "line-2" "button-line-2" "Size=0.25"   .25  ] 
     [ "line-3" "button-line-3" "Size=0.50"   .50  ] 
     [ "line-4" "button-line-4" "Size=0.75"   .75  ] 
     [ "line-5" "button-line-5" "Size=1.00"  1.00  ] 
     [ "line-6" "button-line-6" "Size=1.50"  1.50  ] 
     
     
     [ "tint-1" "button-tint" "Set color tint 1 (darkest)"   -3 ] 
     [ "tint-3" "button-tint" "Set color tint 3 (dark)"      -1 ] 
     [ "tint-5" "button-tint" "Set color tint 5 (middle)"     0 ] 
     [ "tint-7" "button-tint" "Set color tint 7 (light)"      1 ] 
     [ "tint-9" "button-tint" "Set color tint 9 (lightest)"   3 ]
     
     [ "move-toolbar"  "button-move-toolbar" "Move toolbar" "" "" true ] 
     [ "#####" "button-spacer" ]
     [ "wipe"   "button-wipe" "Wipe (erase) the drawing" ] 
     
     [ "col-000-black"     "button-000-black"     "Change color"  000   ]
     [ "col-005-gray"      "button-005-gray"      "Change color"  005   ]
     [ "col-009-white"     "button-009-white"     "Change color"  009.9 ]
     [ "col-015-red"       "button-015-red"       "Change color"  015   ]
     [ "col-025-orange"    "button-025-orange"    "Change color"  025   ]
     [ "col-035-brown"     "button-035-brown"     "Change color"  035   ]
     [ "col-045-yellow"    "button-045-yellow"    "Change color"  045   ]
     [ "col-055-green"     "button-055-green"     "Change color"  055   ]
     [ "col-065-lime"      "button-065-lime"      "Change color"  065   ]
     [ "col-075-turquoise" "button-075-turquoise" "Change color"  075   ]
     [ "col-085-cyan"      "button-085-cyan"      "Change color"  085   ]
     [ "col-095-sky"       "button-095-sky"       "Change color"  095   ]
     [ "col-105-blue"      "button-105-blue"      "Change color"  105   ]
     [ "col-115-violet"    "button-115-violet"    "Change color"  115   ]
     [ "col-125-magenta"   "button-125-magenta"   "Change color"  125   ]
     [ "col-135-pink"      "button-135-pink"      "Change color"  135   ]
     
   ]
  
   foreach hot-spot-list
   [ let b-name first ?
     let b-shape item 1 ?
     let b-action b-name ;; use name as default action
     let b-value 0
     let b-sticky? false ;; defaults to not sticky
     let b-tool-tip ""
     ;; if tool-tip is specified, use that, instead
     if length ? > 2 and item 2 ? != "" [ set b-tool-tip item 2 ? ]
     ;; if value is specified, use that, instead
     if length ? > 3 and item 3 ? != "" [ set b-value    item 3 ? ]
     ;; if action is specified, use that, instead
     if length ? > 4 and item 4 ? != "" [ set b-action   item 4 ? ]
     ;; if sticky? is specified, use that, instead
     if length ? > 5 and item 5 ? != "" [ set b-sticky?  item 5 ? ]
     
     create-button b-name b-shape b-action b-value b-sticky? b-tool-tip
   ]
end

to create-button [ b-name b-shape b-action b-value b-sticky? b-tool-tip ]
   create-hot-spots 1
   [ button-apply-default-properties
     set name b-name
     set shape b-shape
     set action b-action
     set value b-value
     set sticky? b-sticky?
     set tool-tip (word b-tool-tip "  ")
     if sticky? [ set color white ]
   ]
end

to button-apply-default-properties
   set color gray
   set size 2
   set order count hot-spots - 1
   set name ""
   set shape "button"
   set action ""
   set sticky? false
   set tool-tip ""
   set value 0
   set state "u"

   set-hot-spot-xy
   set mouse-here "" ;; not here
   
end

to set-hot-spot-xy
   let columns int (world-width * .5)
   let row int (order / columns)
   let col order mod columns
   setxy int (min-pxcor + 1 + 2 * col) int (toolbar-top - 1 - 2 * row)
   set hot-zone-top ycor + 1
   set hot-zone-right xcor + 1
   set hot-zone-bottom ycor - 1
   set hot-zone-left xcor - 1
end
   

to monitor-mouse
   if mouse-inside?
   [ ask pointers
     [ setxy mouse-xcor mouse-ycor
       ifelse mouse-down?
       [ ifelse click = false
         [ ;; click-beginning
           mouse-down
         ]
         [ ;; mouse-down, possible drag in progress
           mouse-drag
         ]
       ]
       [ ;; mouse is up
         ifelse click = true
         [ ;; click ending, possible drop
           mouse-up
         ]
         [ ;; mouse-up, hover
           mouse-hover
         ]
       ]
     ]
   ]
end



to mouse-down
   ask pointers
   [ set click true 
     set state "c"
   ]
end

to mouse-drag
   ask pointers 
   [ if ox != mouse-xcor or oy != mouse-ycor 
     [ set state "dr" 
       set ox mouse-xcor
       set oy mouse-ycor
     ]
     
   ]
     
   
end

to mouse-up
   ask pointers
   [ set state "u" 
     set click false
     pu
   ]
end

to mouse-hover
   ask pointers
   [ set state "h" ;;hover

   ]
end
  
to go-hotspots
   let px 0
   let py 0
   let ps "" ;; pointer state: h(over) click drag up h(over)
   let hot-spot-click? false
   let new-message ""
   ask pointers 
   [ set px xcor
     set py ycor 
     set ps state
   ]
   ask hot-spots
   [ ifelse px > hot-zone-left and px < hot-zone-right
        and py > hot-zone-bottom and py < hot-zone-top
     [ ;; mouse is inside this hot-spot
       ;; did mouse just enter this hot spot?
       if mouse-here = ""
       [ ;; yes, make a note of that
         set mouse-here "enter" ;; after handling, will be set to "here"
       ]
     ]
     [ ;; mouse is outside this hotspot
       ;; did mouse just leave?
       if mouse-here != ""
       [ ;; yes, make a note of it
         set mouse-here "exit" ;; after handling, will be set to ""
       ]
     ]
     
     ifelse mouse-here = "enter" and ps = "h" 
     [ ;; learned earlier that mouse just entered this hot-spot
       ;; act on mouse entering this hot-spot
       ;; apply "slightly pressed" effect
       setxy (pxcor - .1) (pycor + .1)
       ;; remember time that pointer arrived here
       set arrived timer
       set new-message tool-tip
       ;; now that enter has been acted on, mouse-here becomes "here"
       set mouse-here "here"
       
     ][
     
     ;; if the pointer just exited and the hot-spot click is false (no click has started here)
     ;; or a click started here, (but didn't finish), and pointer isn't here anymore, and the mouse button just came up
     if (mouse-here = "exit" and click != true ) or (click = true and mouse-here = "exit" and ps = "u")
     [ ;; set the button to it's "idle" appearance
       setxy pxcor pycor
       ;; note that the mouse is not here 
       set mouse-here ""
       ;; note that click is no longer pending for here
       set click false
       if new-message = "" [ set new-message "-" ]
       set arrived 0
       
     ]] 
     
     ;; if the pointer is currently inside this hot-spot
     if mouse-here = "here"
     [ 
       if arrived != 0 and timer > arrived + tool-tip-delay
       [ ;; show tip
         ask pointers 
         [ set label [tool-tip] of myself
           
           set label-color 9.9 - label-color
         ]
         set arrived arrived + 1
       ]
       
       ;; if mouse button has just gone from up to down while here, 
       ;; ie mouse click began here, make a note of that
       if ps = "c"
       [ set click true
         ;; apply "pressed" appearance
         setxy (pxcor + .1) (pycor - .1)
         ;; clear label
         ask pointers [ set label "" ]
         set hot-spot-click? true
       ]
       
       ;; if pointer has gone from down to up ... (button released)( WHILE HERE)
       if ps = "u"
       [ ;; see if the click started here
         if click = true
         [ ;; it did! so this completes a click on the hot-spot
           ;; reset the click state
           set click false
           ;; apply "slightly pressed" effect
           setxy (pxcor - .1) (pycor + .1)
           ;; clear tool-tip
           ask pointers [ set label "" ]
           set arrived 0
           ;; see if the button is sticky, if so, change state
           ;; -- action will act on current state
           if sticky?
           [ ifelse state = "d"
             [ set state "u"  ]
             [ set state "d"  ]
           ]
           ;; now, do the button-click action for this button
           do-button-action
         ]
       ]
     ]
   ]
   if new-message != "" [ set messages new-message ]
   if hot-spot-click? = false
   [ ask pointers
     [ if state = "c"
       [ stamp 
         pd
       ]
     ]
   ]
end
     
to do-button-action
   ;; a hot spot runs this code
   let a first action
   ;; COLOR
   ifelse a = "c" [ set-pointer-color value ][
   ;; LINE THICKNESS
   ifelse a = "l" [ set-drawing-line-thickness value ][
   ;; TINT
   ifelse a = "t" [ set-pointer-color-tint value][
   ;; SAVE (EXPORT) VIEW
   ifelse a = "s" [ set observer-message "s" ][
   ;; OPEN (IMPORT) DRAWING
   ifelse a = "o" [ set observer-message "o" ][
   ;; WIPE (ERASE) VIEW
   ifelse a = "w" [ set observer-message "w" ][
   ;; MOVE TOOLBAR
   ifelse a = "m" [ move-toolbar ][
   ]]]]]]]
end

to move-toolbar
   ifelse toolbar-top = max-pycor
   [ let cols int (world-width / 2)
     let rows int (.5 + (count hot-spots) / cols)
     set toolbar-top min-pycor + 2 * rows 
     set color blue
   ]
   [ set toolbar-top max-pycor
     set color white
   ]
   ask hot-spots [ set-hot-spot-xy ]
end

to set-pointer-color [ new-color ]
   ask pointers [ set color new-color]
   ask hot-spots with [ starts-with "line" name ] [ set color new-color ]
   set new-color new-color - new-color mod 10 + 5
   ask hot-spots with [ starts-with "tint" name ] [ set color new-color + value ]
   
end

to set-drawing-line-thickness [ new-size ]
   ask pointers
   [ set pen-size new-size * 15 ;; * pixels per patch
     set size 1.01 * new-size
   ]
end

to set-pointer-color-tint [ new-tint ]
   let new-color 0
   let hue 0
   ask pointers [set hue precision (color - 5) -1 set new-color hue + 5 + new-tint  set color new-color] 
   ask hot-spots with [ starts-with "line" name ] [ set color new-color ]
   
end   

to save-drawing
   let filename user-new-file
   if is-string? filename
   [ if not (ends-with ".png" filename )
     [ set filename (word filename  ".png") ]
     ask turtles [ hide-turtle ]
     carefully [ export-view filename ] []
     ask turtles [ show-turtle ]
   ]
end

to open-drawing
   let filename user-file
   if is-string? filename
   [ carefully [ import-drawing filename ] []
   ]
end

   
to wipe-drawing
   if (user-yes-or-no? "Are you sure you want to wipe the drawing?" )
   [ clear-drawing
     let new-color 0
     ask pointers [ set new-color color ]
     cp
     ask patches [ set pcolor new-color ]
   ]
end
   
to go
   ;every .01
   ;[ 
     no-display
     monitor-mouse
     go-hotspots
     handle-observer-messages
     display
   ;]
end
    
to-report ends-with [ suffix string ]
   ;; reports true of the given string ends with the given suffix
   let len length string
   report (suffix = substring string (len - length suffix) len)
end

to-report starts-with [ prefix string ]
   ;; reports true if given prefix matches the beginning of string
   report (prefix = substring string 0 length prefix)
end
      
to handle-observer-messages
   ;; allows the turtle-based pointer to request actions
   ;; that can only or should only be performed by the observer
   if observer-message != ""
   [ if observer-message = "s" [ save-drawing ]
     if observer-message = "o" [ open-drawing ]
     if observer-message = "w" [ wipe-drawing ]
     set observer-message ""
   ]
end     

                    


Download Link

View or download the complete model file (to download: right-click, save-link-as):
-- Download quick-draw_2009 --