GUI for Drawing 'Super-ellipses', with color options and other controls

uniquename - 2012sep11

I am interested in making nice images for 'toolchest' and 'drawer' backgrounds (and other GUI embellishments), as I have indicated at Experiments in making embellished GUI's and at A color-gradient-button-maker GUI.

In doing some searches on this wiki with keywords including 'canvas', I ran across the Mathematics jewels page of ulis. I assembled the code from that page and ran it --- yielding the 'demo' GUI shown in the following image.

superellipses_withNums_wiki10414_screenshot_620x230.jpg

These are 'super-ellipses' given by the following equation, which is a generalization of the equation for an ellipse.

             |x/a|^n + |y/b|^n = 1

Actually, the numbers were not on the demo GUI. I looked at the code of 'ulis' and added the exponent numbers with an image editor.

Keith Vetter KPV pointed out, in a note added to the Mathematics jewels page, that the 'super-ellipse' shape was used to design a traffic 'circle' in Stockholm. Here are aerial and ground views of 'Sergels Torg Square'.

superellipse_stockholm_sergels_torg_square_aerialView_124x219.gif superellipse_stockholm_sergels_torg_square_300x400.jpg

It turns out that the exponent 2.5 is a popular exponent to use. 'ulis' did not use it in his demo, but someone else shows it in this image of 'super-ellipses' for 7 exponents.

superellipses_7exponents_orangeOnWhite_404x350.gif

Some people have rotated a super-ellipse (with an exponent of 2.5, or thereabout) using the shape to create egg-shaped statues like these.

superellipse_egg_statue_500x375.jpg superellipse_egg_statue_EgeskovCastle_262x349.jpg

When the exponent is around 2.5 (and greater), the ends of the superellipse are just flat enough that the shape will stand on its end --- something you cannot do with an ellipse (n=2) or an egg.

Furthermore, the shape has been used to design stadiums, like this Olympic stadium in Mexico City.

superellipse_olympicStadium_mexicoCity_232x146.png

And some people, like designer Piet Hein, have used the 2.5 superellipse shape to design objects for the home, such as tables, vases, and salt-and-pepper shakers.

superellipse_table_byPietHein_200x160.jpg superellipse_vase_byPietHein_205x261.jpg superellipse_saltANDpepperShakers_412x252.jpg

It struck me that I might find some use for such images in the future --- for rounded button images, unusual 'bullet' images, and icon backgrounds --- for use in Tk GUI's or in web pages.

In the page A two color rounded-rectangle-maker GUI, I presented a GUI for making rectangles with rounded corners (based on the work of others), but those corners are only circular.

With the super-ellipse, one can get rounded buttons ranging in shape from U.S. football shapes (n=1.5) through ellipses (n=2) through 'softly rounded' corners (n=2.5) to rounded corners approaching arbitrarily closely to sharp corners (n > 10).

But there was obviously little control on the 'demo' GUI of 'ulis' --- not even a button.

Color control:

I will probably not need 8 super-ellipses at a time (like in the 'ulis' demo) --- one super-ellipse at a time will usually be sufficient. But I would like to be able to control the colors. There are 2 main colors that I would like to control --- the 'fill' color of the superellipse and the background (canvas) color.

Exponent control:

Of course, there should be a way to specify an exponent for the super-ellipse to be drawn.

Size control :

It would be nice to have some size control. For example, it would be nice if whenever one changed the window size of the GUI, the canvas would change to fill the new window size --- and the superellipse would change to fill the new size of the canvas.

To simplify the GUI somewhat, rather than providing widgets with which to enter values for parameters 'a' and 'b', I decided to determine 'a' and 'b' from the dimensions of the canvas. That is, you can 'squash' the canvas either horizontally or vertically to make b > a or a > b.

Of course, if it turns out that it would be nice to be able to specify an essentially unlimited range of values for 'a' and 'b', one could add a couple of widgets to the GUI.

Drawing technique:

In the 'ulis' demo script, he drew the superellipses by creating 'photo' images and filling in the shape 'a pixel at a time'. He used a color algorithm that gives a gray to black shading toward the outer edge of the superellipse.

