FE 'tkGooie' Utilities

Shaded3D group

Tk GUI to
Draw a DONUT
(ring or torus)

with color-shaded,
3D-like edges

(FE = Freedom Environment)

FE color-shaded, 3D-like
Donut-Ring maker
'tkGooie' interface

FE Home Page > FE Downloads Page >

FE 'tkGooies' Description Page >

FE 'tkGooies' 'IMAGEcreatorsShaded3D' Page >

This 'draw_DONUT_colorShaded3D' tkGooie Page

to 'draw_DONUT_colorShaded3D'

I am interested in making nice images for 'toolchest' and 'drawer' backgrounds (and other GUI embellishments), as I have indicated at wiki.tcl.tk (and on this freedomenv.com site) --- as indicated by


Recently I saw an advertising picture for a drama. The picture was of a metal-looking heart with several 3D-looking bullet holes in it.

Each of the bullet holes had an indented edge, and from that indentation came the shaded, 3D-look.

The heart image looked somewhat like the following image

but the bullet holes looked more like this:

I was more interested in the holes than in the heart-shape. I figured that I could use images of a circle-shape with a hole in it.

I had several ideas for uses for such a donut-shaped or ring-shaped image:

1) At a large size, the 3D-donut image could be used as a background for a logo.

2) At a medium size, the image could be used as a background for an icon.

3) And if the image were shrunk to a small size, the image could be used as an unusual 'bullet' (a 'bullet-with-a-bullet-hole-in-it') for line items on a web page or for menu items in a Tk GUI 'toolchest'.

In 2012, I made scripts for making objects with shaded-edges, yielding a 3D-look.

These scripts were posted at wiki.tcl.tk, and, later, on this freedomenv.com site at pages

These scripts used the concept of a 'color-metric' --- a scalar function defined over the x,y pixels of a Tk canvas image.

The metric had the value 1.0 on the boundary of the shape, and the value 0.0 in the center.

The metrics that I used for the 3 shapes above were

  • v = |x/a|^n + |y/b|^n --- for the super-ellipse

  • v = sqrt(x*x + y*y) / R(theta) --- for the super-formula

  • v = max(abs(x/xhalf), abs(y/yhalf)) --- for the rectangle

(See the page-links above for details.)

So I knew I should be able to make a Tk script for creating a ring --- say with internal radius 'r' and external radius 'R' --- with an edge-shaded hole in it.

In fact, there should be edge-shading at both radius 'r' and radius 'R'.

The tricky thing in this case is that I needed the metric to have the value 0.0 in a place other than the center of the circular shape.

In fact, it would be nice if the metric were zero in the middle of the donut/ring --- at the average radius (R + r)/2.


Let 'r' be the radius of the circular hole in the donut and let 'R' be the radious of the outer edge of the circular donut.

Let any point x,y in the rectangular image area be measured from the center of the rectangular image.

Let 'rho' denote the radial distance of the point x,y from that center. So

rho(x,y) = sqrt (x*x + y*y)

We note that a point x,y is on the circle in the middle of the donut (between the hole and the outer edge) if

rho = (R + r) / 2

We note that the quadratic form v = (rho - (R+r)/2) ^ 2 is a parabola, and v is non-negative.

In fact, if you plot 'v' versus 'rho', the graph touches the 'rho' axis at (R+r)/2 --- and 'v' is greater than zero on either side of that point.

To make this a suitable metric, we need to apply a factor such that v is 1.0 at rho = r and at rho = R.

A little algebra shows that our metric is given by

v = a * (rho - (R+r)/2) ^ 2

where the coefficient 'a' is given by 1 / ((R-r)/2) ^ 2.

Now we have a suitable metric, v.

inside of (or outside of) the donut:

At a point x,y, we determine the 'shaded color' at the point by using a color interpolated between the user-selected 'fill' color (color1) of the 'donut shape' and the user-selected background color (color2).

We calculate the 'shaded color' at x,y by calulating a weighted average based on applying the factor (1.0 - v) to color1 --- and applying v to color2. That is,

shaded-color = (1 - v) * color1 + v * color2.

We actually calculate via formulas like

shaded-R = (1 - v) * R1 + v * R2
shaded-G = (1 - v) * G1 + v * G2
shaded-B = (1 - v) * B1 + v * B2

Thus we will get the edge-shading (the 3D effect) for the 'donut shape'.

Actually, it turns out that 1-v and v gives a rather washed-out (too gradual) shading effect.

It is better if we raise v to a power N and use v^N and (1 - v^N).

It turns out that N = 3 or 4 gives pretty nice shading for the donut shape, but rather than hard-code the value of N, we provide a scale widget on the GUI so that the user can set the value of N.


To create the 'donut' Tk script, I used, as a starting point, the code that I posted at wiki.tcl.tk, and later on this freedomenv.com site at GUI for Drawing a 'Super-Formula' Shape, with nice shaded border. That GUI had most of the needed widgets.

