#!/usr/bin/wish -f ## ## SCRIPT: draw_superformulaShape_colorFilled_onColorBkgnd.tk ## ## ## PURPOSE: This Tk GUI script facilitates the creation of a color-filled ## 'super-formula' shape --- via polygons drawn on a canvas widget ## --- where the canvas widget has a given (background) color. ## ## This Tk script is based on a Tk script at the web page ## called 'Superformula' at http://wiki.tcl.tk/12977 --- ## by Gerard Sookahet in 2004 November. On that page, GS points ## out that the equation for the 'super-formula' is: ## ## r(theta) = ( |cos(m*theta/4)/a| ^ n2 + |sin(m*theta/4)/b| ^ n3 ) ^ -1/n1 ## ## GS points out: ## "Superformula was proposed by Johan Gielis (a Belgian botanist) in 1997. ## He has modified and generalized the superellipse formula discovered by ## Gabriel Lamé in 1818 (a French mathematician). Superformula consists in ## a simple 2D analytical expression allowing to draw a wide variety of ## geometric and natural shapes (starfish, petals, snowflakes) by choosing ## suitable values relevant to few parameters." ## ##+#################################### ## SOME NOTES ON THE FORMULA PARAMETERS: ## ## Note that 'm' generates protrusions/intrusions on the shape. ## As theta goes from 0 to 2*pi in the formula above, and if m=2, ## the angle m*theta/4 goes from 0 to pi --- one 'hump' of a sine ## or cosine curve. In general, if m = 2*n, then there will be ## (at least) n protrusions/intrusions. ## ## We will make the 'm' scale the first on the left, since it ## fundamentally changes the shape. ## ## By some experimenting with the GUI, one finds ## that if 'a' and 'b' are kept equal ## and if 'n2' and 'n3' are kept equal, ## the shape will tend to be symmetric. ## This makes sense by looking at the formula above. ## ## We will make changing the 'a' scale make the 'b' scale change with it. ## We will make changing the 'n2' scale make the 'n3' scale change with it. ## Then the user can change the 'b' and 'n3' scales to make them ## different from the 'a' and 'n2' scales, resp. ## ## Note that the numerators of the 2 terms in absolute value marks (and ## raised to the exponents n2 and n3) are sin and cos which have a max ## value of 1. If n1 = n2 = n3 = 2 and a = b, for example, then the ## formula looks like ## r = ( |cos(...)/a| ^ 2 + |sin(...)/a| ^ 2 ) ^ -1/2 ## And the fundamental trigonometric identity ## cos(...) ^ 2 + sin (...) ^ 2 = 1 ## allows us to simplify this to ## r = (a ^ -2) ^ -1/2 = a ## So for r(theta) to be on the order of 200 (pixels), the denominators ## (a and b) will generally need to be on the order of 200 --- when ## n1 = n2 = n3 = 2 and a = b. ## ## Note that when n1 = 4, then the last equation above becomes ## r = (a ^ -2) ^ -1/4 = a ^ 1/2 = sqrt( a ) ## So (when a & b > 1) the bigger n1 becomes, the smaller the radius becomes. ## And (when a & b < 1) the bigger n1 becomes, the larger the radius becomes ## (but it is less than 1). ## So n1 can be used to zoom in/out of the view of the image/shape. ## ## If we look at the 'resolution' and range of the scales and ## initial values in the Gerard Sookahet demo code, we see: ## Resolution From To Initial Value ## ---------- ---- ---- ------------- ## m - 0.2 0 34 6.0 ## n1 - 0.1 0.1 17 3.0 ## n2 - 0.1 0 17 1.0 ## n3 - 0.1 0 17 1.0 ## a - 0.01 0.1 10 1.0 ## b - 0.01 0.1 10 1.0 ## ## (We will change resolution of 'm' to 2.0 --- to avoid 'cusps' from ## appearing on the right of the shape, at theta = 0, as the m-scale ## sliderbar is moved. ## The shape should then remain smooth --- no partial humps with ## sharp-pointed 'drop-offs' --- as 'm' is changed.) ## ##+####### ## METHOD: The GUI made by this Tk script contains a rectangular ## canvas widget on which the color-filled superformula shape ## will be drawn. ## ## The GUI includes 6 'scale' widgets whose slider-bars can ## be used to change the values of m, n1, n2, n3, a, and b. ## ## The GUI also includes a button to call a color selector GUI ## to set the 'fill' color of the super-formula shape. ## ## Another button is used to call the same color selector GUI ## to set the 'outline' color of the super-formula shape. ## ## Another button calls the same color selector GUI to set a ## background color --- the color of the canvas. ## ## A checkbutton widget allows the user to specify whether ## the polygon should be filled with a color (other than the ## background color) --- i.e. whether only an outline should be ## drawn. ## ## A redraw includes clearing the canvas and redrawing the series ## of lines that make up a polygon that approximates the ## super-formula shape. ## ## A checkbutton widget allows the user to specify whether ## the canvas should be cleared before a redraw. ## ## A redraw should be done (a) whenever any of the 6 scales change, ## (b) whenever a color button is used to change a color, ## (c) whenever the fill checkbutton is clicked, and (d) whenever ## the window (and thus the canvas) is resized --- so that the ## image/shape will be redrawn in the center of the canvas. ## ## (Like G. Sookahet, we break up 360 degrees into N=400 angles to draw ## the shape with a polygon of 400 edges. We have added a scale widget ## to set N. The user can experiment to see how many 'wedges' are needed ## to make a smooth shape --- AND the user can see when the redraws ## become slowed down too much by large N. Amazingly, even a ## thousand-edged polygon can be calculated within a fraction of a ## second --- even when fractional exponentials are being calculated ## via the Tcl 'pow' function. ## ## In testing, I found that if the redraw is done 'dynamically' with ## the '-command' option of the 'scale' widget, a redraw seems to be ## done as the GUI is initially built --- a redraw for each scale ## widget. To eliminate this extraneous processing, I have defined ## a button1-release binding on each of the scale widgets to ## trigger the redraw --- only when the user finishes dragging ## the sliderbar of any scale. ## ## However, it should be pointed out that as long as erasing the ## canvas and calculating-redrawing the super-formula shape completes ## within a small fraction of a second, it is feasible to do the redraws ## 'dynamically' with each sliderbar, via the '-command' option.) ## ## 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 image file. ## ## The editor could also be used to blur the image slightly to ## 'feather' the edges of the polygon. ## ## The colored image file could be used with a utility (like the ## ImageMagick 'convert' command) to change the outer, 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 GUIs. ## ## 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. OR, the shape can be ## used as an underlying pattern to reproduce the shape as a ## scalable shape, by using curve drawing tools of the editor. ## ##+######################################################################## ## '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). ## ## 1) Define ALL frames (and sub-frames). Pack them. ## ## 2) Define all widgets in the frames. Pack them. ## ## 3) Define keyboard or mouse action BINDINGS, if needed. ## ## 4) Define PROCS, if needed. ## ## 5) Additional GUI INITIALIZATION (with procs), if needed. ## ## ## Some detail about the code structure of this particular script: ## ## 1a) Define ALL frames: ## ## Top-level : '.fRleft' , '.fRright' ## ## Sub-frames: '.fRleft.fRbuttons' ## '.fRleft.fRscaleM' ## '.fRleft.fRscaleN1' ## '.fRleft.fRscaleN2' ## '.fRleft.fRscaleN3' ## '.fRleft.fRscaleA' ## '.fRleft.fRscaleB' ## '.fRleft.fRscaleN' ## '.fRleft.fRinfo' ## ## The canvas widget will go in '.fRright'. ## ## 1b) Pack ALL frames. ## ## 2) Define all widgets in the frames (and pack them): ## ## - In '.fRleft.fRbuttons': ## 1 button widget ('Exit'), ## and ## 3 buttons (for setting the super-formula shape's ## fill & outline colors and the ## background/canvas color), ## and ## 1 label widget to display the current color ## values (in hex). ## ## - In '.fRleft.fRscaleX': ## 1 'label' and 1 'scale' widget, in each scale-frame ## for superformula shape parameters --- ## m, n1, n2, n3, a, b --- and N. ## (We added a scale for N, the number of ## polygon edges to be drawn.) ## ## - In '.fRleft.fRinfo': ## 1 label widget to show 'how-to' some usage info and ## the current color values. ## 1 label widget to show the superformula --- and ## maybe current super-formula parameter values. ## ## - In '.fRright': 1 'canvas' widget ## ## 3) Define bindings: ## ## - a button1-release on the fill/no-fill checkbutton causes a redraw ## ## - a button1-release on each of the 7 scale widgets causes a redraw ## ## NOTE1: We do not need a button1-release binding for the ## 'ClearBeforeRedraw' checkbutton. ## ## NOTE3: 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 ## polygon that approximates the super-formula shape ## --- for the current values of the 7 scale ## parameters and the fill & outline colors. ## ## - 'set_shape_color1' - shows a color selector GUI and uses the ## user-selected color to 'fill' the ## super-formula shape on the canvas ## ## - 'set_shape_color2' - shows a color selector GUI and uses the ## user-selected color to 'outline' the ## super-formula shape 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 ## ## 5) Additional GUI initialization: Execute proc 'ReDraw' once with ## an initial, example set of parms ## --- 7 acale vars, COLOR1hex, COLOR2hex, ## COLORbkGNDhex --- ## to start with a super-formula 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 2012sep04 ## Changed by: ...... ......... 2012 ##+####################################################################### ##+####################################################################### ## Set general window parms (title,position,size,color-scheme,fonts,etc.). ##+####################################################################### wm title . "Colored 'Super-formula shape' on a Colored Canvas" wm iconname . "Superformula" wm geometry . +15+30 ## 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 ##+###################################################### ## Set the color scheme for the window and its widgets --- ## and set the initial color for the polygon interior ## and the canvas background (outside the polygon). ##+###################################################### tk_setPalette "#e0e0e0" ## Initialize the super-formula shape 'fill' color. # set COLOR1r 255 # set COLOR1g 255 # set COLOR1b 255 set COLOR1r 255 set COLOR1g 0 set COLOR1b 255 set COLOR1hex [format "#%02X%02X%02X" $COLOR1r $COLOR1g $COLOR1b] ## Initialize the super-formula shape 'outline' color. # set COLOR2r 255 # set COLOR2g 255 # set COLOR2b 0 set COLOR2r 0 set COLOR2g 255 set COLOR2b 255 set COLOR2hex [format "#%02X%02X%02X" $COLOR2r $COLOR2g $COLOR2b] ## Initialize the background color for the canvas. # 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" ##+######################################################## ## Use a VARIABLE-WIDTH FONT for label and button widgets. ## ## Use a FIXED-WIDTH FONT for listboxes (and ## entry fields, 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) ##+########################################################### 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 BDwidthPx_label 2 ## SCALE geom parameters: set BDwidthPx_scale 2 set initScaleLengthPx 300 set scaleWidthPx 10 ##+################################################################### ## Set a MINSIZE of the window. ## ## For width, allow for the minwidth of the '.fRbuttons' frame: ## about 4 buttons (Exit,Color1,Color2,ColorBkgnd), and ## 2 checkbuttons. ## ## For height, allow ## 2 chars high for the '.fRbuttons' frame, ## 200 pixels high for the '.fRscales' frame, and ## 3 chars high for the label widget in the '.fRinfo' frame. ##+################################################################### set minWinWidthPx [font measure fontTEMP_varwidth \ "Exit Fill Outline Background Fill Shape Clear Before Draw"] ## Add some pixels to account for right-left-side window decoration ## (about 8 pixels), about 6 x 4 pixels/widget for borders/padding for ## 6 widgets --- 4 buttons and 2 checkbuttons. set minWinWidthPx [expr 32 + $minWinWidthPx] ## MIN HEIGHT --- ## for the 9 sub-frames '.fRleft.fRbuttons', 7 '.fRleft.fRscaleX', ## and '.fRleft.fRinfo'. ## Allow ## 2 char high for 'fRbuttons' ## 2 char high for each of the 7 scale frames ## 6 char high for 'fRinfo' set CharHeightPx [font metrics fontTEMP_varwidth -linespace] set minWinHeightPx [expr 10 * $CharHeightPx] ## Add about 28 pixels for top-bottom window decoration, ## about 9x4 pixels for each of the 9 stacked frames and their ## widgets (their borders/padding). set minWinHeightPx [expr $minWinHeightPx + 64] ## FOR TESTING: # puts "minWinWidthPx = $minWinWidthPx" # puts "minWinHeightPx = $minWinHeightPx" wm minsize . $minWinWidthPx $minWinHeightPx ##+################################################################ ## DEFINE *ALL* THE FRAMES: ## ## Top-level : '.fRleft' '.fRright' ## ## Sub-frames: '.fRleft.fRbuttons' ## '.fRleft.fRscaleM' ## '.fRleft.fRscaleN1' ## '.fRleft.fRscaleN2' ## '.fRleft.fRscaleN3' ## '.fRleft.fRscaleA' ## '.fRleft.fRscaleB' ## '.fRleft.fRscaleN' ## '.fRleft.fRinfo' ##+################################################################ # 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.fRscaleM -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRleft.fRscaleN1 -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRleft.fRscaleN2 -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRleft.fRscaleN3 -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRleft.fRscaleA -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRleft.fRscaleB -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRleft.fRscaleN -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRleft.fRinfo -relief $RELIEF_frame -borderwidth $BDwidth_frame ##+############################## ## PACK the top-level FRAMES. ##+############################## pack .fRleft \ -side left \ -anchor nw \ -fill x \ -expand 0 pack .fRright \ -side left \ -anchor nw \ -fill both \ -expand 1 ##+############################## ## PACK the sub-FRAMES. ##+############################## pack .fRleft.fRbuttons \ .fRleft.fRscaleM \ .fRleft.fRscaleN1 \ .fRleft.fRscaleN2 \ .fRleft.fRscaleN3 \ .fRleft.fRscaleA \ .fRleft.fRscaleB \ .fRleft.fRscaleN \ .fRleft.fRinfo \ -side top \ -anchor nw \ -fill x \ -expand 0 ##+######################################################### ## OK. Now we are ready to define the widgets in the frames. ##+######################################################### ##+##################################################################### ## In the '.fRleft.fRbuttons' FRAME --- DEFINE-and-PACK ## - an exit-button, ## and ## - 2 buttons ( to specify colors) ## and ## - a label widget, to show current color values (in hex) ## --- and if there is room, show the current ## values of the 6 scale parameters ##+##################################################################### button .fRleft.fRbuttons.buttEXIT \ -text "Exit" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {exit} button .fRleft.fRbuttons.buttCOLOR1 \ -text "\ Fill Color" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_shape_color1" button .fRleft.fRbuttons.buttCOLOR2 \ -text "\ Outline Color" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_shape_color2" button .fRleft.fRbuttons.buttCOLORbkGND \ -text "\ Background Color" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_background_color" set fill0or1 1 checkbutton .fRleft.fRbuttons.chkbuttFILL \ -text "Fill shape" \ -font fontTEMP_varwidth \ -variable fill0or1 \ -selectcolor "#cccccc" \ -relief raised \ -command "ReDraw 0" set cleanBeforeDraw 1 checkbutton .fRleft.fRbuttons.chkbuttCLEAR \ -text "Clean Before Draw" \ -font fontTEMP_varwidth \ -variable cleanBeforeDraw \ -selectcolor "#cccccc" \ -relief raised \ -command "" ##+########################################### ## Pack the widgets in the 'fRbuttons' frame. ##+########################################### pack .fRleft.fRbuttons.buttEXIT \ .fRleft.fRbuttons.buttCOLOR1 \ .fRleft.fRbuttons.buttCOLOR2 \ .fRleft.fRbuttons.buttCOLORbkGND \ .fRleft.fRbuttons.chkbuttFILL \ .fRleft.fRbuttons.chkbuttCLEAR \ -side left \ -anchor w \ -fill none \ -expand 0 ##+################################################################## ## In the '.fRleft.fRscaleM' FRAME ---- DEFINE-and-PACK ## 1 LABEL and 1 SCALE widget. ##+################################################################### label .fRleft.fRscaleM.label \ -text "m" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_label set m 12 scale .fRleft.fRscaleM.scaleM \ -from 0 -to 34 \ -resolution 2 \ -length $initScaleLengthPx \ -font fontTEMP_SMALL_varwidth \ -variable m \ -showvalue true \ -orient hor \ -bd $BDwidthPx_scale \ -width $scaleWidthPx # -command "ReDraw" pack .fRleft.fRscaleM.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRscaleM.scaleM \ -side left \ -anchor w \ -fill x \ -expand 1 ##+################################################################## ## In the '.fRleft.fRscaleN1' FRAME ---- DEFINE-and-PACK ## 1 LABEL and 1 SCALE widget. ##+################################################################### label .fRleft.fRscaleN1.label \ -text "n1" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_label set n1 6.0 scale .fRleft.fRscaleN1.scaleN1 \ -from 0.1 -to 17 \ -resolution 0.1 \ -length $initScaleLengthPx \ -font fontTEMP_SMALL_varwidth \ -variable n1 \ -showvalue true \ -orient hor \ -bd $BDwidthPx_scale \ -width $scaleWidthPx # -command "ReDraw" pack .fRleft.fRscaleN1.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRscaleN1.scaleN1 \ -side left \ -anchor w \ -fill x \ -expand 1 ##+################################################################## ## In the '.fRleft.fRscaleN2' FRAME ---- DEFINE-and-PACK ## 1 LABEL and 1 SCALE widget. ##+################################################################### label .fRleft.fRscaleN2.label \ -text "n2" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_label set n2 6.0 scale .fRleft.fRscaleN2.scaleN2 \ -from 0 -to 17 \ -resolution 0.1 \ -length $initScaleLengthPx \ -font fontTEMP_SMALL_varwidth \ -variable n2 \ -showvalue true \ -orient hor \ -bd $BDwidthPx_scale \ -width $scaleWidthPx # -command "set_scale_n3_to_n2_redraw" pack .fRleft.fRscaleN2.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRscaleN2.scaleN2 \ -side left \ -anchor w \ -fill x \ -expand 1 ##+################################################################## ## In the '.fRleft.fRscaleN3' FRAME ---- DEFINE-and-PACK ## 1 LABEL and 1 SCALE widget. ##+################################################################### label .fRleft.fRscaleN3.label \ -text "n3" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_label set n3 6.0 scale .fRleft.fRscaleN3.scaleN3 \ -from 0 -to 17 \ -resolution 0.1 \ -length $initScaleLengthPx \ -font fontTEMP_SMALL_varwidth \ -variable n3 \ -showvalue true \ -orient hor \ -bd $BDwidthPx_scale \ -width $scaleWidthPx # -command "ReDraw" pack .fRleft.fRscaleN3.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRscaleN3.scaleN3 \ -side left \ -anchor w \ -fill x \ -expand 1 ##+################################################################## ## In the '.fRleft.fRscaleA' FRAME ---- DEFINE-and-PACK ## 1 LABEL and 1 SCALE widget. ##+################################################################### label .fRleft.fRscaleA.label \ -text "a" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_label set a 100.0 scale .fRleft.fRscaleA.scaleA \ -from 0.1 -to 1000 \ -resolution 0.1 \ -length $initScaleLengthPx \ -font fontTEMP_SMALL_varwidth \ -variable a \ -showvalue true \ -orient hor \ -bd $BDwidthPx_scale \ -width $scaleWidthPx # -command "set_scale_b_to_a_redraw" pack .fRleft.fRscaleA.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRscaleA.scaleA \ -side left \ -anchor w \ -fill x \ -expand 1 ##+################################################################## ## In the '.fRleft.fRscaleB' FRAME ---- DEFINE-and-PACK ## 1 LABEL and 1 SCALE widget. ##+################################################################### label .fRleft.fRscaleB.label \ -text "b" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_label set b 100.0 scale .fRleft.fRscaleB.scaleB \ -from 0.1 -to 1000 \ -resolution 0.1 \ -length $initScaleLengthPx \ -font fontTEMP_SMALL_varwidth \ -variable b \ -showvalue true \ -orient hor \ -bd $BDwidthPx_scale \ -width $scaleWidthPx # -command "ReDraw" pack .fRleft.fRscaleB.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRscaleB.scaleB \ -side left \ -anchor w \ -fill x \ -expand 1 ##+################################################################## ## In the '.fRleft.fRscaleN' FRAME ---- DEFINE-and-PACK ## 1 LABEL and 1 SCALE widget. ##+################################################################### label .fRleft.fRscaleN.label \ -text "N" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_label set N 400 scale .fRleft.fRscaleN.scaleN \ -from 10 -to 1000 \ -resolution 1 \ -length $initScaleLengthPx \ -font fontTEMP_SMALL_varwidth \ -variable N \ -showvalue true \ -orient hor \ -bd $BDwidthPx_scale \ -width $scaleWidthPx # -command "ReDraw" pack .fRleft.fRscaleN.label \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRleft.fRscaleN.scaleN \ -side left \ -anchor w \ -fill x \ -expand 1 ##+###################################################### ## DEFINE-and-PACK a 'label' widget ## in the '.fRleft.fRinfo' FRAME ##+###################################################### label .fRleft.fRinfo.labelCOLORS \ -text "" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_button ## The text for labelCOLORS is set in the ReDraw proc. label .fRleft.fRinfo.labelPARMS \ -text "\ Super-formula: r(theta) = ( |cos(m x theta/4) / a| ^ n2 + |sin(m x theta/4) / b| ^ n3 ) ^ -1/n1 and the x,y coordinates on the super-formula shape curve are given by r x cos(theta) & r x sin(theta). N is # of polygon edges used to draw the curve." \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_button pack .fRleft.fRinfo.labelCOLORS \ .fRleft.fRinfo.labelPARMS \ -side top \ -anchor nw \ -fill x \ -expand 0 ##+###################################################### ## DEFINE-and-PACK the 'canvas' widget ## in the '.fRright' FRAME ##+###################################################### canvas .fRright.can \ -width $initCanWidthPx \ -height $initCanHeightPx \ -relief raised \ -borderwidth $BDwidthPx_canvas pack .fRright.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 '.fRright.can'.) ## We move this statement to the bottom of this script. # bind .fRright.can "ReDraw 0" bind .fRleft.fRbuttons.chkbuttFILL "ReDraw 0" bind .fRleft.fRscaleM.scaleM "ReDraw 0" bind .fRleft.fRscaleN1.scaleN1 "ReDraw 0" bind .fRleft.fRscaleN2.scaleN2 "set_scale_n3_to_n2_redraw 0" bind .fRleft.fRscaleN3.scaleN3 "ReDraw 0" bind .fRleft.fRscaleA.scaleA "set_scale_b_to_a_redraw 0" bind .fRleft.fRscaleB.scaleB "ReDraw 0" bind .fRleft.fRscaleN.scaleN "ReDraw 0" ##+###################################################################### ## PROCS SECTION: ## ## - ReDraw - Called by the '-command' option on the ## 7 scale widgets originally (now by the bindings ## on button1-release, above), by a checkbutton binding, ## and by the Color procs. ## ## Draws the super-formula shape on the canvas for ## the current scale parameter values and for the ## current color var values. ## ## - set_scale_n3_to_n2_redraw - called by '-command' for scale-n3 ## ## - set_scale_b_to_a_redraw - called by '-command' for scale-b ## ## - set_shape_color1 - called by color1 (fill) button '-command' ## ## - set_shape_color2 - called by color2 (outline) button '-command' ## ## - set_background_color - called by background color button '-command' ## ##+####################################################################### ##+##################################################################### ## proc ReDraw - ## ## PURPOSE: ## Draws the super-formula shape on the canvas. ## ## CALLED BY: bindings (in the BINDINGS section) --- and by set-color procs. ## ## 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 in the math statements below, the 'superformula' is: ## r(theta) = ## ( |cos(m*theta/4)/a| ^ n2 + |sin(m*theta/4)/b| ^ n3 ) ^ -1/n1 ##+##################################################################### proc ReDraw {x} { global m n1 n2 n3 a b N \ cleanBeforeDraw fill0or1 \ COLOR1hex COLOR2hex COLORbkGNDhex set twopi [expr 2 * 3.14159265358] ## Get the center of the canvas. set x0Px [expr [winfo width .fRright.can] / 2] set y0Px [expr [winfo height .fRright.can] / 2] ## Number of edges/interior-angles for drawing ## the polygon that approximates the superformula shape. ## GS hard-coded the value. ## (We provide a widget on the GUI for the user ## to change this value --- say, in a range of 10 ## to 1000.) # set N 400 ## Provide a size-factor to allow for easily adjusting ## the size of the image within the canvas. ## GS provided a value of 0.4. We hope to prove that ## this factor is not needed, and we set it to 1.0. set sizeFactor 1.0 ## Initialize the points list for the polygon. set listPts {} ## Calculate the x,y coords --- r*cos(theta) and r*sin(theta) ## --- of the points on the superformula curve, where r is given by ## the superformula in terms of theta, where theta is in radians. ## How we do this: ## In a loop from 0 to N, calculate each theta, between 0 and 2*pi radians, ## and calculate r. Then add each x,y pair to the points list. for {set i 0} {$i<=$N} {incr i} { set theta [expr {$i*$twopi/$N}] ## For this theta, calculate r. set t [expr {$m*$theta/4.0}] ## FOR TESTING: # if { $i == 0 || $i == 50 || $i == 100 } { # puts "theta: $theta ; m: $m ; t: $t" # } set term1 [expr {abs(cos($t)/$a)}] set term1 [expr {pow($term1,$n2)}] set term2 [expr {abs(sin($t)/$b)}] set term2 [expr {pow($term2,$n3)}] set r [expr {pow($term1+$term2,1.0/$n1)}] ## Note that, above, when n1=n2=n3=2 that we are ## computing the square root of the sum of the ## squares of the cos(...)/a and sin(...)/b terms. ## This statement essentially applies the ## minus sign of the exponent -1/n1. set r [expr {1.0/$r}] ## FOR TESTING: # if { $i == 0 || $i == 50 || $i == 100 } { # puts "m: $m ; n1: $n1 ; n2: $n2 ; n3: $n3 ; a: $a ; b: $b ; N = $N" # puts "r: $r ; term1: $term1 ; term2: $term2" # } ## Append the new point (in pixels, on the canvas, ## relative to the center of the canvas) ## to the list of points. if {[expr {abs($r)}] <= 0.1} { lappend listPts 0 0 } else { lappend listPts \ [expr $x0Px + round($sizeFactor*$r*cos($theta))] \ [expr $y0Px + round($sizeFactor*$r*sin($theta))] } ## END OF if {[expr {abs($r)}] <= 0.1} } ## END OF LOOP for {set i 0} {$i<=$N} {incr i} if $cleanBeforeDraw {.fRright.can delete all} set edgeWidthPx 1 if $fill0or1 { .fRright.can create polygon $listPts \ -outline $COLOR2hex -width $edgeWidthPx -fill $COLOR1hex } else { .fRright.can create polygon $listPts \ -outline $COLOR2hex -width $edgeWidthPx } ## END OF if $fill0or1 # set polyID [eval .fRright.can create polygon $listPts] # if $fill0or1 { # .fRright.can itemconfigure $polyID \ # -outline $COLOR2hex -width $edgeWidthPx -fill $COLOR1hex # } else { # .fRright.can itemconfigure $polyID \ # -outline $COLOR2hex -width $edgeWidthPx -fill $COLORbkGNDhex # } ## END OF if $fill0or1 ## Make sure the text on the COLORS and PARMS label widgets ## is up to date. .fRleft.fRinfo.labelCOLORS configure -text "\ Click on the 1st four troughs to step through changes. Drag the slider of the last 3 scales to see significant change. Colors: Fill - $COLOR1hex ; Outline - $COLOR2hex ; Background - $COLORbkGNDhex" # .fRleft.fRinfo.labelPARMS configure -text "\ # Current super-formula shape Parameters: # m = $m ; n1 = $n1 ; n2 = $n2 ; n3 = $n3 ; a = $a ; b = $b # in equation # r(theta) = ( |cos(m*theta/4)/a| ^ n2 + |sin(m*theta/4)/b| ^ n3 ) ^ -1/n1 " } ## END OF proc 'ReDraw' ##+########################################## ## proc set_scale_n3_to_n2_redraw ##+########################################## proc set_scale_n3_to_n2_redraw {x} { global n2 n3 set n3 $n2 ReDraw 0 } ## END OF proc 'set_scale_n3_to_n2_redraw' ##+########################################## ## proc set_scale_b_to_a_redraw ##+########################################## proc set_scale_b_to_a_redraw {x} { global a b set b $a ReDraw 0 } ## END OF proc 'set_scale_b_to_a_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 value to set a 'fill' color. ## ## Arguments: none ## ## CALLED BY: .fRleft.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 geometry in the new interior 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 value to set an 'outline' color. ## ## Arguments: none ## ## CALLED BY: .fRleft.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 geometry in the new outline 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 value to set the color of the canvas --- ## on which all the tagged items (lines) lie. ## ## Arguments: none ## ## CALLED BY: .fRleft.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 ## Set the color of the canvas. .fRright.can config -bg $COLORbkGNDhex } ## END OF proc 'set_background_color' ##+##################################################### ## Additional GUI initialization, if needed (or wanted). ##+##################################################### ## 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 statement because ReDraw does not (re)set ## the background/canvas color. .fRright.can config -bg $COLORbkGNDhex ## Rather than use the 'update' & 'ReDraw' statements to initialize ## the GUI, we can kill 2 birds with one stone via the 'bind' below. ## If the window (and canvas) is resized, this bind causes the ## shape to be redrawn in the middle of the new canvas area. ## This bind is also triggered (twice?) when this script falls into ## the Tk event handling loop and the canvas is first configured. bind .fRright.can "ReDraw 0"