#!/usr/bin/wish -f ## ## SCRIPT: make_colorDisk_withCenteredColorGradient_andWithShadedBorder.tk ## ## ## PURPOSE: This Tk GUI script 'draws' a color-filled 'disk shape' ## --- including shading to a 'background color' at the ## outer edge of the disk shape. ## ## AND, to get an effect like a light-reflection in the middle, ## the disk includes shading from a 'lighting color' in the ## center to the 'disk color', as one moves outward from the ## center. ## ## So there are 3 colors involved: ## lighting-color --> disk-color --> background-color ## going from the-center-of-the-disk to its-outer-edge. ## ## Examples of use of this disk-shaped image: ## 1) At a large size, the 3D-looking disk image could be used as a ## background for a LOGO. ## 2) At a medium size, the image could be used as a background ## for an ICON. ## 3) At a small size, the image could be used as a 'BULLET' --- ## for line items on a web page or for menu items in a Tk GUI ## 'toolchest'. ## ######### ## METHOD: ## ## This script makes this shaded shape via a Tk image 'structure' ## placed on a Tk canvas widget. ## ## The image is put on the canvas via a Tk canvas 'image create' command ## --- and the image is generated via 'put -to $x $y' ## commands on the image. ## ## The rectangular image-structure covers the entire canvas widget. The ## disk-shape lies within the canvas (and the image structure) --- ## with a margin around it. In the margin, outside the ## disk-shape on the canvas, a user-selected 'background' color ## is applied. ## ######################### ## REFERENCES and CREDITS: ## ## This Tk script is based on my Tk script at http://wiki.tcl.tk/37214 ## - 'GUI for Drawing a 3D Donut (a ring) with nice shaded edges'. ## ## In that script, I devised a 'color-metric' v at each point x,y. ## Then v and (1-v) could be applied to a user-selected background color ## and a user-selected 'fill' color for the donut shape, to get ## a 'weighted-average' of the two colors, for the color at the pixel x,y. ## ## I defined a 'color-metric' at any point x,y on the rectangular image ## on the rectangular canvas by ## ## v = a * (rho - (R+r)/2) ^ 2 ## ## where 'r' and 'R' are the internal and external radii of the donut ## and ## where the coefficient 'a' is given by 1 / ((R-r)/2) ^ 2 ## and ## where rho(x,y) = sqrt (x*x + y*y). ## ## v is 0.0 in the center of the ring shape and ## v is 1.0 on the inner and outer borders of the ring shape. ## ## The canvas 'create image' (and 'put' commands) technique used in the ## 'make-shaded-donut' script is similar to the technique used in three of my ## scripts that make SHADED EDGES around 'super-ellipses' and around ## 'super-formula-shapes' and around color-gradient-rectangles. ## ## See ## http://wiki.tcl.tk/37004 - ## GUI for Drawing 'Super-ellipses', with nice shaded edges ## and ## http://wiki.tcl.tk/37156 - ## 'GUI for Drawing 'Super-formula' shapes, with nice shaded border' ## and ## http://wiki.tcl.tk/37143 - ## GUI for Drawing Rectangular 'Buttons' with nice shaded edges ## ## This entire sequence of scripts was set off by a wiki.tcl.tk ## posting by 'ulis' (deceased circa 2008) in which he used the ## canvas-'image create'-and-'put' technique to create one of these ## 'super' shapes, with shading at the edge. ## CREDIT GOES TO 'ulis' (R.I.P.). ## ## Those 3 scripts also use a 'color metric' v --- and the factors ## v and (1 - v) were used get a weighted average of 2 colors to ## apply that color to a pixel at x,y. ## ## Probably the most easily explained 'color-metric' is the one ## for the 'super-ellipse'. Here is an explanation for that shape. ## ##+##################### ## THE SHADING TECHNIQUE (metric) for the super-ellipse: ## ## For detail on the shading technique applied to super-ellipses, ## see wiki.tcl.tk/37004 - ## GUI for Drawing 'Super-ellipses', with nice shaded edges. ## ## The edge-shading effect for the super-ellipse benefited from the equation ## for a super-ellipse --- more precisely, the equation for its edge: ## |x/a|^n + |y/b|^n = 1 ## ## The interior of the super-ellipse is given by the inequality ## |x/a|^n + |y/b|^n <= 1 ## ## The edge shading (3D effect) was obtained by using a 'metric' on the ## points x,y in the super-ellipse --- a value 'v', between 0 and 1, ## given by: ## v = |x/a|^n + |y/b|^n ## for each point inside the super-ellipse. ## ## The equation for v dictates v is 1 on the border of the ## super-ellipse ("by definition") and v declines to 0 towards the ## center --- the origin (0,0). ## ## (1.0 - $v) is applied to the user-selected 'unshaded' RGB-color ## for the super-ellipse --- and $v applied to the user-selected RGB ## background color. The weighted-average of the pair of RGB values ## gives us the color at any x,y point in the super-ellipse. ## ## (Note that v is not a constant. The values of x and y gave us the ## 'v' value to apply to get the 'shaded' color at x,y --- i.e. ## v is a function of x and y.) ## ##+######################################################## ## DERIVATION OF A 'COLOR METRIC' FOR THE '2-GRADIENT-DISK': ## ## Let 'R' be the radius of the outer edge of the circular disk. ## ## We will let 'r' be the radius at which the 'lighting-color' at ## the center of the disk FINISHES transitioning to the 'disk-color'. ## Furthermore 'r' is the radius at which the 'disk-color' STARTS ## transitioning to the 'background color'. ## ## Let any point x,y in the rectangular image area be measured ## from the center of the rectangular image. Let 'rho' denote ## the radial distance of the point x,y from that center. So ## ## rho(x,y) = sqrt (x*x + y*y) ## ## We will, as a preliminary step, let our 'color-metric' be ## ## v = rho(x,y) / r when rho is between 0 and r ## ## Note that v on this 'domain' is 0 at rho=0 and 1 at rho=r. ## ## On the 'outer' part of the disk, ## we want v to be 0 at rho=r and v to be 1 at rho=R. ## ## For rho between r and R ## v = (rho(x,y) - r) / ( R - r) ## looks like it will work. ## ## This is a suitable metric if we apply it over the 2 different ## 'domains' to the 2 different color pairs. ## ## Specifically, when rho(x,y) is between 0 and r, ## we get the color at the pixel x,y by the weighted average ## ## (1 - v) * lighting-color + v * disk-color. ## ## Check: ## At x,y=0,0 , rho=0, so v = 0 and the equation ## reduces to the 'lighting-color', which is what we want. ## ## And when x,y is such that rho=r, then v=1 and the equation ## reduces to the 'disk-color', which is what we want. ## ## And when rho(x,y) is between r and R, ## we get the color at the pixel x,y by the weighted average ## ## (1 - v) * disk-color + v * background-color. ## ## Check: ## When x,y is such that rho=r, then v=(r-r)/(R-r)=0 and the ## equation for the color at pixel x,y reduces to the 'disk-color', ## which is what we want. ## ## When x,y is such that rho=R, then v=(R-r)/(R-r)=1 and the ## equation for the color at pixel x,y reduces to the ## 'background-color', which is what we want. ## ## Now we have a suitable metric, v. ## ############################ ## USING THE 'COLOR-METRIC': ## ## At a point x,y, we determine the 'shaded color' at the point by ## using one of the two 'v,1-v' equations. ## ## We calculate the 'shaded color' at x,y by calulating a weighted average ## based on applying the factor (1.0 - $v) to color1 --- and applying $v ## to color2. That is, shaded-color = (1 - v) * color1 + v * color2. ## ## We actually calculate via formulas like ## shaded-R = (1 - v) * R1 + v * R2 ## shaded-G = (1 - v) * G1 + v * G2 ## shaded-B = (1 - v) * B1 + v * B2 ## ## If x,y is such that rho=sqrt(x*x + y*y) is between 0 and r, then ## color1 and color2 will be the 'lighting' color and the 'disk' color. ## ## If x,y is such that rho=sqrt(x*x + y*y) is between r and R, then ## color1 and color2 will be the 'disk' color and the 'background' color. ## ## Thus we will get the two kinds of shading (lighting-shading and ## the 3D effect of edge-shading at the outer radius) for the ## 'disk shape'. ## ## Note that over either domain --- 0 to r, or r to R --- if ## color1 = color2, then the color given by ## (1 - v) * color1 + v * color2 ## is simply a constant color --- olor1 = color2. ## ######################################## ## A SLIGHT CHANGE TO THE 'COLOR-METRIC': ## ## Actually, it turns out that 1-v and v gives a rather washed-out (too ## gradual) shading effect at the outer radius. It is better if we ## raise v to a power N and use v^N and (1 - v^N). ## ## It may turn out that N = 6, say, gives pretty nice shading for ## the disk shape at the outer edge. But rather than hard-code the ## value of N, we provide an 'edge-shading' scale widget on the GUI ## so that the user can set the value of N. ## ## Similarly, we provide a 'lighting-shading' scale widget on the ## GUI so that the user can set a value M, so that the factors ## v^M and (1 - v^M) are used to calculate the lighting shading. ## ##+############## ## THE GUI DESIGN: ## ## The GUI made by this Tk script contains a rectangular ## CANVAS widget on which the color-filled disk shape ## will be drawn. ## ## The GUI includes 2 'SCALE' widgets whose slider-bars can ## be used to change the values of the EXPONENTS M and N, ## mentioned above. ## ## The GUI includes a 'SCALE' widget whose slider-bar can ## be used to set the RATIO of little-r to big-R. ## ## (We could provide 2 'scale' widgets for the user to set ## both r and R, in pixels. But, since the user ## will usually want the disk to be contained within the ## canvas and with a margin of about 10%, we can query ## the current width & height of the canvas, and set R suitably. ## That makes the GUI a little simpler and probably will ## not infringe much on flexibility/freedom for the user.) ## ## The GUI also includes 3 BUTTONS which call a COLOR SELECTOR GUI ## to set (change) the 3 colors: lighting, disk, background/margin. ## ## The main proc is a 'redraw' proc. ## A redraw includes clearing the canvas and redrawing the ## disk shape. ## ## A redraw should be done (a) whenever any of the scales change, ## (b) whenever a color button is used to change a color, and ## (c) whenever the window (and thus the canvas) is resized --- ## so that the image/shape will be redrawn in the center of ## the canvas. ## ## PERFORMANCE CONSIDERATIONS: ## Since the redraw has a lot of pixels to color, especially when ## the canvas is expanded to a pretty large size, a redraw may ## take several seconds. ## ## So it is probably not going to be feasible/pleasing to do redraws ## 'dynamically' with the '-command' option of the 'scale' widgets. ## ## For now, I have defined a button1-release binding on the ## scale widgets to trigger a redraw --- only when the user ## finishes dragging the sliderbar of any scale. ## ## However, it should be pointed out that if erasing the ## canvas and calculating-colors and putting the colors in the ## disk shape and on the background completes within a small ## fraction of a second, it would be feasible to do the redraws ## 'dynamically' with each sliderbar, via the '-command' option. ## (But it might heat up the CPU doing those operations --- ## by quite a few degrees.) ## ############################ ## 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 also be down-sized --- say, to make a 'bullet' image ## file --- or an icon-background image file. ## ## 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 or buttons for use in tkGUIs/web-pages. ## ## 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, scalable text to the image. OR, the shape ## (and color shading) can be used as an underlying pattern to ## reproduce the shape as a scalable shape, by using ## curve drawing tools of the SVG editor. ## The SVG image could then be saved as a totally scalable image, ## without a raster-image needing to be stored in the SVG file. ## ##+######################################################################## ## '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). ## ## 1a) Define ALL frames (and sub-frames, if any). ## 1b) Pack ALL frames and sub-frames. ## ## 2) Define all widgets in the frames. Pack them. ## ## 3) Define keyboard or mouse/touchpad/touch-sensitive-screen action ## BINDINGS, if needed. ## ## 4) Define PROCS, if needed. ## ## 5) Additional GUI INITIALIZATION (typically with one or two of ## the procs), if needed. ## ## ## Some detail about the code structure of this particular script: ## ## 1a) Define ALL frames: ## ## Top-level : '.fRbuttons' ## '.fRscales1' ## '.fRscales2' ## '.fRcanvas' ## No sub-frames. ## ## 1b) Pack ALL frames. ## ## 2) Define all widgets in the frames (and pack them): ## ## - In '.fRbuttons': ## 1 button widget ('Exit'), (perhaps 'Help' someday) ## and ## 3 buttons (for setting the 3 colors: 'lighting', ## 'disk', and 'background'), ## and ## 1 label widget to display the current color ## values (in hex) --- and elapsed ## execution time, as well as a ## 'calculation in progress' msg. ## ## - In '.fRscales1': ## 1 'label' and 1 'scale' widget for setting ## rOverR, the ration of little-r to big-R. ## This is the main control on the outer ## extent of the lighting shading. ## ## - In '.fRscales2': ## 1 'label' and 1 'scale' widget each for M and N ## (where M is an exponent used to control the ## 'extensity' of the lighting shading, and N is ## an exponent used to control the 'extensity' ## of the edge-shading.) ## ## - In '.fRcanvas': 1 'canvas' widget ## ## 3) Define bindings: ## ## - a button1-release on each of the scale widgets causes a redraw ## ## NOTE: The color changes should trigger a redraw, but we do not ## need bindings to do those redraws. ## The redraws can be done in procs that are used to ## set each of the colors. ## ## 4) Define procs: ## ## - 'ReDraw' - to clear the canvas and redraw the pixels ## in the image-rectangle that contains the ## disk shape ## --- for the current values of the scale ## parameters and the 3 colors. ## ## - 'set_shape_color1' - shows a color selector GUI and uses the ## user-selected color to set the 'lighting' ## color for the disk shape on the canvas ## ## - 'set_shape_color2' - shows a color selector GUI and uses the ## user-selected color to set the 'disk' ## color for the disk shape on the canvas ## ## - 'set_background_color' - shows a color selector GUI and uses the ## user-selected color to reset the color of ## the canvas/image-rectangle background ## ## - 'ReDraw_if_canvas_resized' - to do a redraw when a ## event is detected on the canvas --- ## but only if the canvas has been resized. ## ## 5) Additional GUI initialization: Execute proc 'ReDraw' once with ## an initial, example set of parms ## --- 3 scale vars, COLOR1hex, COLOR2hex, ## COLORbkGNDhex --- ## to start with a disk shape on ## the canvas rather than a blank canvas. ## ## Also in this section, we define a binding for ## - a (resize) event on the canvas --- to cause a redraw. ##+######################################################################## ## 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 2012oct27 ## Changed by: ...... ......... 2012 ##+####################################################################### ##+####################################################################### ## Set general window parms (win-title,win-position). ##+####################################################################### wm title . "Edge-shaded, lighted, colored DISK, on a single-color canvas" wm iconname . "ColorDisk" wm geometry . +15+30 ##+###################################################### ## Set the color scheme for the window and its widgets --- ## and set the initial color for the disk-shape interior ## and the canvas background (outside the disk-shape). ##+###################################################### tk_setPalette "#e0e0e0" ## Initialize the 'lighting' color. ## A bright color with RGB's near 255 would be good. # set COLOR1r 255 # set COLOR1g 255 # set COLOR1b 255 set COLOR1r 0 set COLOR1g 200 set COLOR1b 200 set COLOR1hex [format "#%02X%02X%02X" $COLOR1r $COLOR1g $COLOR1b] ## Initialize the 'disk' color ## to gradiate to, from the 'lighting' color. ## Should be darker than the lighting color for ## a nice high-lighting effect. # set COLOR2r 255 # set COLOR2g 255 # set COLOR2b 0 set COLOR2r 0 set COLOR2g 0 set COLOR2b 200 set COLOR2hex [format "#%02X%02X%02X" $COLOR2r $COLOR2g $COLOR2b] ## Initialize the 'background' color. ## Black usually is good --- at least for starters. # set COLORbkGNDr 60 # set COLORbkGNDg 60 # set COLORbkGNDb 60 set COLORbkGNDr 0 set COLORbkGNDg 0 set COLORbkGNDb 0 set COLORbkGNDhex \ [format "#%02X%02X%02X" $COLORbkGNDr $COLORbkGNDg $COLORbkGNDb] # set listboxBKGD "#f0f0f0" # set entryBKGD "#f0f0f0" ##+######################################################## ## Use a VARIABLE-WIDTH FONT for label and button widgets. ## ## Use a FIXED-WIDTH FONT for listboxes and entry fields, ## if any. Usually for 'text' and 'message' widgets too. ##+######################################################## 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) ##+########################################################### set initCanWidthPx 400 set initCanHeightPx 300 set minCanHeightPx 24 # set BDwidthPx_canvas 2 set BDwidthPx_canvas 0 ## BUTTON geom parameters: set PADXpx_button 0 set PADYpx_button 0 set BDwidthPx_button 2 ## LABEL geom parameters: set PADXpx_label 0 set PADYpx_label 0 set BDwidthPx_label 2 ## SCALE geom parameters: set BDwidthPx_scale 2 # set initScaleLengthPx 100 ## This is the 'height' of a scale, ## if it is horizontal orientation. set scaleWidthPx 10 ##+################################################################### ## Set a MINSIZE of the window (roughly). ## ## For width, allow for the minwidth of the '.fRbuttons' frame: ## about 3 buttons (Exit,Color1,Color2,ColorBkgnd). ## We want to at least be able to see the Exit button. ## ## For height, allow ## 2 chars high for the '.fRbuttons' frame, ## 1 char high for the '.fRscales' frame, ## 24 pixels high for the '.fRcanvas' frame. ##+################################################################### set minWinWidthPx [font measure fontTEMP_varwidth \ "Exit Color1 Color2 Background"] ## Add some pixels to account for right-left-side window decoration ## (about 8 pixels), about 4 x 4 pixels/widget for borders/padding for ## 4 widgets --- 4 buttons. set minWinWidthPx [expr {24 + $minWinWidthPx}] ## MIN HEIGHT --- ## for the 'stack' of 3 frames: '.fRbuttons', '.fRscales', ## and '.fRcanvas'. Allow ## 2 char high for 'fRbuttons' ## 1 char high for 'fRscales' ## 24 pixels high for 'fRcanvas' set CharHeightPx [font metrics fontTEMP_varwidth -linespace] set minWinHeightPx [expr {3 * $CharHeightPx}] ## Add about 28 pixels for top-bottom window decoration, ## about 3x4 pixels for each of the 3 stacked frames and their ## widgets (their borders/padding). set minWinHeightPx [expr {$minWinHeightPx + 40}] ## FOR TESTING: # puts "minWinWidthPx = $minWinWidthPx" # puts "minWinHeightPx = $minWinHeightPx" wm minsize . $minWinWidthPx $minWinHeightPx ## We allow the window to be resizable and we pack the canvas with ## '-fill both -expand 1' so that the canvas can be enlarged by enlarging ## the window. ## If you want to make the window un-resizable, ## you can use the following statement. # wm resizable . 0 0 ##+################################################################ ## DEFINE *ALL* THE FRAMES: ## ## Top-level : '.fRbuttons' '.fRscales1' '.fRscales2' '.fRcanvas' ##+################################################################ # set BDwidth_frame 2 # set RELIEF_frame raised set BDwidth_frame 0 set RELIEF_frame flat frame .fRbuttons -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRscales1 -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRscales2 -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRcanvas -relief $RELIEF_frame -borderwidth $BDwidth_frame ##+############################## ## PACK the top-level FRAMES. ##+############################## pack .fRbuttons \ .fRscales1 \ .fRscales2 \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRcanvas \ -side top \ -anchor nw \ -fill both \ -expand 1 ##+######################################################### ## OK. Now we are ready to define the widgets in the frames. ##+######################################################### ##+##################################################################### ## In the '.fRbuttons' FRAME --- ## DEFINE-and-PACK several BUTTONS --- and a LABEL: ## - an exit-button, ## and ## - 3 buttons (to specify colors) ## and ## - a label widget, to show current color values (in hex) ##+##################################################################### button .fRbuttons.buttEXIT \ -text "Exit" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {exit} ## Add this button someday? # button .fRbuttons.buttHELP \ # -text "Help" \ # -font fontTEMP_varwidth \ # -padx $PADXpx_button \ # -pady $PADYpx_button \ # -relief raised \ # -bd $BDwidthPx_button \ # -command {help} button .fRbuttons.buttCOLOR1 \ -text "\ Lighting Color" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_shape_color1" button .fRbuttons.buttCOLOR2 \ -text "\ Disk Color" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_shape_color2" button .fRbuttons.buttCOLORbkGND \ -text "\ Background Color" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_background_color" ## The text for labelCOLORS is set in the ReDraw proc. ## It is done there to make sure that the colors used for ## the current drawing are displayed correctly. ## Also elapsed execution time, or a 'calculation in progress' ## msg is shown in this label. label .fRbuttons.labelCOLORS \ -text "" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_button ##+########################################### ## Pack the widgets in the 'fRbuttons' frame. ##+########################################### pack .fRbuttons.buttEXIT \ .fRbuttons.buttCOLOR1 \ .fRbuttons.buttCOLOR2 \ .fRbuttons.buttCOLORbkGND \ .fRbuttons.labelCOLORS \ -side left \ -anchor w \ -fill none \ -expand 0 # .fRbuttons.buttHELP \ ##+############################################### ## In the '.fRscales1' FRAME ---- ## DEFINE-and-PACK 1 LABEL and 1 SCALE widget - ## for ratio rOverR, where r is the outer ## extent of the lighting shading and R is the ## radius of the disk. ################################################## label .fRscales1.label_rOverR \ -text "\ Ratio 'rOverR' (where 'r' defines the extent of the lighting and 'R' is the radius of the disk):" \ -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 rOverR 0.90 scale .fRscales1.scale_rOverR \ -from 0.00 -to 1.00 \ -resolution 0.05 \ -bigincrement 0.05 \ -digits 3 \ -repeatdelay 1000 \ -length 200 \ -font fontTEMP_SMALL_varwidth \ -variable rOverR \ -showvalue true \ -orient hor \ -bd $BDwidthPx_scale \ -width $scaleWidthPx # -command "ReDraw" ## Pack the widgets in frame 'fRscales1'. pack .fRscales1.label_rOverR \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRscales1.scale_rOverR \ -side left \ -anchor w \ -fill x \ -expand 1 ##+##################################################### ## In the '.fRscales2' FRAME ---- ## DEFINE-and-PACK 2 pairs of LABEL-and-SCALE widgets - ## 1 LABEL and 1 SCALE widget for exponent M, and ## 1 LABEL and 1 SCALE widget for exponent N. ##+##################################################### ## DEFINE SCALE for exponent M. label .fRscales2.label_M \ -text "\ exponent M to control 'extensity' of lighting shading:" \ -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 1 scale .fRscales2.scale_M \ -from 0.2 -to 5.0 \ -resolution 0.2 \ -bigincrement 0.2 \ -digits 2 \ -repeatdelay 1000 \ -length 100 \ -font fontTEMP_SMALL_varwidth \ -variable M \ -showvalue true \ -orient hor \ -bd $BDwidthPx_scale \ -width $scaleWidthPx # -command "ReDraw" ## DEFINE SCALE for exponent N. label .fRscales2.label_N \ -text "\ exponent N to control 'extensity' of outer-edge shading" \ -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 3 scale .fRscales2.scale_N \ -from 1 -to 30 \ -resolution 1 \ -bigincrement 1 \ -digits 2 \ -repeatdelay 1000 \ -length 100 \ -font fontTEMP_SMALL_varwidth \ -variable N \ -showvalue true \ -orient hor \ -bd $BDwidthPx_scale \ -width $scaleWidthPx # -command "ReDraw" ## Pack the widgets in frame 'fRscales2'. pack .fRscales2.label_M \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRscales2.scale_M \ -side left \ -anchor w \ -fill x \ -expand 1 pack .fRscales2.label_N \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRscales2.scale_N \ -side left \ -anchor w \ -fill x \ -expand 1 ##+###################################################### ## In the '.fRcanvas' FRAME - ## DEFINE-and-PACK the CANVAS widget. ##+###################################################### ## We set highlightthickness & borderwidth of the canvas to ## zero, as suggested on page 558, Chapter 37, 'The Canvas ## Widget', in the 4th edition of the book 'Practical ## Programming in Tcl and Tk'. ##+###################################################### canvas .fRcanvas.can \ -width $initCanWidthPx \ -height $initCanHeightPx \ -relief flat \ -highlightthickness 0 \ -borderwidth 0 pack .fRcanvas.can \ -side top \ -anchor nw \ -fill both \ -expand 1 ##+######################################## ## END OF the DEFINITION OF THE GUI WIDGETS ##+######################################## ##+############################### ## BINDINGS SECTION: ##+############################### ## The following bind causes an extra ReDraw ## when the GUI is first configured via an 'update' below ## in the GUI initialization section. ## (And 'update' causes about 40 redraws if ## we use '.' instead of '.fRcanvas.can'.) ## We move this statement to the bottom of this script. # bind .fRcanvas.can "ReDraw 0" bind .fRscales1.scale_rOverR "ReDraw 0" bind .fRscales2.scale_M "ReDraw 0" bind .fRscales2.scale_N "ReDraw 0" ##+###################################################################### ## PROCS SECTION: ## ## - ReDraw - Called by button1-release bindings on the ## scale widgets, by the set-color procs, ## and in the GUI initialization section at the ## bottom of this script. ## ## Draws the disk shape on the canvas for ## the current scale parameter values and for the ## current color var values. ## ## - set_shape_color1 - called by color1 ('lighting') button '-command' ## ## - set_shape_color2 - called by color2 ('disk') button '-command' ## ## - set_background_color - called by background color button '-command' ## ## - ReDraw_if_canvas_resized - called by 'bind' to canvas ## ##+####################################################################### ##+##################################################################### ## proc ReDraw - ## ## PURPOSE: ## Draws the disk shape on the canvas. ## ## We will use symmetry in the 'disk shape' and calculate the pixel ## color of pixels in the upper left quadrant of the image. As the color ## of each pixel is determined, that hex-color is 'poked' into 4 pixels, ## in 4 quadrants, a pixel at a time, with calls like: ## imgID put $hexcolor -to $x $y ## where $x $y is measured (in pixels) relative to the middle of ## the rectangular canvas/image area. ## ## CALLED BY: bindings (in the BINDINGS section) --- and by set-color procs ## --- and by the GUI initialization section at the bottom of ## this script. ## ## 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 try using the '-command' option of the scale widgets to ## do the redraws 'dynamically' as a sliderbar is moved. ##+##################################################################### ## For reference, our 'color-metric' is given as follows: ## ## Let rho = sqrt(x*x + y*y) ## where x,y are measured from the middle of the canvas/image rectangle. ## ## On the 'domain' where rho is between 0 and r, our 'color-metric' is ## ## v = rho(x,y) / r ## ## On the 'domain' where rho is between r and R, our 'color-metric' is ## ## v = (rho(x,y) - r) / ( R - r) ## ## This is a suitable metric if we apply it over the 2 different ## 'domains' to the 2 different color pairs. ## ## Specifically, when rho(x,y) is between 0 and r, ## we get the color at the pixel x,y by the weighted average ## ## (1 - v) * lighting-color + v * disk-color ## More briefly: ## (1 - v) * color1 + v * color2 ## ## And when rho(x,y) is between r and R, ## we get the color at the pixel x,y by the weighted average ## ## (1 - v) * disk-color + v * background-color ## More briefly: ## (1 - v) * color2 + v * bkgndcolor ##+##################################################################### proc ReDraw {x} { global M N rOverR \ COLOR1r COLOR1g COLOR1b COLOR1hex \ COLOR2r COLOR2g COLOR2b COLOR2hex \ COLORbkGNDr COLORbkGNDg COLORbkGNDb COLORbkGNDhex ## Set the current time, for determining elapsed ## time for building the 'photo' image. set t0 [clock milliseconds] ## Indicate that drawing calculations are starting by ## a notice in the COLORS label. Use 'update' to apply the change. ## COMMENTED. This cause processing problems. # .fRbuttons.labelCOLORS configure -text "\ # Colors: Lighting - $COLOR1hex Disk - $COLOR2hex Background - $COLORbkGNDhex # # ** CALCULATIONS IN PROGRESS **" # update ## Change the title of the window to show calculations are in process. ## (This shows how we could put a msg in the window title bar, ## instead of in a label in the GUI.) # wm title . \ # "** DRAWING CALCULATIONS ARE IN PROGRESS ** Please wait." ## Delete the current image structure. ## We especially need to do this when the canvas has been re-sized, ## so that we can redraw the image according to the new canvas size. (?) catch {image delete imgID} ## Get the current canvas size. set curCanWidthPx [winfo width .fRcanvas.can] set curCanHeightPx [winfo height .fRcanvas.can] ## Initialize the width & height of the image that we are going to create ## --- to the size of the canvas --- ## and make each dimension of the image an even integer (pixels). set imgWidthPx $curCanWidthPx set imgHeightPx $curCanHeightPx if {$imgWidthPx % 2 == 1} { incr imgWidthPx -1 } if {$imgHeightPx % 2 == 1} { incr imgHeightPx -1 } ## Make the new image structure. image create photo imgID -width $imgWidthPx -height $imgHeightPx ## Put the new image 'structure' on the canvas. ## (Note to myself: Should this statement be at top or bottom of this proc? ## Does this mainly matter the first time the canvas is used?) .fRcanvas.can create image 0 0 -anchor nw -image imgID ## Get the half width and height of the image rectangle --- ## which is the same as the pixel-coordinates of the origin. set xmidPx [expr {$imgWidthPx / 2}] set ymidPx [expr {$imgHeightPx / 2}] ## Set R to about 90% of the min of the half-width and ## half-height of the image rectangle. set R $xmidPx if { $ymidPx < $R} {set R $ymidPx} set R [expr {round(0.90 * $R)}] ## Set r from R and the ratio rOverR. set r [expr {int($rOverR * $R)}] ######################################################################### ## HERE IS THE 'GUTS': ## In a loop over yPx and xPx, where yPx and xPx are measured from the ## top left of the canvas/image rectangle, we calculate the hex-color ## for each pixel, from x = xPx - xmidPx and y = yPx - ymidPx, ## according to our 'color-metric': ## ## For 0 <= rho <= r, our 'color-metric' is ## v = rho(x,y) / r ## and we set pixel-color(x,y) = (1-v)*color1 + v*color2 . ## ## For r <= rho <= R, our 'color-metric' is ## v = (rho(x,y) - r) / ( R - r) ## and we set pixel-color(x,y) = (1-v)*color2 + v*bkgndcolor . ## ## where ## rho = sqrt( x^2 + y^2 ) ## ## We use the symmetry of the disk shape. We loop over the pixels ## of the upper left quadrant and set the colors in the other 3 ## quadrants using the calculated pixel color in the upper-right quad. ####################################################################### ## Calc. the parts of the metric that depend on R and r --- ## here outside the xy-loop below --- so that we do not ## calculate it again and again. set Rminusr [expr { $R - $r }] # set Rminusr [expr { double($R - $r) }] ## Set the pixel distance to the left side and the ## top side of the square containing the 'disk'. set leftPx [expr { $xmidPx - $R }] set topPx [expr { $ymidPx - $R }] for {set yPx 0} {$yPx <= $ymidPx} {incr yPx} { ## If we are above the square containing the disk ## (and in the top left quadrant), draw the entire ## horizontal scanline across the image --- at height ## $yPx --- and below the square at height $imgHeightPx - $yPx. if { $yPx < $topPx } { ## Draw the scanline at the 2 heights. (We are depending on ## the 'put' command to 'tile' the color across the image.) ## across upper half of image imgID put $COLORbkGNDhex -to \ 0 $yPx \ $imgWidthPx [expr {$yPx + 1}] ## across lower half of image imgID put $COLORbkGNDhex -to \ 0 [expr {$imgHeightPx - $yPx}] \ $imgWidthPx [expr { ($imgHeightPx - $yPx) - 1}] ## FOR TESTING: (show one pair of horiz. scanlines at a time) # update ## Skip to the next y. continue } ## END OF if { $yPx < $topPx } ## If we are below the top of the square containing the disk ## (and in the top left quadrant), draw the 'partial' ## horizontal scanline from the left of the image (x=0) ## to the left side of the square (x=leftPx). Also, draw the ## other 3 partial-scanlines in the other 3 quadrants. if { $yPx >= $topPx } { ## upper-left quadrant (left-point to right-point) imgID put $COLORbkGNDhex -to \ 0 $yPx \ $leftPx [expr {$yPx + 1}] ## lower-left quadrant (left-point to right-point) imgID put $COLORbkGNDhex -to \ 0 [expr {$imgHeightPx - $yPx}] \ $leftPx [expr { ($imgHeightPx - $yPx) - 1}] ## upper-right quadrant (right-point to left-point) imgID put $COLORbkGNDhex -to \ $imgWidthPx $yPx \ [expr {$imgWidthPx - $leftPx}] [expr {$yPx + 1}] ## lower-right quadrant (right-point to left-point) imgID put $COLORbkGNDhex -to \ $imgWidthPx [expr {$imgHeightPx - $yPx}] \ [expr {$imgWidthPx - $leftPx}] [expr { ($imgHeightPx - $yPx) - 1}] ## FOR TESTING: (show the 4 horiz. scanline-'segments' that were drawn) # update } ## END OF if { $yPx >= $topPx } ## Now we just have to draw the pixels inside the square ## containing the disk. We calc. the colors for pixels between ## between leftPx and xmidPx --- and apply the same hexcolor ## to the corresponding pixel in the other 3 quadrants --- ## for each y below topPx (and above ymidPx). ## ## Here is where we need to determine if x,y is located such that ## rho(x,y) is between 0 & r or between r and R. ## Accordingly we set hexcolor1 and hexcolor2 for the averaging. ## Then we calc. the color-metric, v, do the average, and poke ## the color at x,y --- and in the 3 other quadrants. We do this ## 'pixel-by-pixel' rather than by 'scanline', because ## we are in a square where it is NOT all background color and ## the color is gradually changing over the pixels in the disk. for {set xPx $leftPx} {$xPx <= $xmidPx} {incr xPx} { ## Calculate the pixel coords relative to the center of ## the canvas/image rectangle --- and calculate 'rho'. set x [expr {$xPx - $xmidPx}] set y [expr {$yPx - $ymidPx}] set rho [expr {sqrt( ($x * $x) + ($y * $y) )}] ## If rho > R, the point x,y is outside the 'disk shape' ## so we set the pixel to the background color. ## and skip out to the next x with 'continue'. if {$rho > $R} { imgID put $COLORbkGNDhex -to $xPx $yPx ## Put the same color in the other 3 quadrants --- ## top-right, bottom-left, bottom-right. imgID put $COLORbkGNDhex -to [expr {$imgWidthPx - $xPx}] $yPx imgID put $COLORbkGNDhex -to $xPx [expr {$imgHeightPx - $yPx}] imgID put $COLORbkGNDhex -to [expr {$imgWidthPx - $xPx}] [expr {$imgHeightPx - $yPx}] ## FOR TESTING: (show the 4 pixels that have just been drawn) # update continue } ## END OF if {$rho > $R} ## At this point we should be inside the disk. ## Calculate our color-metric: ## ## For 0 <= rho <= r, our 'color-metric' is ## v = rho(x,y) / r ## and we set pixel-color(x,y) = (1-v)*color1 + v*color2 ## and 'continue'. ## ## For r <= rho <= R, our 'color-metric' is ## v = (rho(x,y) - r) / ( R - r) ## and we set pixel-color(x,y) = (1-v)*color2 + v*bkgndcolor ## and 'continue'. if {$rho <= $r} { ## We should be inside the light-shading area. set v [expr {$rho / $r}] set vpow [expr {pow($v,$M)}] set oneMinusVpow [expr {1.0 - $vpow}] set Red [expr {int(($vpow * $COLOR2r) + ($oneMinusVpow * $COLOR1r))}] set Grn [expr {int(($vpow * $COLOR2g) + ($oneMinusVpow * $COLOR1g))}] set Blu [expr {int(($vpow * $COLOR2b) + ($oneMinusVpow * $COLOR1b))}] if {$Red > 255} {set Red 255} if {$Grn > 255} {set Grn 255} if {$Blu > 255} {set Blu 255} if {$Red < 0} {set Red 0} if {$Grn < 0} {set Grn 0} if {$Blu < 0} {set Blu 0} set hexcolor [format "#%02X%02X%02X" $Red $Grn $Blu] ## Put the color at $xPx $yPx. imgID put $hexcolor -to $xPx $yPx ## Put the same color in the other 3 quadrants --- ## top-right, bottom-left, bottom-right. imgID put $hexcolor -to [expr {$imgWidthPx - $xPx}] $yPx imgID put $hexcolor -to $xPx [expr {$imgHeightPx - $yPx}] imgID put $hexcolor -to [expr {$imgWidthPx - $xPx}] [expr {$imgHeightPx - $yPx}] ## FOR TESTING: (show the 4 pixels that have just been drawn) # update continue } ## END OF if {$rho <= $r} ## if {$rho >= $r && $rho <= $R} ## We should be between r and R, so we should not need the 'if' above. set v [expr {($rho - $r) / double($Rminusr)}] set vpow [expr {pow($v,$N)}] set oneMinusVpow [expr {1.0 - $vpow}] set Red [expr {int(($vpow * $COLORbkGNDr) + ($oneMinusVpow * $COLOR2r))}] set Grn [expr {int(($vpow * $COLORbkGNDg) + ($oneMinusVpow * $COLOR2g))}] set Blu [expr {int(($vpow * $COLORbkGNDb) + ($oneMinusVpow * $COLOR2b))}] if {$Red > 255} {set Red 255} if {$Grn > 255} {set Grn 255} if {$Blu > 255} {set Blu 255} if {$Red < 0} {set Red 0} if {$Grn < 0} {set Grn 0} if {$Blu < 0} {set Blu 0} set hexcolor [format "#%02X%02X%02X" $Red $Grn $Blu] ## Put the color at $xPx $yPx. imgID put $hexcolor -to $xPx $yPx ## Put the same color in the other 3 quadrants --- ## top-right, bottom-left, bottom-right. imgID put $hexcolor -to [expr {$imgWidthPx - $xPx}] $yPx imgID put $hexcolor -to $xPx [expr {$imgHeightPx - $yPx}] imgID put $hexcolor -to [expr {$imgWidthPx - $xPx}] [expr {$imgHeightPx - $yPx}] ## FOR TESTING: (show the 4 pixels that have just been drawn) # update ## END OF if {$rho >= $r && $rho <= $R} ## We should not need the brace to end this 'if', if the ## 'if' statement is commented above. } ## END OF for {set xPx 0} {$xPx < $xmidPx} {incr xPx} ## FOR TESTING: (show what's been drawn so far for y=yPx --- ## 2 complete horiz-scanlines with the bkgd-color OR ## 4 horiz-scanline-segments outside the square containing ## the disk (with the bkgnd-color) PLUS the 2 horiz ## lines of pixels that were drawn inside the square with ## varying color. ## NOTE: This can cause lots of redraws due to the ## binding on any action on the canvas. ## Comment this out when done testing, or change ## the canvas binding. # update } ## END OF for {set yPx 0} {$yPx < $imgHeightPx} {incr yPx} ## Make sure the text on the COLORS and PROCESSING label widgets ## is up to date. CAREFUL! If this text is too long, it may cause ## the binding to keep triggering a ReDraw. .fRbuttons.labelCOLORS configure -text "\ Colors: Lighting - $COLOR1hex Disk - $COLOR2hex Background - $COLORbkGNDhex Disk-radius-R: $R Lighting-radius-r: $r (pixels) DRAW TIME: [expr {[clock milliseconds] - $t0}] millisecs elapsed" ## This update to show the change to the COLORS label is not ## necessary. The update is shown as the processing stops ## and we return to the wait-state of the Tk event-handling loop. # update ## Change the title of the window to show execution time. ## (This shows how we could put a msg in the window title bar, ## instead of in a label in the GUI.) # wm title . \ # "Redraw DONE. [expr {[clock milliseconds] - $t0}] millisecs elapsed." } ## END OF proc 'ReDraw' ##+##################################################################### ## proc 'set_shape_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 triplet to set a 'lighting' color. ## ## Arguments: none ## ## CALLED BY: .fRbuttons.buttCOLOR1 button ##+##################################################################### proc set_shape_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 image with the new color. ReDraw 0 } ## END OF proc 'set_shape_color1' ##+##################################################################### ## proc 'set_shape_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 triplet to set an 'disk' color. ## ## Arguments: none ## ## CALLED BY: .fRbuttons.buttCOLOR2 button ##+##################################################################### proc set_shape_color2 {} { global COLOR2r COLOR2g COLOR2b COLOR2hex # 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 image with the new color. ReDraw 0 } ## END OF proc 'set_shape_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 triplet to set the color of the canvas --- ## actually to set a background color for use on the ## 'image structure' that is put on the canvas widget. ## ## Arguments: none ## ## CALLED BY: .fRbuttons.buttCOLORbkGND button ##+##################################################################### proc set_background_color {} { global COLORbkGNDr COLORbkGNDg COLORbkGNDb COLORbkGNDhex # 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 ## Redraw the image with the new color. ReDraw 0 } ## END OF proc 'set_background_color' ##+############################################################# ## 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 .fRcanvas.can ## at bottom of this script. ##+############################################################# proc ReDraw_if_canvas_resized {} { global PREVcanWidthPx PREVcanHeightPx set CURcanWidthPx [winfo width .fRcanvas.can] set CURcanHeightPx [winfo height .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). ##+##################################################### ## Set initial scale widget variables. set rOverR 0.90 set M 1.4 set N 4 ## Initialize the canvas with 'ReDraw'. ## Need 'update' here to set the size of the canvas, ## because 'ReDraw' uses 'winfo' to get the width and ## height of the canvas. ## See the 'bind ' command below. update ReDraw 0 ## After this script drops into the Tk event-handling loop, ## this bind command causes redraws whenever the canvas is resized. set PREVcanWidthPx [winfo width .fRcanvas.can] set PREVcanHeightPx [winfo height .fRcanvas.can] bind .fRcanvas.can "ReDraw_if_canvas_resized"