Of course, the GUI made by this Tk script needs to contain a rectangular canvas widget on which the color-filled donut shape will be drawn.

I decided to put 2 'scale' widgets on the GUI --- whose slider-bars can be used to change the values of 'r' and 'R'.

And I included a scale for the exponent N to control the extent-or-intensity of the edge-shading --- which is achieved using the two color weighting factors v^N and (1.0 - v^N).

Like in the other 'color-metric' GUI's, I used two buttons --- for 'fill-color' and 'background color' --- to call on an 'external' color selector GUI.

    (I could add a checkbutton widget so that I could turn the 'edge-shading' on or off, but I did not do that.

    That's left as an exercise. But note that you can get the same effect by setting N real high.)

After some coding of a 'ReDraw' proc that uses the 'color-metric' to draw the shaded donut, I ended up with the following GUI --- and the code presented below.

Note that the 2 color 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 that offers a non-obfuscated color selector GUI on this site.


Note that I include a display of 'elapsed time' for each redraw --- by using the Tcl 'clock milliseconds' command.

When I developed the script that I posted at wiki.tcl.tk, and on this site, at GUI for Drawing 'Super-ellipses', with nice shaded edges, I learned my lesson about needing to use braces with 'expr' statements --- for much better execution times.

    (Brent Welch et. al. point this out before page 8 in the 4th edition of 'Practical Programming in Tcl and Tk' --- although they do not use this advice in many 'expr' examples later in the book.)

It made a huge difference, so, in this script (and in all my Tk scripts henceforth), I consistently use braces with ALL 'expr' commands --- in particular, in the compute-intensive 'ReDraw' proc and in any procs that are called by 'ReDraw'.


Below is the code that produced this GUI.

There are comments at the top of the 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 frames and sub-frames.

  2) Define & pack all widgets in the frames.

  3) Define keyboard and mouse/touchpad/touch-sensitive-screen action
     BINDINGS, if needed.

  4) Define PROCS, if needed.

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

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

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

Experimenting with the GUI

