#!/usr/bin/wish -f ## ## SCRIPT: draw_squares_spiral.tk ## ## PURPOSE: This Tk GUI script facilitates the creation of a colored ## sequence of 'spiraling squares' --- via polygons ## (filled or outlines) drawn on a canvas widget --- where ## the canvas widget has a given (background) color. ## ## This Tk script is based on a C program in a book by Leendert ## Ammeraal, 'Programming Principles of Computer Graphics'. ## ######################## ## GUI LAYOUT and METHOD: ## ## The GUI made by this Tk script contains a rectangular ## CANVAS widget on which the sequence of squares ## (color-filled or outlines) will be drawn. ## ## The GUI includes a CHECKBOX widget to specify whether the ## squares are to be drawn with a COLOR-OUTLINE (or with COLOR FILL). ## ## The GUI also includes 2 BUTTON widgets to call a color selector GUI ## to set ## - the fill or outline color of the squares ## and, using the same color selector GUI, ## - the background color --- the color of the canvas. ## ## Other BUTTONs may be an EXIT button and a HELP button. ## ######### ## ## We denote the 4 corners of the sequence of squares as A,B,C,D. ## ## They start at ## A = Ax,Ay = 0,0 ## B = Bx,By = a,0 ## C = Cx,Cy = a,a ## D = Dx,Dy = 0,a ## measured from the top-left corner of a square in the middle of ## the canvas, where a is given as an integer, in pixels. ## ## (NOTE: We can set 'a' from the minimum of the current ## width and height of the canvas.) ## ## For the purpose of drawing the image on the canvas, ## we let all computations of the corner coordinates be in pixels ## (integers) rather than floating point numbers. ## ## We rotate the square by using 2 decimal valued parameters p and ## q=1-p, where is a value like 0.95. A SCALE widget on the GUI ## can be used to allow the user to set the value of p, between ## 0.5 and 1.0. ## ## We can think of the square as being 4 lines: AB, BC, CD, DA. ## We rotate the 4 (connected) lines by computing the new coordinates ## of the 4 corners by the following formulas: ## ## newAx = int(p * Ax + q * Bx) ## newAy = int(p * Ay + q * By) ## newBx = int(p * Bx + q * Cx) ## newBy = int(p * By + q * Cy) ## newCx = int(p * Cx + q * Dx) ## newCy = int(p * Cy + q * Dy) ## newDx = int(p * Dx + q * Ax) ## newDy = int(p * Dy + q * Ay) ## ## Then the square is drawn with a canvas 'create polygon' command --- ## using either '-fill' or '-outline'. ## ## Then we reset the 4 corner coordinates with ## Ax = newAx ## Ay = newAy ## Bx = newBx ## By = newBy ## Cx = newCx ## Cy = newCy ## Dx = newDx ## Dy = newDy ## ## This sequence of draws can be done N times, where we can let N ## be set by another SCALE widget on the GUI --- say, N from 1 to 200. ## ## The loop is put in a 'ReDraw' proc which is called upon by ## button1-release BINDINGS on the following widgets: ## - the CHECKBUTTON widget for outline/fill ## - the color-BUTTON for outline/fill ## (NOTE: The color-BUTTON for canvas background does not need to ## call the 'ReDraw' proc. It simply re-configures the '-bg' ## option of the canvas widget.) ## - the SCALE that resets factor 'p' ## - the SCALE that resets N, the number of squares to draw. ## ## In the ReDraw proc, we can use the Tcl '' command to ## report on the number of milliseconds that it took to draw the ## complete sequence for given 'p' and 'N' and outline/fill. ## ## ## (NOTE: If erasing the canvas and redrawing the sequence of squares ## completes within a very small fraction of a second --- say ## less about 50 milliseconds per completed 'frame', which ## corresponds to movie rate of at least 20 frames per second, ## it will be feasible to do the redraws 'dynamically' with ## the sliderbar of 'p' or 'N'.) ## ## USING THE GENERATED IMAGE: ## A screen/window capture utility (like 'gnome-screenshot' ## on Linux) can be used to capture the GUI image in a GIF ## or PNG file, say. ## ## If necessary, an image editor (like 'mtpaint' on Linux) ## can be used to crop the window capture image. The image ## could be used as the background of a logo, for example. ## ## The image could also be down-sized --- say to make a 'bullet' ## image file or an icon (or icon-background) image file. ## ## The editor could also be used to blur the image slightly to ## 'feather' the edges of the polygons (squares). ## ## The colored image file could be used with a utility (like the ## ImageMagick 'convert' command) to change the background ## color to TRANSPARENT, making a partially transparent GIF ## (or PNG) file. Then the semi-transparent image file could be used, ## for 'bullets' in HTML pages or in Tk GUI's --- or for the ## background of icons for use in GUIs --- or for a frame around ## a photograph, say to be used in a web page or an email. ## ## The image could also be taken into a scalable vector graphics ## (SVG) editor (like Inkscape on Linux) and the SVG editor used ## to add anti-aliased text to the image. ## ##+######################################################################## ## 'CANONICAL' STRUCTURE OF THIS TK CODE: ## ## 0) Set general window & widget parms (win-name, win-position, ## win-color-scheme, fonts, widget-geometry-parms, win-size-control, ## text-array-for-labels-etc). ## ## 1a) Define ALL frames (and sub-frames, if any). ## 1b) Pack ALL the frames and sub-frames. ## ## 2) Define all widgets in the frames, frame-by-frame. ## When ALL the widgets for a frame are defined, pack ALL the widgets. ## ## 3) Define keyboard and mouse/touchpad/touch-sensitive-screen 'event' ## BINDINGS, if needed. ## ## 4) Define PROCS, if needed. ## ## 5) Additional GUI INITIALIZATION (typicall with 1 or 2 procs), ## if needed/wanted. ## ## ## Some detail about the code structure of this particular script: ## ## 1a) Define ALL frames: ## ## Top-level : '.fRleft' and '.fRright' ## ## Sub-frames: '.fRleft.fRbuttons' ## '.fRleft.fRcolors' ## '.fRleft.fRoptions' ## '.fRleft.fRscale1' ## '.fRleft.fRscale2' ## '.fRleft.fRscale3' ## ## '.fRright.fRcanvas' ## ## 1b) Pack ALL frames. ## ## 2) Define all widgets in the frames (and pack them): ## ## - In '.fRbuttons': 1 exit BUTTON widget, ## and ## 3 color BUTTONs (for setting the squares' ## outline/fill1 color, fill2 color, and the ## background/canvas color), ## ## - In '.fRcolors': 1 LABEL widget to display current color values. ## ## - In '.fRoptions': 1 CHECKBOX widget (for outline/fill) ## and ## 1 LABEL widget for 'info' ## ## - In '.fRscale1' 1 SCALE widget (for factor 'p') ## ## - In '.fRscale2' 1 SCALE widget (for number of squares, N). ## ## - In '.fRscale3' 1 SCALE widget (for color 'extensity' exponent). ## ## - In '.fRcanvas': 1 'canvas' widget ## ## 3) Define BINDINGS: ## ## - button1-release on the outline/fill CHECKBUTTON should call ## the 'ReDraw' proc ## ## - button1-release on the SCALE widget for 'p' should call ## the 'ReDraw' proc (if we do not use the '-command' option ## of the scale widget to call ReDraw) ## ## - button1-release on the SCALE widget for 'N' should call ## the 'ReDraw' proc (if we do not use the '-command' option ## of the scale widget to call ReDraw) ## ## - a (resize) event on the window should cause a redraw ## ## NOTE: We DO NOT need a binding on the 3 color-BUTTONS. ## They will call on set-color procs that can call the 'ReDraw' ## proc after a color is set. ## ## 4) Define PROCS: ## ## - 'ReDraw' - clears the canvas and redraws the ## spiral of squares within the canvas ## --- for the current values of canvas width ## and height (their minimum), p, N, and the ## outline/fill checkbutton-variable setting. ## ## - 'set_squares_color1' - shows a color selector GUI and uses the ## user-selected color to set the ## outline (or 1st-fill) color of the squares ## --- and redraw the squares on the canvas ## ## - 'set_squares_color2' - shows a color selector GUI and uses the ## user-selected color to set the '2nd fill' ## color of the squares, to gradiate to, from ## fill-color1, as the square-number 'i' ## goes from 1 to N --- and redraw the ## squares on the canvas ## ## - 'set_color_background' - shows a color selector GUI and uses the ## user-selected color to reset the color of ## the canvas background (A redraw is not ## needed. The background UNDER the squares ## changes.) ## ## - 'show_hide_fill_buttons' - called by button1-release on the outline ## checkbutton ## ## - 'ReDraw_if_canvas_resized' - called by event on the canvas ## ## 5) Additional GUI initialization: Execute proc 'ReDraw' once with ## an initial, example set of parms ## --- canvas width-height, p, N, ## outline/fill setting, COLOR1hex, ## COLOR2hex, COLORbkGNDhex --- ## to start with a sequence of squares on ## the canvas rather than a blank canvas. ## ##+######################################################################## ## DEVELOPED WITH: ## Tcl-Tk 8.5 on Ubuntu 9.10 (2009-october release, 'Karmic Koala'). ## ## $ wish ## % puts "$tcl_version $tk_version" ## showed 8.5 8.5 on Ubuntu 9.10 ## after Tcl-Tk 8.4 was replaced by 8.5 --- to get anti-aliased fonts. ##+####################################################################### ## MAINTENANCE HISTORY: ## Created by: Blaise Montandon 2012nov21 ## Changed by: Blaise Montandon 2012nov23 Add the extensity scale. ##+####################################################################### ##+####################################################################### ## Set WINDOW TITLES. ##+####################################################################### wm title . "Spiraling Colored Squares on a Canvas (wireframe or 2-color-gradient)" wm iconname . "Squares" ##+####################################################################### ## Set initial WINDOW POSITION. ##+####################################################################### wm geometry . +15+30 ##+###################################################### ## Set the COLOR SCHEME for the window ## --- and its widgets. ##+###################################################### # set Rpal 0 # set Gpal 100 # set Bpal 200 set Rpal 210 set Gpal 210 set Bpal 210 set hexCOLORpal [format "#%02X%02X%02X" $Rpal $Gpal $Bpal] tk_setPalette "$hexCOLORpal" set BKGDcheckbutton "#cccccc" # set BKGDtext "#f0f0f0" # set BKGDentry "#f0f0f0" # set BKGDlistbox "#f0f0f0" ##+######################################################## ## Set (temporary) FONT-NAMES. ## Use a VARIABLE-WIDTH FONT for label and button widgets. ## ## Use a FIXED-WIDTH FONT for listboxes and entry fields ## and text widgets, if any. ##+######################################################## font create fontTEMP_varwidth \ -family {comic sans ms} \ -size -14 \ -weight bold \ -slant roman font create fontTEMP_SMALL_varwidth \ -family {comic sans ms} \ -size -12 \ -weight bold \ -slant roman ## Some other possible (similar) variable width fonts: ## Arial ## Bitstream Vera Sans ## DejaVu Sans ## Droid Sans ## FreeSans ## Liberation Sans ## Nimbus Sans L ## Trebuchet MS ## Verdana font create fontTEMP_fixedwidth \ -family {liberation mono} \ -size -14 \ -weight bold \ -slant roman font create fontTEMP_SMALL_fixedwidth \ -family {liberation mono} \ -size -12 \ -weight bold \ -slant roman ## Some other possible fixed width fonts (esp. on Linux): ## Andale Mono ## Bitstream Vera Sans Mono ## Courier 10 Pitch ## DejaVu Sans Mono ## Droid Sans Mono ## FreeMono ## Nimbus Mono L ## TlwgMono ##+########################################################### ## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS. ## (e.g. width and height of canvas, and padding for Buttons) ##+########################################################### ## CANVAS parms: set initCanWidthPx 300 set initCanHeightPx 300 set minCanHeightPx 24 # set BDwidthPx_canvas 2 set BDwidthPx_canvas 0 ## BUTTON parms: set PADXpx_button 0 set PADYpx_button 0 set BDwidthPx_button 2 ## LABEL parms: set PADXpx_label 0 set PADYpx_label 0 set BDwidthPx_label 2 ## SCALE parms: set BDwidthPx_scale 2 set scaleWidthPx 10 ##+################################################################### ## Set a MINSIZE of the window. ## ## For WIDTH, allow for the minwidth of the '.fRbuttons' frame: ## about 3 buttons (Exit,Color1,ColorBkgnd), and ## the initial width of the canvas widget on the right. ## ## For HEIGHT, allow ## 2 chars high for the 2-line buttons in the '.fRbuttons' frame, ## 2 chars high for the scale widgets in the '.fRoptions' frame, ## 24 pixels high for the '.fRcanvas' frame. ##+###################################################################### ## We allow the window to be resizable, because we pack the canvas with ## '-fill both -expand 1' so that the canvas can be enlarged by enlarging ## the window. ######################################################################### set minWinWidthPx [font measure fontTEMP_varwidth \ "Exit Outline Background"] set minWinWidthPx [expr {$initCanWidthPx + $minWinWidthPx}] ## Add some pixels to account for right-left-side window decoration ## (about 8 pixels), about 4 widgets x 6 pixels/widget for borders/padding ## for 4 widgets --- 3 buttons and 1 label. set minWinWidthPx [expr {32 + $minWinWidthPx}] ## MIN HEIGHT --- allow ## 2 chars high for 'fRbuttons' ## 2 chars high for 'fRoptions' ## 24 pixels high for 'fRcanvas' set CharHeightPx [font metrics fontTEMP_varwidth -linespace] set minWinHeightPx [expr {24 + (4 * $CharHeightPx)}] ## Add about 28 pixels for top-bottom window decoration, and ## about 3 frames x 6 pixels/frame for each of the 3 stacked frames ## and their widgets (their borders/padding). set minWinHeightPx [expr {46 + $minWinHeightPx}] ## FOR TESTING: # puts "minWinWidthPx = $minWinWidthPx" # puts "minWinHeightPx = $minWinHeightPx" wm minsize . $minWinWidthPx $minWinHeightPx ## If you want to make the window un-resizable, ## you can use the following statement. # wm resizable . 0 0 ####################################################################### ## Set a TEXT-ARRAY to hold text for buttons & labels on the GUI. ## NOTE: This can aid INTERNATIONALIZATION. This array can ## be set according to a nation/region parameter. ####################################################################### ## if { "$VARlocale" == "en"} set aRtext(buttonEXIT) "Exit" # set aRtext(buttonHELP) "Help" set aRtext(buttonCOLOR1) "Outline Color" set aRtext(buttonCOLOR1b) "Fill1 Color" set aRtext(buttonCOLOR2) "Fill2 Color" set aRtext(buttonBkgndCOLOR) "Background Color" set aRtext(chkbuttOUTLINE) "OUTLINE squares (don't fill)" set aRtext(labelP) "Proportion to advance:" set aRtext(labelN) "Number of squares:" set aRtext(labelM) "'Extensity' of color at outer edges and at the center:" ## END OF if { "$VARlocale" == "en"} ##+################################################################ ## DEFINE *ALL* THE FRAMES: ## ## Top-level : '.fRleft' and '.fRright' ## ## Sub-frames: '.fRleft.fRbuttons' ## '.fRleft.fRcolors' ## '.fRleft.fRoptions' ## '.fRleft.fRscale1' ## '.fRleft.fRscale2' ## '.fRleft.fRscale3' ## ## '.fRright.fRcanvas' ##+################################################################ ## FOR TESTING: (to check frame behavior as window expands/contracts) # set BDwidth_frame 2 # set RELIEF_frame raised set BDwidth_frame 0 set RELIEF_frame flat frame .fRleft -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRright -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRleft.fRbuttons -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRleft.fRcolors -relief raised -borderwidth 2 frame .fRleft.fRoptions -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRleft.fRscale1 -relief raised -borderwidth 2 frame .fRleft.fRscale2 -relief raised -borderwidth 2 frame .fRleft.fRscale3 -relief raised -borderwidth 2 frame .fRright.fRcanvas -relief $RELIEF_frame -borderwidth $BDwidth_frame ##+############################## ## PACK the top-level FRAMES. ##+############################## pack .fRleft \ -side left \ -anchor nw \ -fill none \ -expand 0 pack .fRright \ -side left \ -anchor nw \ -fill both \ -expand 1 pack .fRleft.fRbuttons \ -side top \ -anchor nw \ -fill none \ -expand 0 pack .fRleft.fRcolors \ .fRleft.fRoptions \ .fRleft.fRscale1 \ .fRleft.fRscale2 \ .fRleft.fRscale3 \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRright.fRcanvas \ -side top \ -anchor nw \ -fill both \ -expand 1 ##+######################################################### ## OK. Now we are ready to define the widgets in the frames. ##+######################################################### ##+##################################################################### ## In the '.fRleft.fRbuttons' FRAME - ## DEFINE BUTTONS (Exit and 2 color buttons), and ## a LABEL widget, to show current color values. ##+##################################################################### button .fRleft.fRbuttons.buttEXIT \ -text "$aRtext(buttonEXIT)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {exit} button .fRleft.fRbuttons.buttCOLOR1 \ -text "$aRtext(buttonCOLOR1)" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_squares_color1" button .fRleft.fRbuttons.buttCOLOR2 \ -text "$aRtext(buttonCOLOR2)" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_squares_color2" button .fRleft.fRbuttons.buttCOLORbkGND \ -text "$aRtext(buttonBkgndCOLOR)" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_background_color" ##+###################################################### ## Pack ALL the widgets in the '.fRleft.fRbuttons' frame. ##+###################################################### pack .fRleft.fRbuttons.buttEXIT \ .fRleft.fRbuttons.buttCOLOR1 \ .fRleft.fRbuttons.buttCOLORbkGND \ -side left \ -anchor w \ -fill none \ -expand 0 ## We start with the outline checkbutton set to outline-mode, ## so we do not show the fill2-color button. # .fRleft.fRbuttons.buttCOLOR2 \ ##+################################################################## ## In the '.fRleft.fRcolors' FRAME - ## DEFINE a LABEL widget, to display current hexcolor settings. ## Pack it. ##################################################################### label .fRleft.fRcolors.labelCOLORS \ -text "" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_button pack .fRleft.fRcolors.labelCOLORS \ -side left \ -anchor w \ -fill none \ -expand 0 ##+################################################################## ## In the '.fRleft.fRoptions' FRAME - ## DEFINE a CHECKBUTTON widget (for outline/fill), ## and a LABEL widget, for 'info'. ##+################################################################## if {0} { label .fRleft.fRoptions.labelOUTLINE \ -text "$aRtext(chkbuttOUTLINE)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_button } set outline0or1 0 checkbutton .fRleft.fRoptions.chkbuttOUTLINE \ -text "$aRtext(chkbuttOUTLINE)" \ -font fontTEMP_SMALL_varwidth \ -variable outline0or1 \ -selectcolor "$BKGDcheckbutton" \ -relief raised \ -bd 2 label .fRleft.fRoptions.labelINFO \ -text "" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_button ## Pack ALL the widgets of FRAME '.fRoptions' --- ## label, checkbutton, and scale widgets. pack .fRleft.fRoptions.chkbuttOUTLINE \ -side left \ -anchor w \ -fill none \ -expand 0 \ -padx {0 10} pack .fRleft.fRoptions.labelINFO \ -side left \ -anchor w \ -fill none \ -expand 0 ##+################################################################## ## In the '.fRleft.fRscale1' FRAME - ## DEFINE a SCALE widget (for 'p' proportion factor), with LABEL widget. ##+################################################################## ## SCALE for 'p': label .fRleft.fRscale1.labelP \ -text "$aRtext(labelP)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_label ## Set this widget var in the GUI initialization section ## at the bottom of this script. # set p 0.05 scale .fRleft.fRscale1.scaleP \ -from 0.001 -to 0.999 \ -resolution 0.001 \ -length 400 \ -font fontTEMP_SMALL_varwidth \ -variable p \ -showvalue true \ -orient horizontal \ -bd $BDwidthPx_scale \ -width $scaleWidthPx ## Pack ALL the widgets of FRAME '.fRleft.fRscale1' --- ## label and scale widgets. pack .fRleft.fRscale1.labelP \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRscale1.scaleP \ -side left \ -anchor w \ -fill none \ -expand 0 ##+################################################################## ## In the '.fRleft.fRscale2' FRAME - ## DEFINE a SCALE widget (for N number of squares), with LABEL widget. ##+################################################################## ## SCALE for 'N': label .fRleft.fRscale2.labelN \ -text "$aRtext(labelN)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_label ## Set this widget var in the GUI initialization section ## at the bottom of this script. # set N 50 scale .fRleft.fRscale2.scaleN \ -from 1 -to 600 \ -resolution 1 \ -length 300 \ -font fontTEMP_SMALL_varwidth \ -variable N \ -showvalue true \ -orient horizontal \ -bd $BDwidthPx_scale \ -width $scaleWidthPx ## Pack ALL the widgets of FRAME '.fRleft.fRscale2' --- ## label and scale widgets. pack .fRleft.fRscale2.labelN \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRscale2.scaleN \ -side left \ -anchor w \ -fill none \ -expand 0 \ -padx {0 10} ##+################################################################## ## In the '.fRleft.fRscale3' FRAME - ## DEFINE a SCALE widget (for color 'extensity' exponent M), with ## a LABEL widget. ##+################################################################## ## SCALE for 'M': label .fRleft.fRscale3.labelM \ -text "$aRtext(labelM)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_label ## Set this widget var in the GUI initialization section ## at the bottom of this script. # set M 6 scale .fRleft.fRscale3.scaleM \ -from 0.1 -to 10 \ -resolution 0.1 \ -length 200 \ -font fontTEMP_SMALL_varwidth \ -variable M \ -showvalue true \ -orient horizontal \ -bd $BDwidthPx_scale \ -width $scaleWidthPx ## Pack ALL the widgets of FRAME '.fRleft.fRscale3' --- ## label and scale widgets. pack .fRleft.fRscale3.labelM \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRscale3.scaleM \ -side left \ -anchor w \ -fill none \ -expand 0 \ -padx {0 10} ##+###################################################### ## In the '.fRright.fRcanvas' FRAME - ## DEFINE-and-PACK the 'canvas' widget. ##+###################################################### ## We set '-highlightthickness' and '-borderwidth' to ## zero, to avoid covering some of the viewable area ## of the canvas, as suggested on page 558 of the 4th ## edition of 'Practical Programming with Tcl and Tk'. ##+################################################### canvas .fRright.fRcanvas.can \ -width $initCanWidthPx \ -height $initCanHeightPx \ -relief flat \ -highlightthickness 0 \ -borderwidth 0 pack .fRright.fRcanvas.can \ -side top \ -anchor nw \ -fill both \ -expand 1 ## OK. The widgets are defined and packed. ## Now define bindings and procs. ##+############################### ## BINDINGS SECTION: ##+############################### ## The following bind can cause an extra ReDraw ## when the GUI is first configured via an 'update' below ## in the GUI initialization section. ## ## We move this statement to the bottom of this script. # bind .fRright.fRcanvas.can "ReDraw_if_canvas_resized" bind .fRleft.fRoptions.chkbuttOUTLINE \ "show_hide_fill_buttons ; ReDraw 0" bind .fRleft.fRscale1.scaleP "ReDraw 0" bind .fRleft.fRscale2.scaleN "ReDraw 0" bind .fRleft.fRscale3.scaleM "ReDraw 0" ##+###################################################################### ## PROCS SECTION: ## ## - ReDraw - Clears the canvas and redraws the sequence ## of spiraling squares --- for the given outline/fill ## color, outline/fill var, p var, N var. ## ## Called by: ## - the 'set_squares_color1' proc ## - button1-release binding on the outline/fill checkbutton ## - button1-release binding on the 'p' scale ## - button1-release binding on the 'N' scale ## - a binding on the canvas widget. ## ## - set_squares_color1 - called by the outline/fill1 button '-command' --- ## shows a color selector GUI and uses the user-selected ## color to set outline color (or fill-color1) for ## drawing the next sequence of N squares ## ## - 'set_squares_color' - called by the fill2 button '-command' --- ## shows a color selector GUI and uses the ## user-selected color to set the '2nd fill' ## color of the squares, to gradiate to, from ## fill-color1, as the square-number 'i' ## goes from 1 to N --- and redraw the ## squares on the canvas ## ## - set_background_color - called by background color button '-command' ## --- shows a color selector GUI and uses the ## user-selected color to set the background ## color for the canvas ## ## - show_hide_fill_buttons - called by button1-release on the outline checkbutton ## ## - ReDraw_if_canvas_resized - called by event on the canvas ##+####################################################################### ######################################################################## ## proc ReDraw - ## ## PURPOSE: ## Draws the squares on the canvas. ## ## CALLED BY: - the 'set_squares_color1' proc ## - button1-release binding on the outline/fill checkbutton ## - button1-release binding on the 'p' scale ## - button1-release binding on the 'N' scale ## ## NOTE: The 'x' argument is to avoid an error when the scale '-command' ## passes a scale value as an argument to the command. This is in ## case we ever want to implement the 'command' option on the ## 2 scale widgets for the p & N parms. ######################################################################## proc ReDraw {x} { global p N M outline0or1 \ COLOR1hex COLOR1r COLOR1g COLOR1b \ COLOR2hex COLOR2r COLOR2g COLOR2b \ COLORbkGNDhex COLORbkGNDr COLORbkGNDg COLORbkGNDb ## Set the current time, for determining elapsed ## time doing the complete draw of N squares. set t0 [clock milliseconds] ## Get the current width & height of the canvas. set canWidthPx [winfo width .fRright.fRcanvas.can] set canHeightPx [winfo height .fRright.fRcanvas.can] ## Get the inital width & height of the squares 'aPx' ## --- the minimum of canvas width & height. set aPx $canWidthPx if {$canHeightPx < $canWidthPx} {set aPx $canHeightPx} ## FOR TESTING: # puts "ReDraw > aPx: $aPx canWidthPx: $canWidthPx canHeightPx: $canHeightPx" ## Clear the canvas of the previously drawn polygons. # .fRright.fRcanvas.can delete TAGpolygons .fRright.fRcanvas.can delete all ## Set q from p. set q [expr {1.0 - $p}] ## Initialize the coordinates of the 4 corners. set AxPx 0 set AyPx 0 set BxPx $aPx set ByPx 0 set CxPx $aPx set CyPx $aPx set DxPx 0 set DyPx $aPx if {$outline0or1 != 1} { set rRange [expr {$COLOR2r - double($COLOR1r)}] set gRange [expr {$COLOR2g - double($COLOR1g)}] set bRange [expr {$COLOR2b - double($COLOR1b)}] ## For linear distribution of the colors. # set rRatio [expr {$rRange / $N}] # set gRatio [expr {$gRange / $N}] # set bRatio [expr {$bRange / $N}] } ## LOOP thru making the squares, from 0 thru N. ## For outline0or1=0 (color-gradient), the colors will go ## from COLOR1 to COLOR2. for {set i 0} {$i <= $N} {incr i} { ## Draw the first and subsequent squares --- wireframe or ## color-gradient, according to the setting of $outline0or1. if {$outline0or1 == 1} { .fRright.fRcanvas.can create polygon \ $AxPx $AyPx \ $BxPx $ByPx \ $CxPx $CyPx \ $DxPx $DyPx \ -tag TAGpolygons -outline $COLOR1hex -fill $COLORbkGNDhex } else { ## For a 'power' distribution of the colors. set iOverN [expr { double($i) / $N }] set nR [expr {int( $COLOR1r + ($rRange * pow($iOverN, $M)) )}] set nG [expr {int( $COLOR1g + ($gRange * pow($iOverN, $M)) )}] set nB [expr {int( $COLOR1b + ($bRange * pow($iOverN, $M)) )}] ## For linear distribution of the colors. # set nR [expr {int( $COLOR1r + ($rRatio * $i) )}] # set nG [expr {int( $COLOR1g + ($gRatio * $i) )}] # set nB [expr {int( $COLOR1b + ($bRatio * $i) )}] set hexcolor [format "#%02X%02X%02X" $nR $nG $nB] .fRright.fRcanvas.can create polygon \ $AxPx $AyPx \ $BxPx $ByPx \ $CxPx $CyPx \ $DxPx $DyPx \ -tag TAGpolygons -fill $hexcolor } ## FOR TESTING: # puts "ReDraw > Drawing square $i with corners:" # puts " AxPx: $AxPx AyPx: $AyPx" # puts " BxPx: $BxPx ByPx: $ByPx" # puts " CxPx: $CxPx CyPx: $CyPx" # puts " DxPx: $DxPx DyPx: $DyPx" ## Calculate corner coordinates for the next square. set newAxPx [expr { round($q * $AxPx + $p * $BxPx) }] set newAyPx [expr { round($q * $AyPx + $p * $ByPx) }] set newBxPx [expr { round($q * $BxPx + $p * $CxPx) }] set newByPx [expr { round($q * $ByPx + $p * $CyPx) }] set newCxPx [expr { round($q * $CxPx + $p * $DxPx) }] set newCyPx [expr { round($q * $CyPx + $p * $DyPx) }] set newDxPx [expr { round($q * $DxPx + $p * $AxPx) }] set newDyPx [expr { round($q * $DyPx + $p * $AyPx) }] ## Reset the coordinates of the 4 corners. set AxPx $newAxPx set AyPx $newAyPx set BxPx $newBxPx set ByPx $newByPx set CxPx $newCxPx set CyPx $newCyPx set DxPx $newDxPx set DyPx $newDyPx } ## END OF LOOP for {set i 1} {$i <= $N} {incr i} ## Make sure the text on the COLORS label widget ## is up to date --- and color the buttons accordingly. if {$outline0or1 == 1} { ## For outline-mode: .fRleft.fRcolors.labelCOLORS configure -text "\ Colors: Outline of squares = $COLOR1hex Background = $COLORbkGNDhex" } else { ## For fill-mode: .fRleft.fRcolors.labelCOLORS configure -text "\ Colors: Fill1 (outermost square) = $COLOR1hex graduating to Fill2 (innermost square) = $COLOR2hex Background = $COLORbkGNDhex" } .fRleft.fRbuttons.buttCOLOR1 configure -bg $COLOR1hex set sumCOLOR1 [expr {$COLOR1r + $COLOR1g + $COLOR1b}] if {$sumCOLOR1 > 300} { .fRleft.fRbuttons.buttCOLOR1 configure -fg #000000 } else { .fRleft.fRbuttons.buttCOLOR1 configure -fg #f0f0f0 } .fRleft.fRbuttons.buttCOLOR2 configure -bg $COLOR2hex set sumCOLOR2 [expr {$COLOR2r + $COLOR2g + $COLOR2b}] if {$sumCOLOR2 > 300} { .fRleft.fRbuttons.buttCOLOR2 configure -fg #000000 } else { .fRleft.fRbuttons.buttCOLOR2 configure -fg #f0f0f0 } .fRleft.fRbuttons.buttCOLORbkGND configure -bg $COLORbkGNDhex set sumCOLORbkGND [expr {$COLORbkGNDr + $COLORbkGNDg + $COLORbkGNDb}] if {$sumCOLORbkGND > 300} { .fRleft.fRbuttons.buttCOLORbkGND configure -fg #000000 } else { .fRleft.fRbuttons.buttCOLORbkGND configure -fg #f0f0f0 } ## Make sure the text on the INFO label widget ## is up to date. .fRleft.fRoptions.labelINFO configure -text "\ Biggest square's width=height= $aPx pixels Current canvas width: $canWidthPx height: $canHeightPx pixels DRAW TIME: [expr {[clock milliseconds] - $t0}] millisecs elapsed" } ## END OF proc 'ReDraw' ##+##################################################################### ## proc 'set_squares_color1' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set an 'outline' (or'fill1') color for the ## sequence of squares to be drawn. ## ## Arguments: none ## ## CALLED BY: .fRleft.fRbuttons.buttCOLOR1 button ##+##################################################################### proc set_squares_color1 {} { global COLOR1r COLOR1g COLOR1b COLOR1hex # global feDIR_tkguis ## FOR TESTING: # puts "COLOR1r: $COLOR1r" # puts "COLOR1g: $COLOR1g" # puts "COLOR1b: $COLOR1b" set TEMPrgb [ exec \ ./sho_colorvals_via_sliders3rgb.tk \ $COLOR1r $COLOR1g $COLOR1b] # $feDIR_tkguis/sho_colorvals_via_sliders3rgb.tk \ ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB set COLOR1hex "#$hexRGB" set COLOR1r $r255 set COLOR1g $g255 set COLOR1b $b255 ## Redraw the geometry in the new outline color --- OR, ## if outline is turned off, starting from the 'fill1' color. ReDraw 0 } ## END OF proc 'set_squares_color1' ##+##################################################################### ## proc 'set_squares_color2' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set a 'fill2' color, to gradiate to, ## from 'fill1' color, as the sequence of squares are drawn. ## ## Arguments: none ## ## CALLED BY: .fRleft.fRbuttons.buttCOLOR2 button ##+##################################################################### proc set_squares_color2 {} { global COLOR2r COLOR2g COLOR2b COLOR2hex outline0or1 # global feDIR_tkguis ## FOR TESTING: # puts "COLOR2r: $COLOR2r" # puts "COLOR2g: $COLOR2g" # puts "COLOR2b: $COLOR2b" set TEMPrgb [ exec \ ./sho_colorvals_via_sliders3rgb.tk \ $COLOR2r $COLOR2g $COLOR2b] # $feDIR_tkguis/sho_colorvals_via_sliders3rgb.tk \ ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB set COLOR2hex "#$hexRGB" set COLOR2r $r255 set COLOR2g $g255 set COLOR2b $b255 ## Redraw the geometry going to the new 'fill2' color. ## (Make sure the outline switch is turned off, since ## the user apparently wants to use the polygon-fill option.) set outline0or1 0 ReDraw 0 } ## END OF proc 'set_squares_color2' ##+##################################################################### ## proc 'set_background_color' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set the color of the canvas. ## ## Arguments: none ## ## CALLED BY: .fRleft.fRbuttons.buttCOLORbkGND button ##+##################################################################### proc set_background_color {} { global COLORbkGNDr COLORbkGNDg COLORbkGNDb COLORbkGNDhex \ COLORbkGNDr COLORbkGNDg COLORbkGNDb # global feDIR_tkguis ## FOR TESTING: # puts "COLORbkGNDr: $COLORbkGNDr" # puts "COLORbkGNDg: $COLORbkGNDb" # puts "COLORbkGNDb: $COLORbkGNDb" set TEMPrgb [ exec \ ./sho_colorvals_via_sliders3rgb.tk \ $COLORbkGNDr $COLORbkGNDg $COLORbkGNDb] # $feDIR_tkguis/sho_colorvals_via_sliders3rgb.tk \ ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB set COLORbkGNDhex "#$hexRGB" set COLORbkGNDr $r255 set COLORbkGNDg $g255 set COLORbkGNDb $b255 ## Set the color of the canvas. .fRright.fRcanvas.can config -bg $COLORbkGNDhex ## Redraw the geometry in the new outline color --- OR, ## if outline is turned off, starting from the 'fill1' color. ReDraw 0 } ## END OF proc 'set_background_color' ##+############################################################# ## proc show_hide_fill_buttons ## ## PURPOSE: To hide/show the 'fill2' 'COLOR2' button on the GUI by ## using the 'pack forget' command to hide it and by ## usning 'pack forget' and 'pack' to insert the ## button among the existing buttons. ## ## This proc also resets the label text on the ## outline/fill1 button --- the 'COLOR1' button. ## ## CALLED BY: button1-release binding on .fRleft.fRoptions.chkbuttOUTLINE ##+############################################################# proc show_hide_fill_buttons {} { global outline0or1 aRtext if {$outline0or1 == 1} { ## We are switching from fill-mode to outline-mode. pack forget .fRleft.fRbuttons.buttCOLOR2 .fRleft.fRbuttons.buttCOLOR1 configure -text "$aRtext(buttonCOLOR1)" } else { ## We are switching from outline-mode to fill-mode. pack forget .fRleft.fRbuttons.buttCOLOR1 \ .fRleft.fRbuttons.buttCOLORbkGND \ .fRleft.fRcolors.labelCOLORS pack .fRleft.fRbuttons.buttCOLOR1 \ .fRleft.fRbuttons.buttCOLOR2 \ .fRleft.fRbuttons.buttCOLORbkGND \ .fRleft.fRcolors.labelCOLORS \ -side left \ -anchor w \ -fill none \ -expand 0 .fRleft.fRbuttons.buttCOLOR1 configure -text "$aRtext(buttonCOLOR1b)" } } ## END OF PROC 'show_hide_fill_buttons' ##+############################################################# ## proc ReDraw_if_canvas_resized ## ## PURPOSE: To make sure we are only doing redraws if the ## of the canvas resulted in a change ## in the size of the canvas. ## ## CALLED BY: bind .fRright.fRcanvas.can ## at bottom of this script. ##+############################################################# proc ReDraw_if_canvas_resized {} { global PREVcanWidthPx PREVcanHeightPx set CURcanWidthPx [winfo width .fRright.fRcanvas.can] set CURcanHeightPx [winfo height .fRright.fRcanvas.can] if { $CURcanWidthPx != $PREVcanWidthPx || \ $CURcanHeightPx != $PREVcanHeightPx} { ReDraw 0 set PREVcanWidthPx $CURcanWidthPx set PREVcanHeightPx $CURcanHeightPx } } ## END OF ReDraw_if_canvas_resized ##+##################################################### ## Additional GUI initialization, if needed (or wanted). ##+##################################################### ## Initialize the squares' outline/fill1 color, the ## fill2 color, and the background color for the canvas. # set COLOR1r 255 # set COLOR1g 0 # set COLOR1b 0 set COLOR1r 255 set COLOR1g 255 set COLOR1b 0 set COLOR1hex [format "#%02X%02X%02X" $COLOR1r $COLOR1g $COLOR1b] # set COLOR2r 255 # set COLOR2g 0 # set COLOR2b 0 set COLOR2r 0 set COLOR2g 0 set COLOR2b 0 set COLOR2hex [format "#%02X%02X%02X" $COLOR2r $COLOR2g $COLOR2b] # set COLORbkGNDr 0 # set COLORbkGNDg 0 # set COLORbkGNDb 0 set COLORbkGNDr 255 set COLORbkGNDg 0 set COLORbkGNDb 0 set COLORbkGNDhex \ [format "#%02X%02X%02X" $COLORbkGNDr $COLORbkGNDg $COLORbkGNDb] ## Initialize the widget variables, for a checkbutton ## and 3 scale widgets. set outline0or1 1 set p 0.05 set N 50 set M 6 ## Call 'ReDraw' to initialize the canvas. ## We need 'update' here to set the size of the canvas, ## because 'ReDraw' uses 'winfo' to get the width and ## height of the canvas. update ReDraw 0 ## We need this command because ReDraw does not (re)set ## the background/canvas color. .fRright.fRcanvas.can config -bg $COLORbkGNDhex ## After this script drops into the Tk event-handling loop, ## this bind command causes redraws whenever the canvas is resized. set PREVcanWidthPx [winfo width .fRright.fRcanvas.can] set PREVcanHeightPx [winfo height .fRright.fRcanvas.can] bind .fRright.fRcanvas.can "ReDraw_if_canvas_resized"