#!/usr/bin/wish -f ## ## SCRIPT: draw_superEllipse_colorFilled_onColorBkgnd.tk ## ## ## PURPOSE: This Tk GUI script facilitates the creation of a color-filled ## 'super-ellipse' --- via lines 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 'Mathematics jewels' at http://wiki.tcl.tk/10414 --- ## by 'ulis' in 2003 November. On that page, 'ulis' points ## out that the equation that describes a 'super-ellipse' is: ## ## |x/a|^n + |y/b|^n = 1 ## ## * With n = 1 you obtain a rhombus (diamond shape). ## * With n = 2 you already got an ellipse. ## * With n > 2 you obtain a rounded rectangle! The more the power, ## the more the rectangle. ## * With n = 2/3 you obtain an astroid. ## ################## ## A 3D EFFECT: ## 'ulis' gave his drawings of the filled super-ellipse a shaded, 3D effect: ## ## The 3D effect is fairly simple to obtain: ## ## Just compute the value ## v = |x/a|^n + |y/b|^n ## for each point inside the shape. ## ## By definition the value of v is 1 on the border and declines ## to 0 towards the center. ## ## Computing 255 * (1.0 - $v) gives us the color component of ## the 3D effect. ################### ## ## METHOD: The GUI made by this Tk script contains a rectangular ## canvas widget on which the color-filled super-ellipse will be ## drawn. ## ## Note that the values of x,y such that ## |x/a|^n + |y/b|^n is less-than-or-equal-to 1 ## are the values that fill the interior of the super-ellipse. ## ## Furthermore, note that for all points in the interior ## x is between -a and a and y is between -b and b. ## ## For the purpose of drawing the image on the canvas, ## we let x, y, -a, a, -b, and b be given in pixels (integers) rather than ## floating point numbers. ## ## We can fill the interior of the super-ellipse via vertical lines drawn ## as x goes from -a to a. The y-values of the end-points of ## the vertical lines lie between -b and b. And one can find the ## y-value of the end-point for any x by decrementing y from b until ## |x/a|^n + |y/b|^n is less-than-or-equal-to 1. ## ## The GUI includes a 'minilistbox' widget that displays a list ## of useful options for the exponent 'n' --- such as ## 0.5, 0.667, 0.8, 1, 2, 2.5, 3, 4, 5, 6, 8, 10, 12. ## ## The GUI could include 2 'scale' widgets whose slider-bars can ## be used to change the values of a and b --- to a max of ## half the width and height of the screen, say. ## ## OR, we could set the value of a and b to be about 80% of ## half the current width and height of the canvas --- and ## change the size of the super-ellipse by changing the size ## of the canvas (by resizing the window) --- with a redraw ## being done whenever the window is resized. ## ## The GUI also includes a button to call a color selector GUI ## to set the fill color of the super-ellipse. ## ## [Note that if we ever want to implement the 3D effect that ## 'ulis' implemented (as described above), then we could use ## TWO buttons to call a color selector GUI to set the 2 colors ## to gradiate from and to, from the center of the super-ellipse ## to the outer edge.] ## ## Another button calls the same color selector GUI to set a ## background color --- the color of the canvas. ## ## There is a binding to the 'minilistbox' widget that ## is used to the redraw proc whenever a new value of the ## exponent 'n' is chosen. ## ## The redraw includes clearing the canvas and redrawing the series ## of lines that fill up the super-ellipse. ## ## (If the redraw takes more than half-a-second, then we can use ## a button1-release binding on the scale widgets for a and b ## to trigger the redraw --- only when the user finishes dragging ## the sliderbar of either scale. ## ## If erasing the canvas and redrawing the super-ellipse ## completes within a very small fraction of a second, it will ## be feasible to do the redraws 'dynamically' with the sliderbar.) ## ## 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 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. ## ##+######################################################################## ## '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 : '.fRbuttons' , '.fRimgspecs' , '.fRcan' ## ## Sub-frames: none ## ## 1b) Pack ALL frames. ## ## 1c) Define a 'minilistbox' proc that is used to make a couple of ## COMPACT LIST-SELECTION WIDGETS for use in step 2 below --- to serve ## in place of the old-fashioned 'tk_optionMenu' widget, and yet ## to avoid using a newer widget like 'spinbox' that is ## not available to users of older 8.x wish interpreters ## or the really-old 7.x interpreters. ## ## 2) Define all widgets in the frames (and pack them): ## ## - In '.fRbuttons': 1 button widget ('Exit'), ## and ## 2 (or 3) buttons (for setting the super-ellipse ## color and the background/canvas color), ## and ## 1 label widget to display current super-ellipse ## parameter values such as n, a, and b. ## ## - In '.fRimgspecs': 1 'minilistbox' widget to specify the exponent 'n' ## for a super-ellipse --- from a list such as ## 0.5, 0.667, 0.8, 1, 2, 2.5, 3, 4, 5, 6, 8, 10, 12. ## ## And, perhaps someday, ## 2 'scale' widgets, to 'dynamically' change the ## a and b parameters of the super-ellipse. ## ## - In '.fRcan': 1 'canvas' widget ## ## 3) Define bindings: ## ## - a (resize) event on the window should cause a redraw ## ## NOTE: The following 2 (or 3) color changes should trigger a redraw. ## The redraws can be done in procs that are used to ## set each of the 2 (or 3) colors --- but, if the redraw is not ## done at the end of each proc, the redraw could be done ## via a button1-release binding on the 3 color-change buttons. ## ## - change of either of the fill color (or 2 gradient colors) for ## the super-ellipse should cause a redraw ## --- i.e. the commands for the 1 or 2 super-ellipse color-setting ## buttons should end with a redraw ## ## - change of the background (canvas) color MAY need to cause a redraw ## --- i.e. the command for the background-color-setting button ## MAY need to end with a redraw ## ## 4) Define procs: ## ## - 'ReDraw' - to clear the canvas and redraw the ## vertical lines across the super-ellipse ## --- for the current values of a, b, n, and ## the fill color. ## ## - 'set_ellipse_color1' - shows a color selector GUI and uses the ## user-selected color to redraw the ## super-ellipse 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 ## --- a, b, n, COLOR1hex, ## COLORbkGNDhex --- ## to start with a super-ellipse 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 2012sep04 ## Changed by: ...... ......... 2012sep08 Add an "AntiAlias' checkbox and ## associated capability. ##+####################################################################### ##+####################################################################### ## Set general window parms (title,position,size,color-scheme,fonts,etc.). ##+####################################################################### wm title . "Color 'Super-Ellipse' on a Canvas" wm iconname . "SuperEllipse" wm geometry . +15+30 ##+###################################################### ## 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-ellipse color (or 2 gradient colors) ## and the background color for the canvas. # 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] # 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 200 ## LISTBOX geom parameters: set listboxWIDTHchars 3 ##+################################################################### ## Set a MINSIZE of the window. ## ## For width, allow for the minwidth of the '.fRbuttons' frame: ## about 3 buttons (Exit,Color1,ColorBkgnd), and ## a label with current super-ellipse parameter info. ## ## For height, allow for a canvas at least 24 pixels high, and ## 3 small-chars high for the 'minilistbox' height in the ## '.fRimgspecs' frame, and ## 2 chars high for the widgets in the '.fRbuttons' frame. ##+################################################################### set minWinWidthPx [font measure fontTEMP_varwidth \ "Exit Super-ellipse Background Colors:"] ## Add some pixels to account for right-left-side window decoration ## (about 8 pixels), about 4 x 8 pixels/widget for borders/padding for ## 6 widgets --- 3 buttons and 1 label. set minWinWidthPx [expr 40 + $minWinWidthPx] ## MIN HEIGHT --- ## for the 3 frames 'fRbuttons' 'fRimgspecs' 'fRcan'. ## Allow ## 1 char high for 'fRbuttons' ## 2 chars high for 'fRimgspecs' ## 2 chars high for 'fRcan' set CharHeightPx [font metrics fontTEMP_varwidth -linespace] set minWinHeightPx [expr $minCanHeightPx + 5 * $CharHeightPx] ## Add about 28 pixels for top-bottom window decoration, ## about 3x8 pixels for each of the 3 stacked frames and their ## widgets (their borders/padding). set minWinHeightPx [expr $minWinHeightPx + 52] ## 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' '.fRimgspecs' 'fRcan' ## ## Sub-frames: none ##+################################################################ # 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 .fRimgspecs -relief raised -borderwidth 2 frame .fRcan -relief $RELIEF_frame -borderwidth $BDwidth_frame ##+############################## ## PACK the top-level FRAMES. ##+############################## pack .fRbuttons \ .fRimgspecs \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRcan \ -side top \ -anchor nw \ -fill both \ -expand 1 ##+######################### ## DEFINE PROC 'minilistbox' ## (for use in making a couple of widgets below) ##+############################################################## ## By using the global variables ## - fontTEMP_SMALL_fixedwidth ## - fontTEMP_SMALL_varwidth ## - listboxBKGD ## for the decorative & geometric elements/parameters of the GUI, ## we keep the arguments of this widget-made-on-the-fly down ## to the 6 MAIN ELEMENTS/VARIABLES --- 4 INPUT AND 1 OUTPUT AND 1 CMD: ## ## - the parent widget/window, ## ## - an option/line at which to initially position the list in ## the listbox (with the 'see' command), ## ## - an options list, ## ## - width (in chars) of the listbox ## ## - the name of the variable that is to hold the user-selected option, ## i.e. a list-line (the result/output) ## --- retrieved from the listbox with 'curselection' and 'get', ## ## - a command (proc --- and parameters, if any) to be executed at a ## button1-release on this widget's frame. ##+############################################################## proc minilistbox {w opt1 optslist listboxWIDTHchars seloptvar mlbProc} { global fontTEMP_SMALL_fixedwidth fontTEMP_SMALL_varwidth \ listboxBKGD ##+##################################### ## DEFINE-and-PACK the widget SUB-FRAMES: ## '.frup-down' for 2 up and down buttons ## and '.fRopts' for the listbox. ## Pack them side by side. ##+##################################### frame $w.fRup-down -relief flat -bd 2 frame $w.fRopts -relief flat -bd 2 pack $w.fRup-down \ $w.fRopts \ -side left \ -anchor w \ -fill y \ -expand 0 ##+#################################################### ## In FRAME '.fRup-down', ## DEFINE-and-PACK a top-spacer label and 2 buttons. ##+#################################################### ## We comment-out this label definition (and its pack statement) ## to reduce the height of this 'minilistbox' widget. ## See the label definition statement for frame .fRopts, below. # label $w.fRup-down.label \ # -text " " \ # -anchor w \ # -relief flat button $w.fRup-down.buttUP \ -text "Up" \ -font fontTEMP_SMALL_varwidth \ -width 3 -height 1 \ -pady 1 \ -padx 0 \ -command [list $w.fRopts.listbox yview scroll -1 unit] button $w.fRup-down.buttDOWN \ -text "Dwn" \ -width 3 -height 1 \ -font fontTEMP_SMALL_varwidth \ -pady 1 \ -padx 0 \ -command [list $w.fRopts.listbox yview scroll +1 unit] # pack $w.fRup-down.label \ # -side top \ # -anchor n \ # -fill none \ # -expand 0 pack $w.fRup-down.buttUP \ $w.fRup-down.buttDOWN \ -side top \ -anchor n \ -fill none \ -expand 0 ##+#################################################### ## In FRAME '.fRopts', ## DEFINE-and-PACK an info label and a listbox widget. ##+#################################################### ## We comment-out this label definition (and its pack statement) ## to reduce the height of this 'minilistbox' widget. ## The user could supply a label, say to the left of this ## 'minilistbox' widget, using a label-def in their Tk script. # label $w.fRopts.label \ # -text "Up/dwn ; click a line:" \ # -font fontTEMP_SMALL_varwidth \ # -anchor w \ # -relief flat listbox $w.fRopts.listbox \ -font fontTEMP_SMALL_fixedwidth \ -height 3 \ -width $listboxWIDTHchars \ -bg "$listboxBKGD" \ -state normal foreach optline $optslist { $w.fRopts.listbox insert end $optline } # pack $w.fRopts.label \ # -side top \ # -anchor n \ # -fill x \ # -expand 0 pack $w.fRopts.listbox \ -side top \ -anchor n \ -fill x \ -expand 0 ##+################################################### ## POSITION the list at the 'opt1' line, using 'see'. ## And make the opt1 line the default selection. (?) ##+################################################### set INDEXofOPT1 [ lsearch -exact $optslist $opt1 ] if { "$INDEXofOPT1" != "-1" } { set seeINDEX [expr $INDEXofOPT1 - 1 ] if { "$seeINDEX" < "0" } { set seeINDEX "0" } $w.fRopts.listbox see $seeINDEX ## Comment this to de-activate it? $w.fRopts.listbox selection set $INDEXofOPT1 } ## END OF if { "$INDEXofOPT1" != "-1" } ##+######################################################## ## PROC for the following button1-release BINDING: getline ##+######################################################## proc getline {w outvar passedproc} { ## This 'upvar' associates the local var 'selectline' with ## the outer var that is to contain the listbox selection. ## It is like an EQUIVALENCE statement in FORTRAN. upvar #0 $outvar selectline set sel_index [ $w.fRopts.listbox curselection ] ## FOR TESTING: # puts "sel_index: $sel_index" if { $sel_index != "" } { set selectline [ $w.fRopts.listbox get $sel_index ] } else { set selectline "" } eval set $outvar "$selectline" ## FOR TESTING: # puts "selectline: $selectline" ## puts "EXPn: $EXPn" # puts "outvar: [expr \$$outvar]" eval $passedproc } ## END OF proc getline ##+##################################################### ## SET BINDING on the listbox in this new-widget so that ## puts a selected line of the ## listbox in a specified var and executes a ## specified command/proc. ##+##################################################### bind $w.fRopts.listbox "getline $w $seloptvar \"$mlbProc\"" } ## END OF 'minlistbox' PROC ##+######################################################### ## OK. Now we are ready to define the widgets in the frames. ##+######################################################### ##+##################################################################### ## In the '.fRbuttons' FRAME --- DEFINE-and-PACK ## - an exit-button, ## and ## - 2 (or 3) buttons ( to specify colors) ## and ## - a label widget, to show image parameters ##+##################################################################### button .fRbuttons.buttEXIT \ -text "Exit" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {exit} button .fRbuttons.buttCOLOR1 \ -text "\ Super-ellipse Color" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_ellipse_color1" button .fRbuttons.buttCOLORbkGND \ -text "\ Background Color" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command "set_background_color" 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.buttCOLORbkGND \ .fRbuttons.labelCOLORS \ -side left \ -anchor w \ -fill none \ -expand 0 ##+################################################################## ## In the '.fRimgspecs' FRAME ---- DEFINE-and-PACK ## - a LABEL widget ## - a 'minilistbox' widget for exponent of the super-ellipse ## ## - (perhaps someday) 2 LABLE & SCALE widgets, for the a & b parms ##+################################################################### label .fRimgspecs.labelEXPn \ -text "\ Exponent 'n' of the Super-ellipse :" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_button ## DEFINE the 'minilistbox' widget for light-reflection location ## on the sphere/disk. frame .fRimgspecs.fRexponent -relief flat -bd 0 set EXPopts { 0.5 0.667 0.8 1 2 2.5 3 4 5 6 8 10 12 14 16 18 20 22} set EXPn 3 minilistbox .fRimgspecs.fRexponent $EXPn $EXPopts 6 EXPn "ReDraw 0" set AntiAlias0or1 0 checkbutton .fRimgspecs.chkbuttANTIALIAS \ -text "\ AntiAlias (1 mid-color; 1 pixel 'radius')" \ -font fontTEMP_varwidth \ -variable AntiAlias0or1 \ -selectcolor "#cccccc" \ -relief flat \ -padx 10 label .fRimgspecs.labelPARMS \ -text "" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -bd $BDwidthPx_button ## PACK the widgets of FRAME .fRimgspecs --- ## label ; minilistbox-frame pack .fRimgspecs.labelEXPn \ .fRimgspecs.fRexponent \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRimgspecs.chkbuttANTIALIAS \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRimgspecs.labelPARMS \ -side right \ -anchor e \ -fill none \ -expand 0 ##+###################################################### ## DEFINE-and-PACK the 'canvas' widget ## in the '.fRcan' FRAME ##+###################################################### canvas .fRcan.can \ -width $initCanWidthPx \ -height $initCanHeightPx \ -relief raised \ -borderwidth $BDwidthPx_canvas pack .fRcan.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. ## We move this statement to the bottom of this script. # bind .fRcan.can "ReDraw 0" bind .fRimgspecs.chkbuttANTIALIAS "ReDraw 0" ##+###################################################################### ## PROCS SECTION: ## ## - ReDraw - Called by bindings (like the binding ## above, by the 'minilistbox' widget for exponent 'n', ## and by the Color procs. ## ## Draws the super-ellipse on the canvas for the given ## EXPn var and current color var values. ## ## - set_ellipse_color1 - called by color1 button '-command' ## ## - set_ellipse_color2 - called by color2 button '-command' ## (NOT USED, yet -- could be used for an outline ## or creating a 3D effect) ## ## - set_background_color - called by background color button '-command' ## ##+####################################################################### ######################################################################## ## proc ReDraw - ## ## PURPOSE: ## Draws the super-ellipse on the canvas. ## ## CALLED BY: bindings (in the BINDINGS section and in the 'minilistbox' ## proc) --- 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 ever want to implement 2 scale widgets for the a & b parms. ## When a scale widget is changed, we would do a redraw. ######################################################################## proc ReDraw {x} { global EXPn AntiAlias0or1 COLOR1hex COLOR1r COLOR1g COLOR1b \ COLORbkGNDhex COLORbkGNDr COLORbkGNDg COLORbkGNDb ## Get the current center of the canvas. set x0Px [expr [winfo width .fRcan.can] / 2] set y0Px [expr [winfo height .fRcan.can] / 2] ## FOR TESTING: # puts "x0Px: $x0Px ; y0Px: $y0Px" ## Set the a & b parms of the super-ellipse eqn, ## based on the current size of the canvas. set factor 0.8 set aPx [expr round($factor * double($x0Px))] set bPx [expr round($factor * double($y0Px))] ## FOR TESTING: # puts "aPx : $aPx ; bPx : $bPx" ## Clear the canvas of the previously drawn lines. .fRcan.can delete tagLine ## FOR TESTING: (trying to draw a single pixel with 'create line') ## The first one does not draw a point. # .fRcan.can create line \ # 3 3 \ # 3 3 \ # -tag tagLine -fill white -width 1 ## The second one draws one point, at the 2nd x-pixel - not at the 3rd. # .fRcan.can create line \ # 2 3 \ # 3 3 \ # -tag tagLine -fill white -width 1 ## The third one draws 2 points, at the 2nd and 3rd x-pixels - not the 4th. # .fRcan.can create line \ # 2 3 \ # 4 3 \ # -tag tagLine -fill white -width 1 ## The fourth one draws 2 points, at the 2nd and 3rd x-pixels ## --- not the 4th. Does 'create line' sort the 2 points first? ## Why does it draw 2 but not 4, even though we specified 4 first? # .fRcan.can create line \ # 4 3 \ # 2 3 \ # -tag tagLine -fill white -width 1 ## The fifth one draws 2 points, at the 2nd and 3rd y-pixels - not the 4th. # .fRcan.can create line \ # 3 2 \ # 3 4 \ # -tag tagLine -fill white -width 1 ## If we are going to antialias, set the antialias color(s). if { $AntiAlias0or1 == 1 } { ## Number of pixels to use for the anti-aliasing --- a 'radius' ## of the anti-aliasing, aliasPx. ## (The anti-aliasing for aliasPx = 3 may get complex, until I can ## figure out a general formula for any radius. For now, I just ## try to handle radius=1 --- one pixel of 'feathering' around the ## edge of the super-ellipse, with one color between the fill color ## and the background color. Eventually, I would like to handle ## feathering 2 or 3 pixels out, with 2 or 3 colors between the ## fill color and the background color.) set aliasPx 1 # set aliasPx 2 # set aliasPx 3 ## Intitialize some temp vars for the following loop. set denom [expr $aliasPx + 1] set coef1 $denom set coef2 0 ## Make an array of anti-aliasing colors, where there ## are $aliasPx elements in the array. for {set tempIx 1} {$tempIx <= $aliasPx} {incr tempIx} { set coef1 [expr $coef1 - 1] set coef2 [expr $coef2 + 1] set Raliased($tempIx) [expr (($coef1 * $COLOR1r) + ($coef2 * $COLORbkGNDr)) / $denom] set Galiased($tempIx) [expr (($coef1 * $COLOR1g) + ($coef2 * $COLORbkGNDg)) / $denom] set Baliased($tempIx) [expr (($coef1 * $COLOR1b) + ($coef2 * $COLORbkGNDb)) / $denom] set COLORaliased($tempIx) \ [format "#%02X%02X%02X" $Raliased($tempIx) $Galiased($tempIx) $Baliased($tempIx)] ## FOR TESTING: # puts "COLORaliased(tempIx): $COLORaliased($tempIx) tempIx: $tempIx" } ## END OF for {set tempIx 1} {$tempIx <= $aliasPx} {incr tempIx} ## Actually, rather than simply a linear interpolation to get colors between ## the fill color and the background color, perhaps the luminosity of the ## colors involved should be taken into account --- where green is more ## luminous than red, which is more luminous than blue. } ## END OF if { $AntiAlias0or1 == 1 } ############################################################## ## We now draw the super-ellipse according to the inequality: ## |x/a|^n + |y/b|^n = LHS is less-than-or-equal-to 1 ## Note that we are drawing vertical lines as x goes from ## 0 to a. And we check the values of LHS as y is decremented ## from y=b to y=0, better yet from y=prevTopPx to y=0 --- ## where prevTopPx is the value determined for the top of the ## previously drawn vertical line. ############################################################## set prevTopPx $bPx for {set xPx 0} {$xPx <= $aPx} {incr xPx} { ## FOR TESTING: ## Provide time to see each vertical line being drawn. # update # after 5 for {set yPx $prevTopPx} {$yPx >= 0} {incr yPx -1} { ################################################### ## Evaluate the expression |x/a|^n + |y/b|^n ## where -a < x < a and -b < y < b. ## Actually, because of the symmetry of the image, ## we use 0 < x < a and 0 < y < b. ################################################### set LHS [ expr pow([expr abs([expr $xPx / double($aPx)])] , $EXPn) + \ + pow([expr abs([expr $yPx / double($bPx)])] , $EXPn) ] if { $LHS <= 1.0 } { ######################################################### ## If LHS is less than or equal to 1 for this value of y, ## draw 2 lines, on the right and left of the y-axis: ## - from ( xPx,yPx) to ( xPx,-yPx) ## - from (-xPx,yPx) to (-xPx,-yPx) ## but add x0Px,y0Px to these coords to draw the lines ## relative to the center of the canvas. ## ## Then break out of the y-loop, back to the x-loop. ######################################################## set xrightPx [expr $xPx + $x0Px] set xleftPx [expr -$xPx + $x0Px] set ytopPx [expr -$yPx + $y0Px] set ybotPx [expr $yPx + $y0Px] ## Draw the vertical line on the right side of the symmetric image. .fRcan.can create line \ $xrightPx $ytopPx \ $xrightPx $ybotPx \ -tag tagLine -fill $COLOR1hex ## Draw the vertical line on the left side of the symmetric image. .fRcan.can create line \ $xleftPx $ytopPx \ $xleftPx $ybotPx \ -tag tagLine -fill $COLOR1hex ## We are pretty must done now for this value of x. ## Most of the code below is to handle the optional anti-aliasing. ################################################################# ## If the antialias switch is set on, we do the anti-alias stuff ## at the tops and bottoms of vertical lines, ## by using the averaged rgb values of COLOR1 & COLORbkGND --- ## $COLORaliased($tempIx) for tempIx going from 1 to aliasPx, ## where aliasPx is the 'radius' of the anti-aliasing. ################################################################# if { $AntiAlias0or1 == 1 } { ## Set the jump (downward, if any) from the previosly drawn ## vertical line. set deltay [expr $prevTopPx - $yPx] for {set tempIx 1} {$tempIx <= $aliasPx} {incr tempIx} { ################################################ ## Draw blended pixel(s) at the TOP AND RIGHT of ## the current RIGHT vertical line. ################################################ .fRcan.can create line \ $xrightPx [expr ($ytopPx - $tempIx) - $deltay] \ $xrightPx $ytopPx \ -tag tagLine -fill $COLORaliased($tempIx) ## Add aliased pixel(s) to the right if we have jagged down. if {$prevTopPx > $yPx } { .fRcan.can create line \ [expr $xrightPx + $tempIx] [expr $ytopPx - 1] \ [expr ($xrightPx + 1) + $tempIx] [expr $ytopPx - 1] \ -tag tagLine -fill $COLORaliased($tempIx) } ############################################### ## Draw blended pixel(s) at the TOP AND LEFT of ## the current LEFT vertical line. ############################################### .fRcan.can create line \ $xleftPx [expr ($ytopPx - $tempIx) - $deltay] \ $xleftPx $ytopPx \ -tag tagLine -fill $COLORaliased($tempIx) ## Add aliased pixel(s) to the left if we have jagged down. if {$prevTopPx > $yPx } { .fRcan.can create line \ [expr ($xleftPx + 1) - $tempIx] [expr $ytopPx - 1] \ [expr $xleftPx - $tempIx] [expr $ytopPx - 1] \ -tag tagLine -fill $COLORaliased($tempIx) } ################################################### ## Draw blended pixel(s) at the BOTTOM AND RIGHT of ## the current RIGHT vertical line. ################################################### .fRcan.can create line \ $xrightPx $ybotPx \ $xrightPx [expr $ybotPx + $tempIx + $deltay] \ -tag tagLine -fill $COLORaliased($tempIx) ## Add aliased pixel(s) to the right if we have jagged down. if {$prevTopPx > $yPx } { .fRcan.can create line \ [expr $xrightPx + $tempIx] [expr $ybotPx + 1] \ [expr ($xrightPx + 1) + $tempIx] [expr $ybotPx + 1] \ -tag tagLine -fill $COLORaliased($tempIx) } ################################################## ## Draw blended pixel(s) at the BOTTOM AND LEFT of ## the current LEFT vertical line. ################################################## .fRcan.can create line \ $xleftPx $ybotPx \ $xleftPx [expr $ybotPx + $tempIx + $deltay] \ -tag tagLine -fill $COLORaliased($tempIx) ## Add aliased pixel(s) to the left if we have jagged down. if {$prevTopPx > $yPx } { .fRcan.can create line \ [expr ($xleftPx + 1) - $tempIx] [expr $ybotPx + 1] \ [expr $xleftPx - $tempIx] [expr $ybotPx + 1] \ -tag tagLine -fill $COLORaliased($tempIx) } } ## END OF for {set tempIx 0} {$tempIx < $aliasPx} ############################################################ ## We are still in the if { $AntiAlias0or1 == 1 } section. ## At the far right and left vertical lines, ## draw an 'aliased' vertical line next to those two lines. ############################################################ if {$xPx == $aPx} { for {set tempIx 1} {$tempIx <= $aliasPx} {incr tempIx} { ## Draw the vertical line on the right side of the symmetric image. .fRcan.can create line \ [expr $xrightPx + $tempIx] [expr $ytopPx + 1] \ [expr $xrightPx + $tempIx] [expr $ybotPx - 1] \ -tag tagLine -fill $COLORaliased($tempIx) ## Draw the vertical line on the left side of the symmetric image. .fRcan.can create line \ [expr $xleftPx - $tempIx] [expr $ytopPx + 1] \ [expr $xleftPx - $tempIx] [expr $ybotPx - 1] \ -tag tagLine -fill $COLORaliased($tempIx) ## FOR TESTING: # puts "Drawing vertical lines at $aPx and -$aPx. tempIx: $tempIx" } ## END OF for {set tempIx 0} {$tempIx <= $aliasPx} } ## END OF if {$xPx == $aPx} } ## END OF if { $AntiAlias0or1 == 1 } ######################################################## ## Set var prevTopPx to determine where to start checking ## the value of LHS on the next iteration, rather than ## starting at bPx. I.e. we know the next line should be ## drawn between topPx and -topPx, rather than between ## bPx and -bPx, which is a larger range of pixels. ######################################################## set prevTopPx $yPx ## FOR TESTING: # puts "xPx: $xPx ; topPx: $topPx" ## Cause the y-loop to finish and go to the next x-value. set yPx -1 continue } ## END OF if { $LHS <= 1 } } ## END OF yPx loop } ## END OF xPx loop ## Make sure the text on the COLORS and PARMS label widgets ## is up to date. .fRbuttons.labelCOLORS configure -text "\ Colors: Fill - $COLOR1hex Background - $COLORbkGNDhex" .fRimgspecs.labelPARMS configure -text "\ Current Super-ellipse Parameters: n = $EXPn ; a = $aPx ; b = $bPx for equation |x/a|^n + |y/b|^n = 1 a & b are set from canvas size." } ## END OF proc 'ReDraw' ##+##################################################################### ## proc 'set_ellipse_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: .fRbuttons.buttCOLOR1 button ##+##################################################################### proc set_ellipse_color1 {} { global COLOR1r COLOR1g COLOR1b COLOR1hex COLOR1r COLOR1g COLOR1b # 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_ellipse_color1' ##+##################################################################### ## proc 'set_ellipse_color2' ## (NOT USED yet ; could be used for an outline) ##+##################################################################### ## 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 ... ## ## Arguments: none ## ## CALLED BY: .fRbuttons.buttCOLOR2 button ##+##################################################################### proc set_ellipse_color2 {} { global COLOR2r COLOR2g COLOR2b COLOR2hex COLOR2r COLOR2g COLOR2b # 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 gradient color. ReDraw 0 } ## END OF proc 'set_ellipse_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: .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. .fRcan.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. ## COMMENTED. See the 'bind ' command below. # update # ReDraw 0 ## We need this command because ReDraw does not (re)set ## the background/canvas color. .fRcan.can config -bg $COLORbkGNDhex ## We kill 2 birds with one stone with this bind command, ## in place of the 'update' & 'ReDraw 0' commands above. ## (When the GUI is initialized, there seem to be 2 draws.) bind .fRcan.can "ReDraw 0"