As in all my scripts that use the 'pack' geometry manager (which is all of my 100-plus Tk 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 of this GUI. 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.

For example, you may want the scale widgets to stay fixed in length, rather than x-expanding whenever the window is expanded in the x-direction.

    (In fact, this is advisable. Strange things can happen with the scale widget when you allow it to change size with window size.)


Additional GUI experimentation:
You might want to change the fonts used for the various GUI widgets. For example, you could change '-weight' from 'bold' to 'normal' --- or '-slant' from 'roman' to 'italic'. Or change font families.

In fact, you may NEED to change the font families, because the families I used may not be available on your computer --- and the default font that the 'wish' interpreter chooses may not be very pleasing.

I use variables to set geometry parameters of widgets --- parameters such as border-widths and padding. And I have included the '-relief' parameter on the definitions of frames and widgets. Feel free to experiment with those 'appearance' parameters as well.

Some features of the code

There are plenty of comments in the code to describe what most of the code-sections are doing.

See the top of the 'PROCS' section for a list of the procs used in this Tk script.

See comments in the procs for details on the purpose of each proc and for details on the methods by which each proc was implemented.

Here is a quick overview of the procs --- to give an idea of the 'guts' of this utility:

  - ReDraw                            - called by button1-release bindings on 3 scale 
                                        widgets (for r,R,N), by the set-color procs,
                                        and in the GUI initialization section at the
                                        bottom of this script.

  - set_scale_r_lessThan_R_redraw     - called by a button1-release binding for scale-r

  - set_scale_R_greaterThan_r_redraw  - called by a button1-release binding for scale-R

  - set_shape_color1                  - called by the color1 (fill) button

  - set_background_color              - called by the background color button

The most complex code is in the 'ReDraw' proc. I attempted to significantly reduce the number of 'put' commands used to draw an image --- by using 'horizontal-scanlines' (of hex-colors) in the areas of the image where the color is totally the background color --- i.e. above and below the square containing the donut --- and on the left and right of that square.

When I first wrote the ReDraw' proc, I 'put' ALL hexcolors at each individual pixel --- no use of the horizontal scanline technique. The draw times for the image were around 10 seconds or more.

By using the 'horizontal-scanlines' technique with the 'put' command, I was able to reduce the draw times TO about half a second.

No doubt more draw-speed improvements could be made. (See the 'Enhancements' section below.)

Comments in the Code

It is my hope that the copious comments in the code will help Tcl-Tk coding 'newbies' get started in making GUI's like this.

Without the comments --- especially in the 'ReDraw' proc, the code would look like 'too much monkey business to be involved in', to quote Chuck Berry.

Without the comments, potential young Tcler's might be tempted to return to their iPhones and iPads and iPods --- to watch America's/Britain's/Icelands's/Greenland's Funniest Home Videos.

The Tcl-Tk CODE

Here is a link to CODE for the script 'make_donut3D_shadedEdges.tk'.

With your web browser, you can 'right-click' on this link --- and in the menu that pops up, select an item like 'Save Link Target As ...' --- to save this file to your local computer.

Then you can rename the file to remove the '.txt' suffix. Make sure that you have execute permission set on the file --- in order to execute the script.


To show what I meant by using these shaded-3D-donut images for logo-backgrounds, here are a couple of sample images --- one done with the GIMP image editor and one done with the Inkscape SVG (scalable vector graphics) editor, both on Linux --- to add the anti-aliased text.

I ended up with an elliptical shaped image in Inkscape --- mainly because I/you need a 3-week course in using Inkscape --- because it is so non-intuitive and user-UN-friendly.

    (I have seen the kind of help Inkscape users give to newbies on the Inkscape help forum. Brutal. All heat and no light. You do not want to go there. Spend many an hour going through the built-in tutorials first.)

Ending up with the elliptical image was actually fortunate.

I was tempted to go back and try to make this utility more general, by devising a 'color-metric' to use on an ellipical shaped ring.

But this image-result just convinced me that I can be satisfied with getting the elliptical shape via an image editor.

On controlling the edge shading

By putting a scale widget on the GUI for adjusting a 'pow' (power) function exponent to change the 'extensity' (extent and/or intensity) of the shading at the edge of the rectangle, it is easy to find that low powers (like 1 or 2) give really washed-out edges to the donut.

I think you will find that a higher value (like 4) gives a good-looking edge.

That means that v^4 and (1 - v^4) gives better border edges when blending the 'fill' color at (x,y) near an edge and the 'background' color --- better than using the weighted average of the 2 colors using v and (1-v).


It appears that one could make some quite high-quality 'bullet', 'button', icon-background, and logo-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 'color-shaded, 3D-like' image generator.

There are several items that I can foresee wanting to implement in the future to make this Tk script a little bit better:

  • Add a 'Help' button
    In the help text I would add how-to-use information --- such as:
    The user can control the size of the generated image by resizing the entire GUI window. That causes the Tk 'canvas' to resize, and the size of the image to generate is determined from the canvas size.

  • Add 2 widgets by which to set ImgWidth and ImgHeight (pixels) precisely
    This would allow the user to set the generated image size precisely.

  • Improve the draw-speed, by a factor of 2 or more
    As indicated above, we can improve the draw-speed significantly by saving up the hex-colors for pixels in a Tcl 'list' and then apply those colors to the image 'structure' one horizontal scan-line at a time.

    In fact, there is at least one small demo script on wiki.tcl.tk in which a Tcl-er has saved up all the hex-colors for an entire image (in a list of horizontal scan-line lists --- a list of lists) and applied all of them to the image 'structure' in one 'put' call.

    That may be worth a try with this utility. It might provide sub-second draw times, even when the image is the size of an entire computer monitor.

I may implement these enhancements sometime in the future.

(If I use this script as a starting point for another 'image-creator_color-shaded_3D-like' utility, then I will probably implement these enhancements in that new utility --- and then, perhaps, go back to this utility and implement the enhancements.)

You are welcome to take this code and add these enhancements --- and others.


As I have said on several other code-donation pages on this freedomenv.com site and on the Tclers' wiki at wiki.tcl.tk ...

There's a lot to like about a utility that is 'free freedom' --- that is, no-cost and open-source so that you can modify/enhance/fix it without having to wait for someone else to do it for you (which may be never).

A BIG THANK YOU to Ousterhout for starting Tcl-Tk, and a BIG THANK YOU to the Tcl-Tk developers and maintainers who have kept the simply MAH-velous 'wish' interpreter going.

Bottom of this page with
Tcl-Tk Code to
Draw a DONUT (a ring or torus)
with color-shaded, 3D-like edges

--- a utility in the FE 'tkGooies' system,
in the 'IMAGEcreatorsShaded3D' group.

To return to a previously visited web page location, click on the Back button of your web browser a sufficient number of times. OR, use the History-list option of your web browser.
OR ...

< Go to Top of Page, above. >

Page history:

The code was created in 2012 --- and posted 2012 Oct 24 at http://wiki.tcl.tk/37214.

This FE web page was created 2014 May 10 --- as a backup and alternative to the wiki.tcl.tk page.

Page was changed 2015 Oct 07.
(Small changes.)

Page was changed 2015 Dec 30.
(Added some paragraphs and some links.)

Page was changed 2019 Mar 01.
(Added css and javascript to try to handle text-size for smartphones, esp. in portrait orientation.)

Page was changed 2019 Jun 14.
(Specified image widths in percents to size the images according to width of the browser window.)

The code here MAY BECOME more 'up-to-date' than the code posted on the Tcler's Wiki ---
wiki.tcl-lang.org --- formerly wiki.tcl.tk.