I decided to draw the ellipse like the 'gradient buttons' were drawn in A color-gradient-button-maker GUI with 6 'miniscale' widgets and its predecessors --- that is, with 'create line' commands on the canvas, to draw entire lines of pixels 'at a time'.

It seemed to me that the drawing might proceed faster by doing a line at a time rather than a pixel at a time.

To keep things relatively simple, at least in a first version of the GUI, I decided to NOT try to create the shade-to-black effect that 'ulis' generated --- at least, not at this time.

Having those goals in mind, I ended up with a GUI that looks like this.

superellipse_GUI_withColorControl_screenshot_638x451.jpg

Note that I have supplied 2 buttons on the GUI with which to set the superellipse 'fill' color and the 'background' (canvas) color. Those 2 buttons call on a color-selector-GUI script to set those colors. You can make that color-selector script by cutting-and-pasting the code from the page A non-obfuscated color selector GUI on this site.

I used a 'minilistbox' widget to provide the choice of exponents. I have used that 'minilistbox' widget in the script at A two-color rounded-POLYGON-maker GUI (equilateral and not so equilateral) and in GUI for Drawing 'Gradient Spheres' (lighted disks), with lots of control.

As I mentioned on those pages, I got the technique for making a new widget, from existing, built-in Tk widgets, from the 'spinner' widget of Richard Suchenwirth that he presented on the spinbox page. As he described it, the 'spinner' is "a concoction of a 1-line high listbox with two tiny buttons, to approximate the effects of a spinbox.".

My 'minlistbox' widget is more like a 3-line listbox, and I had to enhance Suchenwirth's demo code with font variables and width variables and other parameters.

As I mentioned on the A two-color rounded-POLYGON-maker GUI (equilateral and not so equilateral) page, I may extract the 'minilistbox' code someday and put it in a simpler demo script, like Suchenwirth did with his 'spinner' proc. For now, people who want to use this 'minilistbox' --- or make a similar widget --- can extract the proc and the calling code from this much larger script.

_____________________________________________________________________

Below is the code that produced this GUI.

There are comments above the sample code, in a section titled 'USING THE GENERATED IMAGE', that describe how one could make use of images produced by this GUI.

I follow my usual 'canonical' structure for Tk code, for this Tk script:

  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).

  1b) Pack ALL the frames.

  2) Define & pack all widgets in the frames.

  3) Define keyboard or mouse action BINDINGS, if needed.

  4) Define PROCS, if needed.

  5) Additional GUI initialization (typically with one or more of
     the procs), if needed.

This structure is discussed in more detail on the page A Canonical Structure for Tk Code --- and variations.

This structure makes it easy for me to find code sections --- while generating and testing a Tk script, and when looking for code snippets to include in Tk scripts (code re-use).

To make the 'minilistbox' widget to be used in step (2), I essentially inserted a new step:

1c) Define any procs to be used in making widgets.

_________________________________________________________________

As in all my scripts that use the 'pack' geometry manager (which is all of my scripts, so far), I provide the four main pack parameters --- '-side', '-anchor', '-fill', and '-expand' --- on all the 'pack' commands for the frames and widgets.

I think I have found a good setting of the '-side', '-anchor', '-fill', and '-expand' parameters on the 'pack' commands for the various widgets. In particular ...

The 'canvas' widget expands/contracts appropriately when the window size is changed --- and button and label widgets stay fixed in size and relative-location as the window size is changed.

If anyone wants to change the way the GUI configures itself as the main window size is changed, they can experiment with the '-side', '-anchor', '-fill', and '-expand' parameters on the 'pack' commands for the various widgets --- to get the widget behavior that they want.

Furthermore, there are variables used to set geometry parameters of widgets --- parameters such as border-widths and padding. Feel free to experiment with those parameters as well.

_____________________________________________________________________

That said, here's the code --- with plenty of comments to describe what most of the code-sections are doing.

Since I used a 'create line' technique instead of the 'create image' technique of 'ulis', I did not use his drawing code. Instead, I devised a totally new drawing proc that I called 'ReDraw'.

When I finally worked all the bugs out and got a first superellipse image up, I noticed that there were noticeable 'jaggies' at the rounded corners of the superellipse (for n=3, at least).

That led me to add an 'AntiAlias' checkbutton to the GUI and to try to use tiny line segments at the edges of the superellipse (with 'fill' color an average of the superellipse fill color and the canvas background color). I will eventually discuss some of those results and the issues that arise --- below the code on this page.

Adding the (preliminary) anti-alias code to the 'ReDraw' proc makes the code rather complicated looking. But ...

The copious comments might help Tcl-Tk coding 'newbies' get started in making GUI's like this. Without the comments, the code might look too cryptic, and potential young Tcler's might be tempted to return to trying Facebook on their iPhones.


 Code for the Tk script 'draw_superEllipse_colorFilled_onColorBkgnd.tk' :
#!/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 https://wiki.tcl-lang.org/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 <Configure> (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
   ##         <ButtonRelease-1> puts a selected line of the
   ##        listbox in a specified var and executes a
   ##        specified command/proc.
   ##+#####################################################

   bind  $w.fRopts.listbox <ButtonRelease-1> "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 <Configure> "ReDraw 0"

bind .fRimgspecs.chkbuttANTIALIAS <ButtonRelease-1>  "ReDraw 0"


##+######################################################################
## PROCS SECTION:
##
##  - ReDraw            - Called by bindings (like the <Configure> 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 <Configure>' 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 <Configure> "ReDraw 0"


Here is an image that shows the almost unnoticeable effect of supplying a 'radius=1-pixel' anti-alias technique (with one color between the superellipse color and the background color). I may return to the code someday to try to formulate a radius-2 (with 2 intermediate colors) or radius-3 (with 3 intermediate colors) anti-alias technique --- done with tiny 'create line' segments.

And I may return to the code someday to try to add the shading effect that 'ulis' created --- probably using 'create line' and tiny line segments.

superellipse_GUI_withColorControl_antiAliased_screenshot_639x451.jpg

Someday, I may add some blown-up images of the edges of this superellipse, so that you can see that there really ARE 'anti-alias' pixels around this superellipse.

In fact, it turned out to be quite tricky to get a smooth, symmetric 'layer' of anti-alias pixels around the superellipse --- on the northwest, southwest, and southeast quadrants as well as the northeast quadtrant.

It turns out that 'create line' does some 'funny' things at the ends of a line segment (the starting pixel of a line is drawn but the ending pixel is not).

I may document that 'pathology' (with blown up images) on another wiki page in the future --- along with a disussion of the implications for anti-aliasing and color-shading with 'create line'.

Actually, one can get better quality images using 'create image' rather than 'create line' --- as I did some months after making the code on this page. For results using 'create image', see the images and code of the page GUI for Drawing 'Super-ellipses', with nice shaded edges.

________________

An alternative way to do the anti-aliasing could be to capture the super-ellipse image in a file (for example, by using 'gnome-screenshot' on Linux), cropping the image file with an image editor (like 'mtpaint' on Linux), and then appying a blur function to the image --- either with 'mtpaint' or with the GUI for Editing Photo-images with Functions.

I may add some blur functions to that GUI utility --- so that many more types of blur will be available than are available in a program like 'mtpaint'. Examples:

* 5-pixel blur that averages the color at a given pixel with the colors at 4 adjacent pixels and applies that new color to the central pixel (This is what could be called a 'radius=1' or 'extent=1' blur.)

* 4-pixel blur that uses the colors at the 4 adjacent pixels but not the color at the central pixel (This could be useful to remove single-pixel 'specks' from an image.) (Even better would be a function that only applied the blur at pixels whose color differed quite significantly from the colors at the 4 adjacent pixels.)

* 9-pixel blur that averages the color from the 3x3 pixel square centered at any given pixel (This is another 'radius=1' blur.)

* 25-pixel blur that averages the color from the 5x5 pixel square centered at any given pixel (This is a 'radius=2' blur --- extending 2 pixels out from a central pixel.)

* 49-pixel blur that averages the color from the 7x7 pixel square centered at any given pixel (This is a 'radius=3' blur --- extending 3 pixels out from a central pixel.) (Typically a lesser weighting of the colors at the outer pixels would be used.)

I plan to add those functions to GUI for Editing Photo-images with Functions in coming months.

________________

In any case, it appears that one could make some interesting 'button', 'bullet' and icon-background images with this utility.

And I have the option of enhancing the Tk script on this page, to provide a few more capabilities to this superellipse generator.