Version 12 of A 3D Model Generator GUI

Updated 2013-01-28 13:09:44 by uniquename

uniquename - 2012nov15

I indicated in April 2012, on my 'biography page' at uniquename, that I was inspired by Mark Stucky and Gerard Sookahet to create some 3D model viewing utilities --- complete with fairly robust 3D model data importers (for a variety of 3D data formats --- such as CAD-like data files, Wavefront OBJ files, Cyberware PLY files, STL stereolithography files, etc.).

Well, I am getting started on some 3D viewing projects, but to aid in that endeavor, I have found that it would be nice to have a variety of test files at hand --- boxes, tetrahedrons, octahedrons, icosahedrons, spheres, cones, cylinders, tori --- even buckyballs --- of various dimensions and in various formats.

And what better way to generate such files (with parameters such as widths, heights, depths, radii, etc.) than with Tcl.

And what better way to make an easy-and-quick-and-consolidated-and-self-documenting way of running the code that generates those files than Tk.

Here are a couple of screenshots that sum up in a few inches what the resulting GUI looks like. The 2 images indicate the current extent of the capabilities --- and some capabilities (model-types indicated by a hash-sign at the beginning of the line in the listbox) that may be implemented in the future.

3DmodelFileGeneratorGUI_box6quads_afterWrite_703x313.jpg

and

3DmodelFileGeneratorGUI_box6quads_beforeWrite_701x313.jpg

You can see from the first image that I have created 'writers' for 'box' models (parameters: width,height,depth) for four different output types: a 'CAD-like' output format, OBJ format, PLY format, and STL format.

The 2nd image indicates that I have, so far, implemented one 'writer' for 'icosahedron' models (with 2 dimension parameters) --- for the 'CAD-like' output format.


The code

I provide the code for this 3D model generator GUI below.

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

  0) Set general window & widget parms (win-name, win-position,
     win-color-scheme, fonts, widget-geometry-parms, win-size-control,
     text-array-for-labels-etc).

  1a) Define ALL frames (and sub-frames, if any).
  1b) Pack   ALL frames and sub-frames.

  2) Define & pack all widgets in the frames, frame by frame.

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

One new thing that I have started doing recently is using a text-array for text in labels, buttons, and other widgets in the GUI. This can make it easier for people to internationalize my scripts. I will be using a text-array like this in most of my scripts in the future.


Experimenting with the GUI

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

That helps me when I am initially testing the behavior of a GUI (the various widgets within it) as I resize the main window.

You can experiment with the '-side', '-anchor', '-fill', and '-expand' parameters on the 'pack' commands for the various frames and widgets --- to get the widget behavior that you want.

___

In addition, 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

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

One of the rather unusual features of this script is the fact that the 'parameters' frame is changed according to the 'model-type' selected. You can see this in the two images above:

- When 'box' is selected, the 'parameters' frame prompts for the 3 parameters width, height, and depth.

- When 'icosahedron' is selected, the 'parameters' frame prompts for two parameters --- 'x' and 'z'.

You can look at the procs 'load_parameters_frame_BOX' and 'load_parameters_frame_ICOSAHEDRON' --- to see how the pack forget command is used to change the widgets in that frame.

At the same time, those 'load_parameters_frame' procs can enable or disable the 'output-type' radiobuttons on the GUI to correspond to the currently available 'write' procs for any selected 'model-type'.

___

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 --- and perhaps have some fun with the 'pack forget' command. Without the comments, it might not be clear what the formats of OBJ, PLY, and STL files are --- and which parts of the format specifications are being implemented in these 'writers'.

Without the comments, potential young Tcler's might be tempted to return to their iPhones and iPads and iPods --- to watch Earth's/Jupiter's/Uranus's Funniest Home Videos.

#!/usr/bin/wish -f
##
## SCRIPT: 3DmodelGenerator.tk
##
## PURPOSE:  This Tk GUI script offers a GUI on which the user can
##             - choose model type (box, tetrahedron, octahedron, icosahedron,
##               ..., sphere, cone, cylinder, torus, etc.)
##           and
##             - enter associated parameters such as distances (length, radius,
##               number of longitudinal segments, number of latitudinal
##               segments, etc.)
##
##           and then click a button to generate a 3D model data file
##           according to a chosen format type (OBJ, PLY, OFF, STL, ...).
##
##           Since the OBJ file format is the most common ASCII 3D model
##           file format used at 3D model archive sites, we put a priority
##           on creating models in OBJ format.
##
##           We may use a slight extension of the OBJ format, dubbed 'cOBJ',
##           in which we put 'c' (color) records in the file --- to
##           indicate a color for the following 'v' (vertex) or 'f' (face)
##           records.
##
##           These 'c' records are like the 'Kd' (diffuse light) records of
##           OBJ 'mtl' (material) files in that they contain 3 decimal numbers
##           between 0 and 1 that specify a Red-Green-Blue color. Example:
##           c 0.72 0.45 0.0  , which would be an orange color.
##
#########
## METHOD:   After the user specifies 3 types of items:
##             - a model type (box, sphere, etc.)
##             - some parameters associated with that model type,
##           and 
##             - an output file type,
##
##           the file is written using Tcl 'puts' commands.
##
##           The file may be automatically presented in a GUI text-editor.
##
##           (To change the GUI editor setting, the user can edit this
##            script to change the setting of an 'EDITOR_text' variable
##            at the bottom of this script.)
##
##           The output file may be put in a temporary-file directory,
##           such as /tmp on Linux/Unix. That default directory is
##           set in a variable like 'outDIR' at the bottom of this script.
##
##           The user can use the 'SaveAs...' option of their text editor
##           to save the file in a directory of the user's choice ---
##           after 'touching up' the file however the user desires.
##           For example, comment lines starting with '#' may be added.
##
#################
## THE GUI LAYOUT:
##           The GUI contains the following frames and widgets:
##
##              - 1 'model type' frame for a LISTBOX --- used to select a
##                3D model-file type to generate --- box, sphere/ellipsoid,
##                tetrahedron, pyramid, cone, octahedron, torus, whatever.
##
##              - 1 'buttons' frame for an 'Exit' BUTTON, a 'Help' BUTTON, a
##                'Write' BUTTON, and a LABEL widget to hold 'info',
##                such as info about the 3D-model --- for example,
##                number of points/lines/facets=polygons written.
##
##              - 1 'outtypes' frame for some RADIOBUTTONS --- used to specify
##                output-file type --- 'CAD-like', OBJ, whatever.
##
##              - 1 'parameters' frame for LABEL and ENTRY widgets and whatever
##                other kinds of widgets are appropriate for the
##                user-selected output type.
##
##                   Note: There will actually be a parameter frame (and widgets)
##                   defined for each output type. The current 'parameters' frame
##                   will be replaced by the frame(s) appropriate to the
##                   user-selected output type. A button1-release
##                   binding on each of the model-types in the listbox will
##                   be used to call on a proc-for-each-output-type.
##                   That proc will use a 'pack-forget-and-re-pack' technique
##                   to replace the current frame with the appropriate frame.
##
##+######################################################################
## 'CANONICAL' STRUCTURE OF THIS CODE:
##
##  0) Set general window parms (win-name,win-position,color-scheme,fonts,
##     widget-geometry-parms,win-size-control,text-array-for-labels-etc).
##  1a) Define ALL frames (and sub-frames, if any).
##  1b) Pack   ALL the frames and sub-frames.
##  2) Define all widgets in the frames, frame by frame.
##     When all widgets are defined for a frame, pack them.
##
##  3) Define keyboard and mouse/touchpad/touch-sensitive-screen 'event'
##     BINDINGS, if needed.
##  4) Define PROCS, if needed.
##  5) Additional GUI INITIALIZATION (typically, with one or two procs),
##     if needed.
##
## The code-structure detail for this particular script:
##
##  1a) Define ALL frames:
## 
##      Top-level : '.fRleft'  '.fRright'
##
##      Sub-frames:
##        in  '.fRleft': '.fRlistbox' (for listbox and scrollbar widgets)
##        in '.fRright': '.fRbuttons'  '.fRouttypes'  '.fRparameters' 
##
##  1b) Pack ALL frames.
##
##  2) Define all widgets in the frames (and pack them):
##
##       - In '.fRright.fRbuttons':
##            3 button widgets ('Exit','Help','Write') and1 label widget
##
##       - In '.fRright.fRparameters_XXX':
##             label,entry(,other?) widgets, where XXX is a name
##             indicating the model-type --- BOX6QUADS, OCTA20TRIAS, whatever
##
##                     (The current 'parameters' frame is overlaid by a frame
##                      chosen via one of the model-types radiobuttons.)
##
##       - In '.fRright.fRouttypes' frame:
##             several radiobutton widgets, for output type --- OBJ, PLY, ...
##
##  3) Define BINDINGS: 
##
##        a button1-release binding on the model-type listbox,
##        to call on a 'load_parameters_frame_XXX' to change
##        the '.fRparameters_XXX' frame.
##
##  4) Define PROCS:
##
##     'load_parameters_frame_XXX'  - called via the binding above,
##                         according to which model-type listbox line
##                         is clicked. Example proc name:
##                            'load_parameters_frame_BOX6QUADS'
##
##     'write_model_XXX_YYY' - called when the 'WriteFile' button is clicked.
##                         Writes the output file according to the
##                         XXX model-type setting (box,sphere, ...) and the
##                         YYY output-type radiobutton setting (OBJ, ...).
##                         Example proc name:
##                            'write_model_BOX6QUADS_OBJ'
##
##     'popup_msg_var_scroll' - for a Help button
##
##  5) Additional GUI initialization:
##         Some constants (like pi) are set here, for use by some of the
##         'write_model' procs.
##
##         The 'EDITOR_text' variable is set here. Change this 'set'
##         statement to change the text editor to be used.
##
##         The 'outDIR' variable is set here. Change this 'set'
##         statement to change where the model files will be created.
##
##         The 'curPARMframe' is set here with the name of the current
##         (dummy) parameter frame.
##
##         OPTIONALLY: (to have a non-dummy parameter frame showing
##                      when the GUI first comes up)
##         We set an initial listbox line setting via variable
##         'VARmodeltype' and an initial radiobutton setting for
##         output-type.   Example: BOX-6-QUADS and OBJ.
##
##         The 'load_parameters_frame_BOX6QUADS' for BOX-6-QUADS (or whatever)
##         is also called here, to display the '.fRright.fRparameters_BOX6QUADS'
##         frame, which will typically contain some default parameter values.
##         
##         It the model-type and output-type and parameters are what the
##         user wants, the user can simply click the 'WriteFile' button to
##         immediately generate a model file and see its contents be
##         displayed in the specified text-editor.
##
##         Otherwise, the user can change model-type and/or output-type,
##         set some parameters, and THEN click 'WriteFile'.
##
##+########################################################################
## DEVELOPED WITH:
##   Tcl-Tk 8.5 on Ubuntu 9.10 (2009-october, 'Karmic Koala).
##
##   $ wish
##   % puts "$tcl_version $tk_version"
##                                  showed   8.5 8.5   on Ubuntu 9.10.
##+#######################################################################
## MAINTENANCE HISTORY:
## Created by: Blaise Montandon 2012nov15
## Changed by: Blaise Montandon 2013jan27 Changed model-type selection 
##                                        from radiobuttons to a listbox.
##                                        Added parameter and write procs
##                                        for some model-types.
##+#######################################################################

##+#######################################################################
## Set window parms --- WIN-TITLE and WIN-POSITION.
##+#######################################################################

wm title    . "3D Model Generator, for some model types & output formats"
wm iconname . "3DmodelGen"

wm geometry . +15+30


##+#########################################################
## Set the COLOR SCHEME (palette) for the window ---
## and some colors for its widgets --- such as scale widgets.
##+#########################################################

set R255pal 210
set G255pal 210
set B255pal 210

## sandy brown
set R255pal 244
set G255pal 164
set B255pal 96

set hexCOLORpal [format "#%02X%02X%02X" $R255pal $G255pal $B255pal]
tk_setPalette "$hexCOLORpal"

set BKGD_listbox "#f0f0f0"
set BKGD_entry   "#f0f0f0"
set BKGD_radbutt "#f0f0f0"


##+########################################################
## SET 'FONT-NAMES'.
##
## We use a VARIABLE-WIDTH FONT for labels and buttons
## and the numeric values shown by scale widgets.
##
## We use a FIXED-WIDTH FONT for the help text in a text
## widget (so that any columns in the text stay lined up).
##+########################################################

font create fontTEMP_varwidth \
   -family {comic sans ms} \
   -size -14 \
   -weight bold \
   -slant roman

font create fontTEMP_SMALL_varwidth \
   -family {comic sans ms} \
   -size -10 \
   -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

## 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. padding and borderwidths for Buttons and Labels)
##+###########################################################

## For LABEL widgets:

set PADYpx_label 0
set PADXpx_label 0

# set BDwidthPx_label 0
  set BDwidthPx_label 2


## For BUTTON widgets:

set PADXpx_button 0
set PADYpx_button 0
set BDwidthPx_button 2


## For the LISTBOX widget:

set initListboxWidthChars 25
set initListboxHeightChars 15
set BDwidthPx_listbox 2


## For ENTRY widgets:

set BDwidthPx_entry 2


## For TEXT widgets:

set BDwidthPx_text 2


##+########################################################
## Set a MINSIZE of the window (roughly) -- according to the
## approx max WIDTH of the buttons in the 'fRbuttons' frame
## --- and according to the approx HEIGHT of the 4 frames.
##+########################################################

set minWinWidthPx [font measure fontTEMP_varwidth \
       "Exit  Help  Write"]

## Add some pixels to account for right-left-side window decoration
## (about 8 pixels), about 3 widgets x 3 pixels/widget for borders/padding
## for 3 widgets --- 3 buttons.

set minWinWidthPx [expr {17 + $minWinWidthPx}]


## MIN HEIGHT --- allow at least
##     1 char   high for the 'fRbuttons' frame
##     1 char   high for the 'fRouttypes' frame
##     1 char   high for the 'fRparameters_XXX' frame
##
## OR at least 6 chars high for model-types in the listbox.


set charHeightPx [font metrics fontTEMP_varwidth -linespace]

set minWinHeightPx [expr {6 * $charHeightPx}]

## Add some pixels to account for top-bottom window decoration
## (about 28 pixels) and frame/widget padding vertically
## (about 4 pixels/frame x 3 frames).

set minWinHeightPx [expr {40 + $minWinHeightPx}]

## 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' so that the canvas can be enlarged by enlarging the
## window.


## If you want to make the window un-resizable, 
## you can use the following statement.

# wm resizable . 0 0


#######################################################################
## Set a TEXT-ARRAY to hold text for buttons & labels on the GUI.
##     NOTE: This can aid INTERNATIONALIZATION. This array can
##           be set according to a nation/region parameter.
#######################################################################

## if { "$VARlocale" == "en"}

## For 'fRbuttons' frame:

set aRtext(buttonEXIT)   "Exit"
set aRtext(buttonHELP)   "Help"
set aRtext(buttonWRITE)  "WriteFile"
set aRtext(labelINFO)    "Change default parameter values if desired,
select an output file type, and click on
'WriteFile' to write the file."

## For 'fRouttypes' frame:

set aRtext(labelOUTTYPES)   "Output File Format:"
set aRtext(radbuttOBJ)     "OBJ"
set aRtext(radbuttPLY)     "PLY"
set aRtext(radbuttOFF)     "OFF"
set aRtext(radbuttSTL)     "STL"
# set aRtext(radbuttCADlike) "CAD-like"


## For various 'fRparameters_XXX' frames:
## (Some label-text may be hardcoded in the various
##  label-widget definitions for the parameter frames,
##  in the 'load_parameter_frame_XXX' procs.)

set aRtext(labelWIDTH)    "Width:"
set aRtext(labelHEIGHT)   "Height:"
set aRtext(labelDEPTH)    "Depth:"
set aRtext(labelRADIUS)   "Radius:"



## END OF  if { "$VARlocale" == "en"}



##+################################################################
## DEFINE *ALL* THE FRAMES:
##
##   Top-level : '.fRleft'  '.fRright'
##
##   Sub-frames: '.fRleft.fRlistbox' (for listbox and scrollbars)
##               '.fRright.fRbuttons'
##               '.fRright.fRouttypes'
##               '.fRright.fRparameters_XXX', for many XXX
##
##   Note: The 'fRparameters_XXX' frames are defined here but they will
##         not be packed in this GUI define-and-pack section.
##         The 'fRparameters_XXX' frames will be packed as needed, by
##         'load_parameter_frame_XXX' procs.
##+################################################################

## FOR TESTING of expansion of frames (esp. during window expansion):
#  set RELIEF_frame raised
#  set BDwidth_frame 2

set RELIEF_frame flat
set BDwidth_frame 0

frame .fRleft   -relief $RELIEF_frame  -borderwidth $BDwidth_frame
frame .fRright  -relief $RELIEF_frame  -borderwidth $BDwidth_frame

frame .fRleft.fRlistbox  -relief $RELIEF_frame  -borderwidth $BDwidth_frame

frame .fRright.fRbuttons   -relief $RELIEF_frame  -borderwidth $BDwidth_frame

frame .fRright.fRouttypes  -relief raised         -borderwidth 2

frame .fRright.fRparameters  -relief $RELIEF_frame  -borderwidth $BDwidth_frame

## The specific '.fRparameters_XXX' frames are not defined here.
## They are defined below, as entries are inserted into the
## model-types listbox.


##+##############################
## PACK the top-level FRAMES. 
##+##############################

pack .fRleft \
   -side left \
   -anchor nw \
   -fill both \
   -expand 1

pack .fRright \
   -side left \
   -anchor nw \
   -fill none \
   -expand 0


##+##############################
## PACK the '.fRleft' SUB-FRAME. 
##+##############################

pack .fRleft.fRlistbox \
   -side top \
   -anchor nw \
   -fill both \
   -expand 1


##+##############################
## PACK the '.fRright' SUB-FRAMES. 
##+##############################

pack .fRright.fRbuttons \
   -side top \
   -anchor nw \
   -fill none \
   -expand 0

pack .fRright.fRouttypes \
   -side top \
   -anchor nw \
   -fill x \
   -expand 0

pack .fRright.fRparameters \
   -side top \
   -anchor nw \
   -fill none \
   -expand 0


## OK. ALL frames are defined and packed --- except those 'fRparameters_XXX'
## frames that will be defined below as listbox entries are inserted.
## The 'fRparameters_XXX' frames will be packed as-needed, by procs.
##
## Ready to define widgets.


##+################################################################
##+################################################################
## START DEFINING & PACKING WIDGETS WITHIN THEIR FRAMES,
## frame-by-frame. When all widgets for a frame are defined,
## pack them in the frame.
##+################################################################
##+################################################################


##+######################################################
## In FRAME '.fRleft.fRlistbox' -
## DEFINE-and-PACK a LISTBOX WIDGET,
## with scrollbars --- for a list of functions of 2 vars.
##+######################################################

listbox .fRleft.fRlistbox.listbox \
   -width $initListboxWidthChars \
   -height $initListboxHeightChars \
   -font fontTEMP_fixedwidth \
   -relief raised \
   -borderwidth $BDwidthPx_listbox \
   -state normal \
   -yscrollcommand ".fRleft.fRlistbox.scrbary set" \
   -xscrollcommand ".fRleft.fRlistbox.scrbarx set"

scrollbar .fRleft.fRlistbox.scrbary \
   -orient vertical \
   -command ".fRleft.fRlistbox.listbox yview"

scrollbar .fRleft.fRlistbox.scrbarx \
   -orient horizontal \
   -command ".fRleft.fRlistbox.listbox xview"


##+##########################################################
## INSERT MODEL-TYPE LISTBOX ENTRIES that hold brief, unique
## model-type identifiers --- followed by a separator
## character (#) --- followed by an arbitrary amount of
## descriptive info.
##
## By adding a '#' at the beginning of each line, we signal
## that that model-type is not implemented yet --- but it
## is being considered for addition at some future date.
#############################################################################
## FOR EACH MODEL-TYPE ADDED, ADD 2 PROCS TO THE PROCS SECTION
## --- with frame and widget definitions-and-packing.
##
## Note that for each listbox-line added here, there should be
## at least a couple of procs added to the PROCS section below:
##     'load_parameters_frame_XXX'
##                  Example name: 'load_parameters_frame_BOX6QUADS'
##     'write_model_XXX_YYY'
##                  Example names: 'write_model_BOX6QUADS_OBJ'
##                                 'write_model_BOX6QUADS_PLY'
##
## In the proc 'load_parameters_frame_XXX', there should be
## frame and widget definitions and pack statements for
## the new frame and new widgets.
##
## See the 'load_parameters_frame_XXX' procs for examples of how
## the previous parameters-frame is REMOVED by a 'pack forget' command,
## and THEN the XXX frame is packed.
#############################################################################

## Make sure the listbox is empty.

.fRleft.fRlistbox.listbox delete 0 end

## New MODEL-TYPES are to be added here:

.fRleft.fRlistbox.listbox insert end "BOX-6-QUADS # one quad for each face of the 6 faces this rectangular parallapiped"
.fRleft.fRlistbox.listbox insert end "BOX-12-TRIAS # two triangles for each face of the 6 faces this rectangular parallapiped"
.fRleft.fRlistbox.listbox insert end "ICOSA-20-TRIAS # one triangle for each of the 20 faces of this regular icosahedron"
.fRleft.fRlistbox.listbox insert end "# CUBE-M-N-QUADS # one quad for each face of the 6 faces this rectangular parallapiped"
.fRleft.fRlistbox.listbox insert end "# TETRA-4-TRIAS # one triangle for each of the 4 faces of this regular tetrahedron"
.fRleft.fRlistbox.listbox insert end "# PYR-4-TRIAS-1-QUAD # one triangle for each of the 4 side of this regular pyramid, quad on bottom"

# .fRleft.fRlistbox.listbox insert end "# OCTA-N-TRIAS # one ??? for each of the ??? faces of this regular octahedron"

.fRleft.fRlistbox.listbox insert end "# SPHERE-QUADS-TRIAS # triangles at north and south poles, quadrangles elsewhere"
.fRleft.fRlistbox.listbox insert end "# SPHERE-ALL-TRIAS # triangles everywhere"
.fRleft.fRlistbox.listbox insert end "# ELLIPSOID-QUADS-TRIAS # triangles at north and south poles, quadrangles elsewhere"
.fRleft.fRlistbox.listbox insert end "# CONE-N-TRIAS # N triangles around axis of cone; no bottom"
.fRleft.fRlistbox.listbox insert end "# CYL-N-QUADS # N quads around axis of cylinder; no top or bottom"
.fRleft.fRlistbox.listbox insert end "# TORUS-M-N-QUADS # M segments of small circle; N segments of large circle"
.fRleft.fRlistbox.listbox insert end "# PRISM-2-TRIAS-3-QUADS # M segments of small circle; N segments of large circle"

# .fRleft.fRlistbox.listbox insert end "# BUCKYBALL # M pentagons and ??? ???gons"


#################################################################
## Get the number of model-types loaded into the listbox --- to
## show users how many are in the listbox, out of sight.
#################################################################

set numModelTypes [.fRleft.fRlistbox.listbox index end]


#######################################
## Pack the listbox and its scrollbars.
#######################################

pack .fRleft.fRlistbox.scrbary \
   -side right \
   -anchor e \
   -fill y \
   -expand 0

pack .fRleft.fRlistbox.scrbarx \
   -side bottom \
   -anchor s \
   -fill x \
   -expand 0

## We need to pack the listbox AFTER
## the scrollbars, to get the scrollbars
## positioned properly --- BEFORE
## the listbox FILLS the pack area.

pack .fRleft.fRlistbox.listbox \
   -side top \
   -anchor nw \
   -fill both \
   -expand 1


##+################################################################
## IN THE '.fRright.fRbuttons' frame -
## DEFINE several BUTTONS (Exit,Help,Write) and a LABEL widget.
##+################################################################

button .fRright.fRbuttons.buttEXIT \
   -text "$aRtext(buttonEXIT)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {exit}

button .fRright.fRbuttons.buttHELP \
   -text "$aRtext(buttonHELP)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {popup_msg_var_scroll "$HELPtext"}

button .fRright.fRbuttons.buttWRITE \
   -text "$aRtext(buttonWRITE)" \
   -font fontTEMP_varwidth \
   -padx $PADXpx_button \
   -pady $PADYpx_button \
   -relief raised \
   -bd $BDwidthPx_button \
   -command {write_model_file}


## The text for this label will be loaded
## by a proc, such as 'load_3Dmodel_XXX'.

label .fRright.fRbuttons.labelINFO \
   -text "Select a model-type from the listbox, change its default
parameter values if desired, select an output file type, and
click on 'WriteFile' to write the file." \
   -font fontTEMP_SMALL_varwidth \
   -padx $PADXpx_label \
   -pady $PADYpx_label \
   -justify left \
   -anchor w \
   -relief raised \
   -bd $BDwidthPx_label


## Pack ALL the widgets in frame 'fRbuttons'.

pack .fRright.fRbuttons.buttEXIT \
     .fRright.fRbuttons.buttHELP \
     .fRright.fRbuttons.buttWRITE \
     .fRright.fRbuttons.labelINFO \
   -side left \
   -anchor w \
   -fill none \
   -expand 0


##+######################################################
## In frame '.fRright.fRouttypes' -
## DEFINE a LABEL widget and several RADIOBUTTON widgets.
## Then PACK them.
##+#####################################################

label .fRright.fRouttypes.labelOUTTYPES \
   -text "$aRtext(labelOUTTYPES)" \
   -font fontTEMP_varwidth \
   -justify left \
   -anchor w \
   -relief raised \
   -bd 2

## The radiobuttons' variable, VARouttype, is
## initialized in the additional-GUI-initialization
## section at the bottom of this script. 

radiobutton  .fRright.fRouttypes.radbuttOBJ \
   -text "$aRtext(radbuttOBJ)" \
   -font fontTEMP_varwidth \
   -anchor w \
   -variable VARouttype \
   -value "OBJ" \
   -selectcolor "$BKGD_radbutt" \
   -relief flat \
   -bd $BDwidthPx_button

radiobutton  .fRright.fRouttypes.radbuttPLY \
   -text "$aRtext(radbuttPLY)" \
   -font fontTEMP_varwidth \
   -anchor w \
   -variable VARouttype \
   -value "PLY" \
   -selectcolor "$BKGD_radbutt" \
   -relief flat \
   -bd $BDwidthPx_button

radiobutton  .fRright.fRouttypes.radbuttOFF \
   -text "$aRtext(radbuttOFF)" \
   -font fontTEMP_varwidth \
   -anchor w \
   -variable VARouttype \
   -value "OFF" \
   -selectcolor "$BKGD_radbutt" \
   -relief flat \
   -bd $BDwidthPx_button

radiobutton  .fRright.fRouttypes.radbuttSTL \
   -text "$aRtext(radbuttSTL)" \
   -font fontTEMP_varwidth \
   -anchor w \
   -variable VARouttype \
   -value "STL" \
   -selectcolor "$BKGD_radbutt" \
   -relief flat \
   -bd $BDwidthPx_button

if {0} {
## This button is NOT USED YET.
radiobutton .fRright.fRouttypes.radbuttCADlike \
   -text "$aRtext(radbuttCADlike)" \
   -font fontTEMP_varwidth \
   -anchor w \
   -variable VARouttype \
   -value "CAD-like" \
   -selectcolor "$BKGD_radbutt" \
   -relief flat \
   -bd $BDwidthPx_button
}

## Pack ALL the widgets in frame 'fRouttypes'.

pack .fRright.fRouttypes.labelOUTTYPES \
     .fRright.fRouttypes.radbuttOBJ \
     .fRright.fRouttypes.radbuttPLY \
     .fRright.fRouttypes.radbuttOFF \
     .fRright.fRouttypes.radbuttSTL \
   -side left \
   -anchor w \
   -fill none \
   -expand 0

## Not used yet:
#     .fRright.fRouttypes.radbuttCADlike \


##+########################################################
## In the '.fRright.fRparameters' frame -
## DEFINE a 'dummy' LABEL widget
##
## in this is an initial, 'dummy' frame that is to be
## replaced by other frames for particular model types
## --- box, tetrahedron, sphere, torus, whatever.
##
## Then PACK the widget.
##+########################################################
## NOTE:
## The widgets for the '.fRparameters_XXX' frames ---
## such as LABEL and ENTRY widgets for the
## '.fRparameters_BOX6QUADS' frame ---
## are defined and packed, as needed, in the
## '' procs --- for example,
## ''.
##########################################################

label .fRright.fRparameters.labelDUMMY \
   -text "Select a model-type from the listbox. Its parameter prompts will appear here." \
   -font fontTEMP_varwidth \
   -padx $PADXpx_label \
   -pady $PADYpx_label \
   -justify left \
   -anchor w \
   -relief raised \
   -bd $BDwidthPx_label

## Pack ALL the widgets in the 'fRparameters' frame.

pack .fRright.fRparameters.labelDUMMY \
   -side left \
   -anchor w \
   -fill none \
   -expand 0




##+#####################################################################
## END OF SECTIONS TO SETUP THE GUI.
## ALL FRAMES and WIDGETS are DEFINED-and-PACKED --- except for
## the 'parameter' frames and widgets for specific model-types.
## Those will be defined-and-packed in the 'load_parameters_frame_XXX'
## procs below.
##
## We are now ready to define BINDINGS and PROCS, and do some
## additional initialization of the GUI.
##+#####################################################################


##+#######################################################################
## DEFINE BINDINGS SECTION, including:
##    - button1-release on the listbox ---
##      to call on a corresponding 'load_parameters_frame_XXX' proc.
##+#######################################################################

bind .fRleft.fRlistbox.listbox <ButtonRelease-1>  {listboxSelectionTOparmframeload}



##+######################################################################
## DEFINE PROCS SECTION, including:
##
##  'listboxSelectionTOparmframeload'
##                calls on a 'load_parameters_frame_XXX' proc
##                depending on the listbox line selected.
##
##-----------------------------------------------------------------------
##   'load_parameters_frame_XXX' procs for various model-types XXX,
##    where XXX = BOX6QUADS, TETRA4TRIAS, etc.
##    Example:
##
##     'load_parameters_frame_BOX6QUADS' - 
##          Unpacks (with 'pack forget') the current 'parameters' frame
##          --- held in var 'curPARMframe' --- and packs the
##          'BOX6QUADS' parm-frame (with its widgets) in its place.
##
##          If the 'BOX6QUADS' parm-frame does not exist yet,
##          the proc defines the frame and its widgets
##          and packs the widgets within the frame.
##          THEN it proceeds to pack the 'BOX6QUADS' parm-frame.
##
##          Note: The proc should define the 'BOX6QUADS' frame and
##          its widgets just once in any session, but the
##          'BOX6QUADS' frame could be packed and 'pack forget'-ed
##          multiple times in a session.
##
##          The 'load_parameters_frame_BOX6QUADS' proc is called
##          via the 'listboxSelectionTOparmframeload' proc,
##          if the 'BOX6QUADS' line of the listbox was clicked.
##
##-----------------------------------------------------------------------
##
##     'edit_outfile'  - called by the 'write_model_XXX_YYY' procs, below, to
##                       display the new output file in a GUI text-editor.
##
##-----------------------------------------------------------------------
##
##  'write_model_file'   - called by the 'WriteFile' button.
##                         This proc calls on a 'write_model_XXX_YYY' proc
##                         depending on the current settings of the
##                         model-type and the out-types radiobuttons.
##
##   Example proc that this proc calls: 'write_model_BOX6QUADS_OBJ' 
##-----------------------------------------------------------------------
##   'write_model_XXX_YYY' procs for various model-types XXX and for various
##                       out-types YYY, where YYY = OBJ, PLY, ...
##
##     'write_model_XXX_YYY' - called by the 'write_model_file' proc
##                             when the 'WriteFile' button is clicked.
##                         Writes the output file according to the
##                         XXX model-type setting and the
##                         YYY output-type radiobutton setting.
##
##      Example name: 'write_model_BOX6QUADS_OBJ' 
##-----------------------------------------------------------------------
##
##     'popup_msg_var_scroll' - for a Help button
##-----------------------------------------------------------------------
## PERHAPS SOMEDAY - procs like these may be used:
##
##    'randomColor'  - called by the 'write_model_XXX_YYY' procs, to
##                     generate random hexcolor codes --- for use in writing
##                     3D model files --- i.e. for applying colors to points
##                     and faces/polygons, rather-randomly.
##
##    'rainbowColor' - called by the 'write_model_XXX_YYY' procs, to
##                     generate hexcolor codes from the rainbow spectrum
##                     --- for use in writing 3D model files --- i.e. for
##                     applying colors to points and faces/polygons, 
##                     quasi-randomly.
##
##+########################################################################


##+#########################################################################
## proc  'listboxSelectionTOparmframeload'
############################################################################
## PURPOSE: Calls on a 'load_parameters_frame_XXX' proc
##          depending on the model-types listbox line selected.
##
## CALLED BY: button1-release on the model-types listbox
##
## See the listbox insert statements above (where the listbox widget was
## defined) for the model-type ID strings that were loaded in the listbox.
##+#########################################################################

proc listboxSelectionTOparmframeload {} {

   global VARmodeltype

   set sel_index [ .fRleft.fRlistbox.listbox curselection ]

   if { $sel_index != "" } {
      set MODELdesc  [ .fRleft.fRlistbox.listbox get $sel_index ]
      set TEMPlist [split $MODELdesc #]
      set VARmodeltype [lindex $TEMPlist 0]
      set VARmodeltype [string trim $VARmodeltype]
   } else {return}


   ## FOR TESTING:
   #   puts "proc 'listboxSelectionTOparmframeload' >  VARmodeltype: $VARmodeltype"


   if {"$VARmodeltype" == ""} {return}

   if {"$VARmodeltype" == "BOX-6-QUADS"} {load_parameters_frame_BOX6QUADS}

   if {"$VARmodeltype" == "BOX-12-TRIAS"} {load_parameters_frame_BOX12TRIAS}

   if {"$VARmodeltype" == "ICOSA-20-TRIAS"} {load_parameters_frame_ICOSA20TRIAS}

   if {"$VARmodeltype" == "CUBE-M-N-QUADS"} {load_parameters_frame_CUBE_m_n_QUADS}

   if {"$VARmodeltype" == "TETRA-4-TRIAS"} {load_parameters_frame_TETRA4TRIAS}

   if {"$VARmodeltype" == "PYR-4TRIAS-1QUAD"} {load_parameters_frame_PYR4TRIAS1QUAD}

   if {"$VARmodeltype" == "OCTA?TRIAS"} {load_parameters_frame_OCTA?TRIAS}

   if {"$VARmodeltype" == "SPHERE-QUADS-TRIAS"} {load_parameters_frame_SPHEREquadsTrias}

   if {"$VARmodeltype" == "ELLIPSOID-QUADS-TRIAS"} {load_parameters_frame_ELLIPSOIDquadsTrias}

   if {"$VARmodeltype" == "CONE-N-TRIAS"} {load_parameters_frame_CONEnTRIAS}

   if {"$VARmodeltype" == "CYL-N-QUADS"} {load_parameters_frame_CYLnQUADS}

   if {"$VARmodeltype" == "TORUS-M-N-QUADS"} {load_parameters_frame_TORUS_m_n_QUADS}

   if {"$VARmodeltype" == "PRISM-2TRIAS-3QUADS"} {load_parameters_frame_PRISM2TRIAS3QUADS}

   if {"$VARmodeltype" == "BUCKYBALL"} {load_parameters_frame_BUCKYBALL}


}
## END OF PROC 'listboxSelectionTOparmframeload'



##+#########################################################################
## proc  'load_parameters_frame_BOX6QUADS'
############################################################################
## PURPOSE: Replace the current parameters-frame by the frame with
##          data entry widgets for generation of a 3D model file for a BOX.
##
##          Also enable/disable the outtype radiobuttons according to
##          whether we have an BOX-write proc for the outtype.
##
## CALLED BY: button1-release on the BOX6QUADS model-type listbox line
##+#########################################################################

proc load_parameters_frame_BOX6QUADS {} {

   global curPARMframe RELIEF_frame BDwidth_frame aRtext \
      PADXpx_label PADYpx_label BDwidthPx_label BKGD_entry BDwidthPx_entry \
      VARwidth  VARheight  VARdepth   VARouttype


   ## If the frame is not defined yet, define it.
   ## Also define and pack its widgets.

   if {![winfo exists .fRright.fRparameters_BOX6QUADS]} {

      frame .fRright.fRparameters_BOX6QUADS   -relief $RELIEF_frame  -borderwidth $BDwidth_frame

      ##+########################################################
      ## In the '.fRright.fRparameters_BOX6QUADS' frame -
      ## DEFINE 3 ENTRY widgets, for width,height,depth of the box
      ## --- along with 3 LABEL widgets.
      ## PACK the 6 label and entry widgets.
      ##
      ## Note that this frame should not show up in the GUI until
      ## requested. The frame was defined above in the frame
      ## definitions section, so that these widgets could be
      ## defined and packed, but the frame was NOT packed.
      ##+########################################################

      label .fRright.fRparameters_BOX6QUADS.labelWIDTH \
      -text "BOX-6-QUADS - $aRtext(labelWIDTH)" \
      -font fontTEMP_varwidth \
      -padx $PADXpx_label \
      -pady $PADYpx_label \
      -justify left \
      -anchor w \
      -relief raised \
      -bd $BDwidthPx_label

      set VARwidth "1.0"

      entry .fRright.fRparameters_BOX6QUADS.entWIDTH \
      -textvariable VARwidth \
      -font fontTEMP_fixedwidth \
      -width 7 \
      -bg "$BKGD_entry" \
      -relief sunken \
      -bd $BDwidthPx_entry

      label .fRright.fRparameters_BOX6QUADS.labelHEIGHT \
      -text "$aRtext(labelHEIGHT)" \
      -font fontTEMP_varwidth \
      -padx $PADXpx_label \
      -pady $PADYpx_label \
      -justify left \
      -anchor w \
      -relief raised \
      -bd $BDwidthPx_label

      set VARheight "1.0"

      entry .fRright.fRparameters_BOX6QUADS.entHEIGHT \
      -textvariable VARheight \
      -font fontTEMP_fixedwidth \
      -width 7 \
      -bg "$BKGD_entry" \
      -relief sunken \
      -bd $BDwidthPx_entry

      label .fRright.fRparameters_BOX6QUADS.labelDEPTH \
      -text "$aRtext(labelDEPTH)" \
      -font fontTEMP_varwidth \
      -padx $PADXpx_label \
      -pady $PADYpx_label \
      -justify left \
      -anchor w \
      -relief raised \
      -bd $BDwidthPx_label

      set VARdepth "1.0"

      entry .fRright.fRparameters_BOX6QUADS.entDEPTH \
      -textvariable VARdepth \
      -font fontTEMP_fixedwidth \
      -width 7 \
      -bg "$BKGD_entry" \
      -relief sunken \
      -bd $BDwidthPx_entry

      ## Pack the 3 entry widgets.

      pack .fRright.fRparameters_BOX6QUADS.labelWIDTH \
      .fRright.fRparameters_BOX6QUADS.entWIDTH \
      .fRright.fRparameters_BOX6QUADS.labelHEIGHT \
      .fRright.fRparameters_BOX6QUADS.entHEIGHT \
      .fRright.fRparameters_BOX6QUADS.labelDEPTH \
      .fRright.fRparameters_BOX6QUADS.entDEPTH \
      -side left \
      -anchor w \
      -fill none \
      -expand 0

   }
   ## END OF if {![winfo exists .fRright.fRparameters_BOX6QUADS]}

   ## FOR TESTING:
   #  puts "proc 'load_parameters_frame_BOX6QUADS' > Replacing curPARMframe: $curPARMframe"

   ## Remove the current 'parameters' frame (and the frames below it, if any)
   ## and re-pack using the 'fRparameters_BOX6QUADS' frame.
 
   pack forget $curPARMframe

   pack .fRright.fRparameters_BOX6QUADS \
      -side top \
      -anchor nw \
      -fill none \
      -expand 0

   set curPARMframe ".fRright.fRparameters_BOX6QUADS"

   ## Set the state of the outtypes radiobuttons to 'normal'
   ## if a write proc exists for this model-type.
   ## Set the state to 'disabled' if no write proc.

   .fRright.fRouttypes.radbuttOBJ  configure -state normal
   .fRright.fRouttypes.radbuttPLY  configure -state normal
   .fRright.fRouttypes.radbuttOFF  configure -state disabled
   .fRright.fRouttypes.radbuttSTL  configure -state disabled
   # .fRright.fRouttypes.radbuttCADlike configure -state normal

   set VARouttype "OBJ"

   .fRright.fRbuttons.labelINFO configure -text "$aRtext(labelINFO)"

   ## FOR TESTING:
   #  puts "proc 'load_parameters_frame_BOX6QUADS' > 'pack forget' and re-pack was done."
   #  puts "proc 'load_parameters_frame_BOX6QUADS' > curPARMframe IS: $curPARMframe"

}
## END OF proc 'load_parameters_frame_BOX6QUADS'


##+#########################################################################
## proc  'load_parameters_frame_BOX12TRIAS'
############################################################################
## PURPOSE: Replace the current parameters-frame by the frame with
##          data entry widgets for generation of a 3D model file for a BOX.
##
##          Also enable/disable the outtype radiobuttons according to
##          whether we have an BOX12TRIAS-write proc for the outtype.
##
## CALLED BY: button1-release on the BOX12TRIAS model-type listbox line
##+#########################################################################

proc load_parameters_frame_BOX12TRIAS {} {

   global curPARMframe RELIEF_frame BDwidth_frame aRtext \
      PADXpx_label PADYpx_label BDwidthPx_label BKGD_entry BDwidthPx_entry \
      VARwidth  VARheight  VARdepth  VARouttype

   ## If the frame is not defined yet, define it.
   ## Also define and pack its widgets.

   if {![winfo exists .fRright.fRparameters_BOX12TRIAS]} {

      frame .fRright.fRparameters_BOX12TRIAS   -relief $RELIEF_frame  -borderwidth $BDwidth_frame

      ##+########################################################
      ## In the '.fRright.fRparameters_BOX12TRIAS' frame -
      ## DEFINE 3 ENTRY widgets, for width,height,depth of the box
      ## --- along with 3 LABEL widgets.
      ## PACK the 6 label and entry widgets.
      ##
      ## Note that this frame should not show up in the GUI until
      ## requested. The frame was defined above in the frame
      ## definitions section, so that these widgets could be
      ## defined and packed, but the frame was NOT packed.
      ##+########################################################

      label .fRright.fRparameters_BOX12TRIAS.labelWIDTH \
      -text "BOX-12-TRIAS - $aRtext(labelWIDTH)" \
      -font fontTEMP_varwidth \
      -padx $PADXpx_label \
      -pady $PADYpx_label \
      -justify left \
      -anchor w \
      -relief raised \
      -bd $BDwidthPx_label

      set VARwidth "1.0"

      entry .fRright.fRparameters_BOX12TRIAS.entWIDTH \
      -textvariable VARwidth \
      -font fontTEMP_fixedwidth \
      -width 7 \
      -bg "$BKGD_entry" \
      -relief sunken \
      -bd $BDwidthPx_entry

      label .fRright.fRparameters_BOX12TRIAS.labelHEIGHT \
      -text "$aRtext(labelHEIGHT)" \
      -font fontTEMP_varwidth \
      -padx $PADXpx_label \
      -pady $PADYpx_label \
      -justify left \
      -anchor w \
      -relief raised \
      -bd $BDwidthPx_label

      set VARheight "1.0"

      entry .fRright.fRparameters_BOX12TRIAS.entHEIGHT \
      -textvariable VARheight \
      -font fontTEMP_fixedwidth \
      -width 7 \
      -bg "$BKGD_entry" \
      -relief sunken \
      -bd $BDwidthPx_entry

      label .fRright.fRparameters_BOX12TRIAS.labelDEPTH \
      -text "$aRtext(labelDEPTH)" \
      -font fontTEMP_varwidth \
      -padx $PADXpx_label \
      -pady $PADYpx_label \
      -justify left \
      -anchor w \
      -relief raised \
      -bd $BDwidthPx_label

      set VARdepth "1.0"

      entry .fRright.fRparameters_BOX12TRIAS.entDEPTH \
      -textvariable VARdepth \
      -font fontTEMP_fixedwidth \
      -width 7 \
      -bg "$BKGD_entry" \
      -relief sunken \
      -bd $BDwidthPx_entry

      ## Pack the 3 entry widgets.

      pack .fRright.fRparameters_BOX12TRIAS.labelWIDTH \
      .fRright.fRparameters_BOX12TRIAS.entWIDTH \
      .fRright.fRparameters_BOX12TRIAS.labelHEIGHT \
      .fRright.fRparameters_BOX12TRIAS.entHEIGHT \
      .fRright.fRparameters_BOX12TRIAS.labelDEPTH \
      .fRright.fRparameters_BOX12TRIAS.entDEPTH \
      -side left \
      -anchor w \
      -fill none \
      -expand 0

   }
   ## END OF if {![winfo exists .fRright.fRparameters_BOX12TRIAS]}

   ## FOR TESTING:
   #  puts "proc 'load_parameters_frame_BOX12TRIAS' > Replacing curPARMframe: $curPARMframe"

   ## Remove the current 'parameters' frame (and the frames below it, if any)
   ## and re-pack using the 'fRparameters_BOX12TRIAS' frame.
 
   pack forget $curPARMframe

   pack .fRright.fRparameters_BOX12TRIAS \
      -side top \
      -anchor nw \
      -fill none \
      -expand 0

   set curPARMframe ".fRright.fRparameters_BOX12TRIAS"

   ## Set the state of the outtypes radiobuttons to 'normal'
   ## if a write proc exists for this model-type.
   ## Set the state to 'disabled' if no write proc.

   .fRright.fRouttypes.radbuttOBJ  configure -state disabled
   .fRright.fRouttypes.radbuttPLY  configure -state normal
   .fRright.fRouttypes.radbuttOFF  configure -state disabled
   .fRright.fRouttypes.radbuttSTL  configure -state normal
   # .fRright.fRouttypes.radbuttCADlike configure -state normal

   set VARouttype "PLY"

   .fRright.fRbuttons.labelINFO configure -text "$aRtext(labelINFO)"


   ## FOR TESTING:
   #  puts "proc 'load_parameters_frame_BOX12TRIAS' > 'pack forget' and re-pack was done."
   #  puts "proc 'load_parameters_frame_BOX12TRIAS' > curPARMframe IS: $curPARMframe"

}
## END OF proc 'load_parameters_frame_BOX12TRIAS'



##+#########################################################################
## proc  'load_parameters_frame_ICOSA20TRIAS'
############################################################################
## PURPOSE: Replace the current parameters-frame by the frame with
##          data entry widgets for generation of a 3D model file for a
##          ICOSAHEDRON.
##
##          Also enable/disable the outtype radiobuttons according to
##          whether we have an ICOSAHEDRON-write proc for the outtype.
##
## CALLED BY: button1-release on the ICOSAHEDRON model-type listbox line
##
## Reference: OpenGL Programming Guide, Chapter 2, Drawing
##            Geometric Objects
##+#########################################################################

proc load_parameters_frame_ICOSA20TRIAS {} {

   global curPARMframe RELIEF_frame BDwidth_frame aRtext \
      PADXpx_label PADYpx_label BDwidthPx_label BKGD_entry BDwidthPx_entry \
      VARx VARz  VARouttype

   ## If the frame is not defined yet, define it.
   ## Also define and pack its widgets.

   if {![winfo exists .fRright.fRparameters_ICOSA20TRIAS]} {

      frame .fRright.fRparameters_ICOSA20TRIAS  -relief $RELIEF_frame  -borderwidth $BDwidth_frame

      ##+########################################################
      ## In the '.fRright.fRparameters_ICOSA20TRIAS' frame -
      ## DEFINE 2 ENTRY widgets, for 'X' and 'Z'
      ## --- along with 2 LABEL widgets.
      ## PACK the 4 label and entry widgets.
      ##+########################################################

      label .fRright.fRparameters_ICOSA20TRIAS.labelX \
      -text "ICOSA-20-TRIAS - X:" \
      -font fontTEMP_varwidth \
      -padx $PADXpx_label \
      -pady $PADYpx_label \
      -justify left \
      -anchor w \
      -relief raised \
      -bd $BDwidthPx_label

      set VARx "0.525731"

      entry .fRright.fRparameters_ICOSA20TRIAS.entX \
      -textvariable VARx \
      -font fontTEMP_fixedwidth \
      -width 10 \
      -bg $BKGD_entry \
      -relief sunken \
      -bd $BDwidthPx_entry

      label .fRright.fRparameters_ICOSA20TRIAS.labelZ \
      -text "Z:" \
      -font fontTEMP_varwidth \
      -padx $PADXpx_label \
      -pady $PADYpx_label \
      -justify left \
      -anchor w \
      -relief raised \
      -bd $BDwidthPx_label

      set VARz "0.850651"

      entry .fRright.fRparameters_ICOSA20TRIAS.entZ \
      -textvariable VARz \
      -font fontTEMP_fixedwidth \
      -width 10 \
      -bg $BKGD_entry \
      -relief sunken \
      -bd $BDwidthPx_entry


      ## Pack the 2 entry widgets, with labels.

      pack .fRright.fRparameters_ICOSA20TRIAS.labelX \
      .fRright.fRparameters_ICOSA20TRIAS.entX \
      .fRright.fRparameters_ICOSA20TRIAS.labelZ \
      .fRright.fRparameters_ICOSA20TRIAS.entZ \
      -side left \
      -anchor w \
      -fill none \
      -expand 0

   }
   ## END OF   if {![winfo exists .fRright.fRparameters_ICOSA20TRIAS]}


   ## Remove the current 'parameters' frame (and the frames below it, if any)
   ## and re-pack using the 'fRparameters_ICOSA20TRIAS' frame.
 
   pack forget $curPARMframe

   pack .fRright.fRparameters_ICOSA20TRIAS \
      -side top \
      -anchor nw \
      -fill none \
      -expand 0

   set curPARMframe ".fRright.fRparameters_ICOSA20TRIAS"


   ## Set the state of the outtypes radiobuttons to 'normal'
   ## if a write proc exists for this model-type.
   ## Set the state to 'disabled' if no write proc.

   .fRright.fRouttypes.radbuttOBJ  configure -state normal
   .fRright.fRouttypes.radbuttPLY  configure -state disabled
   .fRright.fRouttypes.radbuttOFF  configure -state disabled
   .fRright.fRouttypes.radbuttSTL  configure -state disabled
   # .fRright.fRouttypes.radbuttCADlike configure -state normal

   set VARouttype "OBJ"

   .fRright.fRbuttons.labelINFO configure -text "$aRtext(labelINFO)"


}
## END OF proc 'load_parameters_frame_ICOSA20TRIAS'



##+#########################################################################
## proc  'load_parameters_frame_TETRA4TRIAS'
############################################################################
## PURPOSE: Replace the current parameters-frame by the frame with
##          data entry widgets for generation of a 3D model file for a
##          TETRAHEDRON.
##
##          Also enable/disable the outtype radiobuttons according to
##          whether we have an TETRAHEDRON-write proc for the outtype.
##
## CALLED BY: button1-release on the TETRA4TRIAS model-type listbox line
##+#########################################################################

proc load_parameters_frame_TETRA4TRIAS {} {

   global curPARMframe RELIEF_frame BDwidth_frame \
      PADXpx_label PADYpx_label BDwidthPx_label BKGD_entry BDwidthPx_entry

   ## Not ready yet.
   return

   ## If the frame is not defined yet, define it.

   if {![winfo exists .fRright.fRparameters_TETRA4TRIAS]} {
      frame .fRright.fRparameters_TETRA4TRIAS  -relief $RELIEF_frame  -borderwidth $BDwidth_frame
   }

   ## Remove the current 'parameters' frame (and the frames below it, if any)
   ## and re-pack using the 'fRparameters_TETRA4TRIAS' frame.
 
   pack forget $curPARMframe

   pack .fRright.fRparameters_TETRA4TRIAS \
      -side top \
      -anchor nw \
      -fill none \
      -expand 0

   set curPARMframe ".fRright.fRparameters_TETRA4TRIAS"

   ## Set the state of the outtypes radiobuttons to 'normal'
   ## if a write proc exists for this model-type.
   ## Set the state to 'disabled' if no write proc.

   .fRright.fRouttypes.radbuttOBJ  configure -state disabled
   .fRright.fRouttypes.radbuttPLY  configure -state disabled
   .fRright.fRouttypes.radbuttOFF  configure -state disabled
   .fRright.fRouttypes.radbuttSTL  configure -state disabled
   # .fRright.fRouttypes.radbuttCADlike configure -state disabled

}
## END OF proc 'load_parameters_frame_TETRAHEDRON'


##+#########################################################################
## proc  'load_parameters_frame_CUBE_m_n_QUADS'
############################################################################
## PURPOSE: Replace the current parameters-frame by the frame with
##          data entry widgets for generation of a 3D model file for a
##          CUBE_m_n_QUADS.
##
##          Also enable/disable the outtype radiobuttons according to
##          whether we have an CUBE_m_n_QUADS-write proc for the outtype.
##
## CALLED BY: button1-release on the CUBE-M-N-QUADS model-type listbox line
##+#########################################################################

proc load_parameters_frame_CUBE_m_n_QUADS {} {

   global curPARMframe RELIEF_frame BDwidth_frame \
      PADXpx_label PADYpx_label BDwidthPx_label BKGD_entry BDwidthPx_entry

   ## This proc is not ready.
   return

frame .fRright.fRparameters_CUBE_m_n_QUADS   -relief $RELIEF_frame  -borderwidth $BDwidth_frame

}
## END OF proc 'load_parameters_frame_CUBE_m_n_QUADS'



##+#########################################################################
## proc  'load_parameters_frame_PYR4TRIAS1QUAD'
############################################################################
## PURPOSE: Replace the current parameters-frame by the frame with
##          data entry widgets for generation of a 3D model file for a
##          PYR4TRIAS1QUAD.
##
##          Also enable/disable the outtype radiobuttons according to
##          whether we have an PYR4TRIAS1QUAD-write proc for the outtype.
##
## CALLED BY: button1-release on the PYR-4TRIAS-1QUAD model-type listbox line
##+#########################################################################

proc load_parameters_frame_PYR4TRIAS1QUAD {} {

   global curPARMframe RELIEF_frame BDwidth_frame \
      PADXpx_label PADYpx_label BDwidthPx_label BKGD_entry BDwidthPx_entry

   ## This proc is not ready.
   return

frame .fRright.fRparameters_PYR4TRIAS1QUAD  -relief $RELIEF_frame  -borderwidth $BDwidth_frame


}
## END OF proc 'load_parameters_frame_PYR4TRIAS1QUAD'


##+#########################################################################
## proc  'load_parameters_frame_OCTA?TRIAS'
############################################################################
## PURPOSE: Replace the current parameters-frame by the frame with
##          data entry widgets for generation of a 3D model file for a
##          OCTA?TRIAS.
##
##          Also enable/disable the outtype radiobuttons according to
##          whether we have an OCTA?TRIAS-write proc for the outtype.
##
## CALLED BY: button1-release on the OCTA-?TRIAS model-type listbox line
##+#########################################################################

proc load_parameters_frame_OCTA?TRIAS {} {

   global curPARMframe RELIEF_frame BDwidth_frame \
      PADXpx_label PADYpx_label BDwidthPx_label BKGD_entry BDwidthPx_entry

   ## This proc is not ready.
   return

# frame .fRright.fRparameters_OCTA?TRIAS   -relief $RELIEF_frame  -borderwidth $BDwidth_frame


}
## END OF proc 'load_parameters_frame_OCTA?TRIAS'


##+#########################################################################
## proc  'load_parameters_frame_SPHEREquadsTrias'
############################################################################
## PURPOSE: Replace the current parameters-frame by the frame with
##          data entry widgets for generation of a 3D model file for a
##          SPHEREquadsTrias.
##
##          Also enable/disable the outtype radiobuttons according to
##          whether we have an SPHEREquadsTrias-write proc for the outtype.
##
## CALLED BY: button1-release on the SPHERE-QUADS-TRIAS model-type listbox line
##+#########################################################################

proc load_parameters_frame_SPHEREquadsTrias {} {

   global curPARMframe RELIEF_frame BDwidth_frame \
      PADXpx_label PADYpx_label BDwidthPx_label BKGD_entry BDwidthPx_entry

   ## This proc is not ready.
   return

frame .fRright.fRparameters_SPHEREquadsTrias   -relief $RELIEF_frame  -borderwidth $BDwidth_frame


}
## END OF proc 'load_parameters_frame_SPHEREquadsTrias'


##+#########################################################################
## proc  'load_parameters_frame_ELLIPSOIDquadsTrias'
############################################################################
## PURPOSE: Replace the current parameters-frame by the frame with
##          data entry widgets for generation of a 3D model file for a
##          ELLIPSOIDquadsTrias.
##
##          Also enable/disable the outtype radiobuttons according to
##          whether we have an ELLIPSOIDquadsTrias-write proc for the outtype.
##
## CALLED BY: button1-release on the ELLIPSOID-QUADS-TRIAS model-type listbox line
##+#########################################################################

proc load_parameters_frame_ELLIPSOIDquadsTrias {} {

   global curPARMframe RELIEF_frame BDwidth_frame \
      PADXpx_label PADYpx_label BDwidthPx_label BKGD_entry BDwidthPx_entry

   ## This proc is not ready.
   return

frame .fRright.fRparameters_ELLIPSOIDquadsTrias   -relief $RELIEF_frame  -borderwidth $BDwidth_frame

}
## END OF proc 'load_parameters_frame_ELLIPSOIDquadsTrias'


##+#########################################################################
## proc  'load_parameters_frame_CONEnTRIAS'
############################################################################
## PURPOSE: Replace the current parameters-frame by the frame with
##          data entry widgets for generation of a 3D model file for a
##          CONEnTRIAS.
##
##          Also enable/disable the outtype radiobuttons according to
##          whether we have an CONEnTRIAS-write proc for the outtype.
##
## CALLED BY: button1-release on the CONE-N-TRIAS model-type listbox line
##+#########################################################################

proc load_parameters_frame_CONEnTRIAS {} {

   global curPARMframe RELIEF_frame BDwidth_frame \
      PADXpx_label PADYpx_label BDwidthPx_label BKGD_entry BDwidthPx_entry

   ## This proc is not ready.
   return

frame .fRright.fRparameters_CONEnTRIAS  -relief $RELIEF_frame  -borderwidth $BDwidth_frame


   ## Remove the current 'fRparameters' frame (and the frames below it)
   ## and re-pack using the 'fRparameters_CONE' frame.
 
   pack forget $curPARMframe

   pack .fRright.fRparameters_CONEnTRIAS \
      -side top \
      -anchor nw \
      -fill none \
      -expand 0

   set curPARMframe ".fRright.fRparameters_CONEnTRIAS"


   .fRright.fRouttypes.radbuttOBJ  configure -state disabled
   .fRright.fRouttypes.radbuttPLY  configure -state disabled
   .fRright.fRouttypes.radbuttSTL  configure -state disabled
   # .fRright.fRouttypes.radbuttCADlike configure -state disabled

}
## END OF proc 'load_parameters_frame_CONEnTRIAS'


##+#########################################################################
## proc  'load_parameters_frame_CYLnQUADS'
############################################################################
## PURPOSE: Replace the current parameters-frame by the frame with
##          data entry widgets for generation of a 3D model file for a
##          CYLnQUADS.
##
##          Also enable/disable the outtype radiobuttons according to
##          whether we have an CYLnQUADS-write proc for the outtype.
##
## CALLED BY: button1-release on the CYL-N-QUADS model-type listbox line
##+#########################################################################

proc load_parameters_frame_CYLnQUADS {} {

   global curPARMframe RELIEF_frame BDwidth_frame \
      PADXpx_label PADYpx_label BDwidthPx_label BKGD_entry BDwidthPx_entry

   ## This proc is not ready.
   return

frame .fRright.fRparameters_CYLnQUADS  -relief $RELIEF_frame  -borderwidth $BDwidth_frame

}
## END OF proc 'load_parameters_frame_CYLnQUADS'


##+#########################################################################
## proc  'load_parameters_frame_TORUS_m_n_QUADS'
############################################################################
## PURPOSE: Replace the current parameters-frame by the frame with
##          data entry widgets for generation of a 3D model file for a
##          TORUS_m_n_QUADS.
##
##          Also enable/disable the outtype radiobuttons according to
##          whether we have an TORUS_m_n_QUADS-write proc for the outtype.
##
## CALLED BY: button1-release on the TORUS-M-N-QUADS model-type listbox line
##+#########################################################################

proc load_parameters_frame_TORUS_m_n_QUADS {} {

   global curPARMframe RELIEF_frame BDwidth_frame \
      PADXpx_label PADYpx_label BDwidthPx_label BKGD_entry BDwidthPx_entry

   ## This proc is not ready.
   return

frame .fRright.fRparameters_TORUS_m_n_QUADS     -relief $RELIEF_frame  -borderwidth $BDwidth_frame

}
## END OF proc 'load_parameters_frame_TORUS_m_n_QUADS'


##+#########################################################################
## proc  'load_parameters_frame_PRISM2TRIAS3QUADS'
############################################################################
## PURPOSE: Replace the current parameters-frame by the frame with
##          data entry widgets for generation of a 3D model file for a
##          PRISM2TRIAS3QUADS.
##
##          Also enable/disable the outtype radiobuttons according to
##          whether we have an PRISM2TRIAS3QUADS-write proc for the outtype.
##
## CALLED BY: button1-release on the PRISM-2TRIAS-3QUADS model-type listbox line
##+#########################################################################

proc load_parameters_frame_PRISM2TRIAS3QUADS {} {

   global curPARMframe RELIEF_frame BDwidth_frame \
      PADXpx_label PADYpx_label BDwidthPx_label BKGD_entry BDwidthPx_entry

   ## This proc is not ready.
   return

frame .fRright.fRparameters_PRISM2TRIAS3QUADS   -relief $RELIEF_frame  -borderwidth $BDwidth_frame

}
## END OF proc 'load_parameters_frame_PRISM2TRIAS3QUADS'


##+#########################################################################
## proc  'load_parameters_frame_BUCKYBALL'
############################################################################
## PURPOSE: Replace the current parameters-frame by the frame with
##          data entry widgets for generation of a 3D model file for a
##          BUCKYBALL.
##
##          Also enable/disable the outtype radiobuttons according to
##          whether we have an BUCKYBALL-write proc for the outtype.
##
## CALLED BY: button1-release on the BUCKYBALL model-type listbox line
##+#########################################################################

proc load_parameters_frame_BUCKYBALL {} {

   global curPARMframe RELIEF_frame BDwidth_frame \
      PADXpx_label PADYpx_label BDwidthPx_label BKGD_entry BDwidthPx_entry

   ## This proc is not ready.
   return

# frame .fRright.fRparameters_BUCKYBALL   -relief $RELIEF_frame  -borderwidth $BDwidth_frame

}
## END OF proc 'load_parameters_frame_BUCKYBALL'


#########################################################
## MORE 'load_parameters_frame_XXX' procs are to go here.
#########################################################


##+#########################################################################
## proc  'edit_outfile'
############################################################################
## PURPOSE: Calls on a GUI text-editor of the user's choice,
##          with which to disply the output 3D model file.
##
##          NOTE: The user can edit this script to change the setting
##                of the variable EDITOR_text to the editor of their choice.
##                See the example settings below.
##
## CALLED BY: the 'write_model_XXX_YYY' procs.
##+#########################################################################

proc edit_outfile {FULFILname} {
   
   global env EDITOR_text

   # exec /usr/bin/sh -c "$EDITOR_text "$FULFILname" > /dev/null  2>&1"

   exec $EDITOR_text "$FULFILname"

}
## END OF proc 'edit_outfile'


##+#########################################################################
## proc  'write_model_file'
############################################################################
## PURPOSE: Calls on a 'write_model_XXX_YYY' proc according to the
##          current settings of VARmodeltype and the out-type radiobuttons.
##
## CALLED BY: called by the 'WriteFile' button
##+#########################################################################

proc write_model_file {} {

   global VARmodeltype VARouttype

   ## We could put a message into the 'labelINFO' widget, rather than
   ## silently returning when VARmodeltype is not set yet.

   if {![info exists VARmodeltype]} {return}

   if {"$VARmodeltype" == "BOX-6-QUADS" && "$VARouttype" == "OBJ"} {
      write_model_BOX6QUADS_OBJ
      return
   }

   if {"$VARmodeltype" == "BOX-6-QUADS" && "$VARouttype" == "PLY"} {
      write_model_BOX6QUADS_PLY
      return
   }

   if {"$VARmodeltype" == "BOX-12-TRIAS" && "$VARouttype" == "PLY"} {
      write_model_BOX12TRIAS_PLY
      return
   }

   if {"$VARmodeltype" == "BOX-12-TRIAS" && "$VARouttype" == "STL"} {
      write_model_BOX12TRIAS_STL
      return
   }


   if {"$VARmodeltype" == "ICOSA-20-TRIAS" && "$VARouttype" == "OBJ"} {
      write_model_ICOSA20TRIAS_OBJ
      return
   }


   if {"$VARmodeltype" == "TETRA-4-TRIAS" && "$VARouttype" == "OBJ"} {
      # write_model_TETRA4TRIAS_OBJ
      return
   }

   if {"$VARmodeltype" == "SPHERE-QUADS-TRIAS" && "$VARouttype" == "OBJ"} {
      # write_model_SPHEREquadsTrias_OBJ
      return
   }

   if {"$VARmodeltype" == "CONE-N-TRIAS" && "$VARouttype" == "OBJ"} {
      # write_model_CONEnTRIAS_OBJ
      return
   }

}
## END OF proc 'write_model_file'


##+#########################################################################
## proc  'write_model_BOX6QUADS_OBJ'
############################################################################
## PURPOSE: Write out a model file for 'BOX6QUADS' model type, into
##          a file with 'OBJ' data format.
##
## CALLED BY: 'write_model_file' proc
###########################################################################
## METHOD:  Uses the 'puts' command to write records.
##         
##          Writes 'v' (vertex) and 'f' (face) records.
##          For a description of the OBJ file format, see the HELPtext
##          variable that is set near the bottom of this script.
#############################################################################

proc write_model_BOX6QUADS_OBJ {} {

   global env outDIR VARwidth VARheight VARdepth

   set userID "$env(USER)"

   set OUTfilename "$outDIR/${userID}_box6quads.obj"

   set f [open $OUTfilename w]

   set CNTpoints 0
   set CNTpolygons  0

   ## Write a heading comment record to the file.

   puts $f "## BOX-6-QUADS MODEL.  WIDTH: $VARwidth  HEIGHT: $VARheight  DEPTH: $VARdepth"


   ## Write 8 'v' records for the corners of the box.

   puts $f "## OBJ file."
   puts $f "## 8 vertex records follow."

   incr CNTpoints
   puts $f "v 0.0 0.0 0.0"
   incr CNTpoints
   puts $f "v $VARwidth 0.0 0.0"
   incr CNTpoints
   puts $f "v $VARwidth $VARheight 0.0"
   incr CNTpoints
   puts $f "v 0.0 $VARheight 0.0"
   incr CNTpoints
   puts $f "v 0.0 0.0 $VARdepth"
   incr CNTpoints
   puts $f "v $VARwidth 0.0 $VARdepth"
   incr CNTpoints
   puts $f "v $VARwidth $VARheight $VARdepth"
   incr CNTpoints
   puts $f "v 0.0 $VARheight $VARdepth"

   ## Write 6 faces (rectangles) of the box.

   puts $f "##"
   puts $f "## 6 face records follow (all quadrilaterals)."

   incr CNTpolygons
   puts $f "f  1 4 3 2" ;# back
   incr CNTpolygons
   puts $f "f  5 6 7 8" ;# front
   incr CNTpolygons
   puts $f "f  6 2 3 7" ;# right
   incr CNTpolygons
   puts $f "f  1 5 8 4" ;# left
   incr CNTpolygons
   puts $f "f  4 8 7 3" ;# top
   incr CNTpolygons
   puts $f "f  1 2 6 5" ;# bottom

   ## Close the file.
   close $f

   ## FOR TESTING:
   #   puts "proc 'write_model_BOX6QUADS_OBJ' >  CNTpoints: $CNTpoints CNTpolygons: $CNTpolygons"

   .fRright.fRbuttons.labelINFO configure -text "Counts for OBJ file for model
BOX-6-QUADS - Points: $CNTpoints  Polygons: $CNTpolygons"

   ## Display the file in a GUI text-editor.

   edit_outfile "$OUTfilename"

}
## END OF PROC  'write_model_BOX6QUADS_OBJ'


##+########################################################################
## PROC 'write_model_BOX6QUADS_PLY'
##+########################################################################
## PURPOSE: Write out a model file for model-type 'BOX-6-QUADS', into
##          a file with 'PLY' data format.
##
## CALLED BY: 'write_model_file' proc
###########################################################################
## METHOD:  Uses the 'puts' command to write records.
##         
##          For a description of the PLY file format, see the 'HELPtext'
##          variable that is set near the bottom of this script.
######################################################################

proc write_model_BOX6QUADS_PLY {} {

   global env outDIR VARwidth VARdepth VARheight

   set userID "$env(USER)"

   set OUTfilename "$outDIR/${userID}_box6quads.ply"

   set f [open $OUTfilename w]

   set CNTpoints 0
   set CNTpolygons  0

   ## Write the PLY header records.

   puts $f "ply"
   puts $f "format ascii 1.0"
   puts $f "comment BOX-6-QUADS MODEL.  WIDTH: $VARwidth  HEIGHT: $VARheight  DEPTH: $VARdepth"
   puts $f "element vertex 8"
   puts $f "property float x"
   puts $f "property float y"
   puts $f "property float z"
   puts $f "element face 6"
   puts $f "property list uchar int vertex_indices"
   puts $f "end_header"


   ## Write 8 points for the corners of the box.
   ##
   ## We use points: (w=VARwidth,h=VARheight,d=VARdepth)
   ## 1 - 0 0 0
   ## 2 - w 0 0
   ## 3 - w h 0
   ## 4 - 0 h 0
   ## 5 - 0 0 d
   ## 6 - w 0 d
   ## 7 - w h d
   ## 8 - 0 h d

   puts $f "##"
   puts $f "## PLY Vertex records follow."

   incr CNTpoints
   puts $f "0.0 0.0 0.0"
   incr CNTpoints
   puts $f "$VARwidth 0.0 0.0"
   incr CNTpoints
   puts $f "$VARwidth $VARheight 0.0"
   incr CNTpoints
   puts $f "0.0 $VARheight 0.0"
   incr CNTpoints
   puts $f "0.0 0.0 $VARdepth"
   incr CNTpoints
   puts $f "$VARwidth 0.0 $VARdepth"
   incr CNTpoints
   puts $f "$VARwidth $VARheight $VARdepth"
   incr CNTpoints
   puts $f "0.0 $VARheight $VARdepth"

   ## Write the PLY face records (6 rectangles).

   puts $f "##"
   puts $f "## PLY Face records follow."


   incr CNTpolygons
   puts $f "4 0 3 2 1" ;# back
   incr CNTpolygons
   puts $f "4 4 5 6 7" ;# front
   incr CNTpolygons
   puts $f "4 5 1 2 6" ;# right
   incr CNTpolygons
   puts $f "4 0 4 7 3" ;# left
   incr CNTpolygons
   puts $f "4 3 7 6 2" ;# top
   incr CNTpolygons
   puts $f "4 0 1 5 4" ;# bottom

   ## Write the vertex and face counts into the last record in the file.
   puts $f "## Vertices: $CNTpoints    Polygons: $CNTpolygons"

   ## Close the file.
   close $f

   ## FOR TESTING:
   #   puts "proc 'write_model_BOX6QUADS_PLY' >  CNTpoints: $CNTpoints  CNTpolygons: $CNTpolygons"

   .fRright.fRbuttons.labelINFO configure -text "Counts for PLY file and model
BOX-6-QUADS -  Points (vertices) : $CNTpoints   Polygons (faces) : $CNTpolygons"

   ## Display the file in a GUI text-editor.

   edit_outfile "$OUTfilename"

}
## END OF PROC 'write_model_BOX6QUADS_PLY'


##+########################################################################
## PROC 'write_model_BOX12TRIAS_PLY'
##+########################################################################
## PURPOSE: Write out a model file for model-type 'BOX12TRIAS'--- a surface
##          made of 12 triangles --- in a file with Cyberware '.ply' data format.
##
## CALLED BY: 'write_model_file' proc
###########################################################################
## METHOD:  Uses the 'puts' command to write records.
##         
##          Writes vertex and face records to make a Cyberware '.ply' file.
##          For a description of the PLY file format, see the 'HELPtext'
##          variable that is set near the bottom of this script.
######################################################################

proc write_model_BOX12TRIAS_PLY {} {

   global env outDIR VARwidth VARdepth VARheight

   set userID "$env(USER)"

   set OUTfilename "$outDIR/${userID}_box12trias.ply"

   set f [open $OUTfilename w]

   set CNTpoints 0
   set CNTpolygons  0

   ## Write the PLY header records.

   puts $f "ply"
   puts $f "format ascii 1.0"
   puts $f "comment BOX-12-TRIAS MODEL.  WIDTH: $VARwidth  HEIGHT: $VARheight  DEPTH: $VARdepth"
   puts $f "element vertex 8"
   puts $f "property float x"
   puts $f "property float y"
   puts $f "property float z"
   puts $f "element face 12"
   puts $f "property list uchar int vertex_indices"
   puts $f "end_header"

   ## Write 8 points for the corners of the box.
   ##
   ## We use points: (w=VARwidth,h=VARheight,d=VARdepth)
   ## 1 - 0 0 0
   ## 2 - w 0 0
   ## 3 - w h 0
   ## 4 - 0 h 0
   ## 5 - 0 0 d
   ## 6 - w 0 d
   ## 7 - w h d
   ## 8 - 0 h d

   puts $f "##"
   puts $f "## PLY Vertex records follow."

   incr CNTpoints
   puts $f "0.0 0.0 0.0"
   incr CNTpoints
   puts $f "$VARwidth 0.0 0.0"
   incr CNTpoints
   puts $f "$VARwidth $VARheight 0.0"
   incr CNTpoints
   puts $f "0.0 $VARheight 0.0"
   incr CNTpoints
   puts $f "0.0 0.0 $VARdepth"
   incr CNTpoints
   puts $f "$VARwidth 0.0 $VARdepth"
   incr CNTpoints
   puts $f "$VARwidth $VARheight $VARdepth"
   incr CNTpoints
   puts $f "0.0 $VARheight $VARdepth"

   ## Write PLY face records.

   puts $f "##"
   puts $f "## PLY Face records follow (all triangles)."

   ## For faces, we use triangles made from the points as follows:
   ## (We convert base 1 to base 0.)
   ##  1 - 1 3 2 -> 0 2 1
   ##  2 - 1 4 3 -> 0 3 2
   ##  3 - 5 6 7 -> 4 5 6
   ##  4 - 5 7 8 -> 4 6 7
   ##  5 - 6 2 3 -> 5 1 2
   ##  6 - 6 3 7 -> 5 2 6
   ##  7 - 5 8 4 -> 4 7 3
   ##  8 - 5 4 1 -> 4 3 0
   ##  9 - 4 8 7 -> 3 7 6
   ## 10 - 4 7 3 -> 3 6 2
   ## 11 - 1 2 6 -> 0 1 5
   ## 12 - 1 6 5 -> 0 5 4

   incr CNTpolygons
   puts $f "3  0 2 1"
   incr CNTpolygons
   puts $f "3  0 3 2"
   incr CNTpolygons
   puts $f "3  4 5 6"
   incr CNTpolygons
   puts $f "3  4 6 7"
   incr CNTpolygons
   puts $f "3  5 1 2"
   incr CNTpolygons
   puts $f "3  5 2 6"
   incr CNTpolygons
   puts $f "3  4 7 3"
   incr CNTpolygons
   puts $f "3  4 3 0"
   incr CNTpolygons
   puts $f "3  3 7 6"
   incr CNTpolygons
   puts $f "3  3 6 2"
   incr CNTpolygons
   puts $f "3  0 1 5"
   incr CNTpolygons
   puts $f "3  0 5 4"

   ## Write the vertex and face counts into the last record in the file.
   puts $f "## Vertices: $CNTpoints    Polygons: $CNTpolygons"
   
   ## Close the file.
   close $f

   ## FOR TESTING:
   #   puts "proc 'write_model_BOX12TRIAS_PLY' >  CNTpoints: $CNTpoints  CNTpolygons: $CNTpolygons"

   .fRright.fRbuttons.labelINFO configure -text "Counts for PLY file and model
BOX-12-TRIAS -  Points (vertices) : $CNTpoints    Polygons (faces) : $CNTpolygons"

   ## Display the file in a GUI text-editor.

   edit_outfile "$OUTfilename"

}
## END OF PROC 'write_model_BOX12TRIAS_PLY'


##+########################################################################
## PROC 'write_model_BOX12TRIAS_STL'
##+########################################################################
## PURPOSE: Write out a model file for model-type 'BOX-12-TRIAS' --- a file
##          with 'ASCII STL' (stereolithography) data format.
##
## CALLED BY: 'write_model_file' proc
###########################################################################
## METHOD:  Uses the Tcl 'puts' command to write records.
##          The format of STL files is very simple.
##          See a decription of the format in the 'HELPtext' variable
##          that is set near the bottom of this script.
###########################################################################

proc write_model_BOX12TRIAS_STL {} {

   global env outDIR VARwidth VARdepth VARheight

   set userID "$env(USER)"

   set OUTfilename "$outDIR/${userID}_box12trias.stl"

   set f [open $OUTfilename w]

   set CNTpoints 0
   set CNTpolygons  0

   ## Write the top record of the STL file

   puts $f "solid BOX-12-TRIAS  WIDTH: $VARwidth  HEIGHT: $VARheight  DEPTH: $VARdepth ; ASCII STL FILE"

   ## Write the facet-groups of records for each of 12 triangles
   ## making up the faces of the box. Each group looks like:
   ##
   ##  facet normal  1.0   0.0   0.0    
   ##    outer loop
   ##      vertex    0.0   0.0   0.0    
   ##      vertex    1.0   1.0   0.0    
   ##      vertex    1.0   0.0   0.0    
   ##    endloop
   ##  endfacet
   ##
   ## We do not determine the normal. We simply put '1.0   0.0   0.0'
   ## as the values for each normal. There are programs like 'admesh'
   ## that can go through an STL file and correct the normals ---
   ## giving them an orientation corresponding to the specification
   ## of the 3 vertices.
   ##
   ## We use points: (w=VARwidth,h=VARheight,d=VARdepth)
   ## 1 - 0 0 0
   ## 2 - w 0 0
   ## 3 - w h 0
   ## 4 - 0 h 0
   ## 5 - 0 0 d
   ## 6 - w 0 d
   ## 7 - w h d
   ## 8 - 0 h d
   ## We use triangles made from the points as follows:
   ##  1 - 1 3 2
   ##  2 - 1 4 3
   ##  3 - 5 6 7
   ##  4 - 5 7 8
   ##  5 - 6 2 3
   ##  6 - 6 3 7
   ##  7 - 5 8 4
   ##  8 - 5 4 1
   ##  9 - 4 8 7
   ## 10 - 4 7 3
   ## 11 - 1 2 6
   ## 12 - 1 6 5

   ## Facet1:
   puts $f "facet normal  1.0   0.0   0.0"
   puts $f "  outer loop"
   puts $f "    vertex 0.0 0.0 0.0"
   puts $f "    vertex $VARwidth $VARheight 0.0"
   puts $f "    vertex $VARwidth 0.0 0.0"
   puts $f "  end loop"
   puts $f "endfacet"
   incr CNTpolygons

   ## Facet2:
   puts $f "facet normal  1.0   0.0   0.0"
   puts $f "  outer loop"
   puts $f "    vertex 0.0 0.0 0.0"
   puts $f "    vertex 0.0 $VARheight 0.0"
   puts $f "    vertex $VARwidth $VARheight 0.0"
   puts $f "  end loop"
   puts $f "endfacet"
   incr CNTpolygons

   ## Facet3:
   puts $f "facet normal  1.0   0.0   0.0"
   puts $f "  outer loop"
   puts $f "    vertex 0.0 0.0 $VARdepth"
   puts $f "    vertex $VARwidth 0.0 $VARdepth"
   puts $f "    vertex $VARwidth $VARheight $VARdepth"
   puts $f "  end loop"
   puts $f "endfacet"
   incr CNTpolygons

   ## Facet4:
   puts $f "facet normal  1.0   0.0   0.0"
   puts $f "  outer loop"
   puts $f "    vertex 0.0 0.0 $VARdepth"
   puts $f "    vertex $VARwidth $VARheight $VARdepth"
   puts $f "    vertex 0.0 $VARheight $VARdepth"
   puts $f "  end loop"
   puts $f "endfacet"
   incr CNTpolygons

   ## Facet5:
   puts $f "facet normal  1.0   0.0   0.0"
   puts $f "  outer loop"
   puts $f "    vertex $VARwidth 0.0 $VARdepth"
   puts $f "    vertex $VARwidth 0.0 0.0"
   puts $f "    vertex $VARwidth $VARheight 0.0"
   puts $f "  end loop"
   puts $f "endfacet"
   incr CNTpolygons

   ## Facet6:
   puts $f "facet normal  1.0   0.0   0.0"
   puts $f "  outer loop"
   puts $f "    vertex $VARwidth 0.0 $VARdepth"
   puts $f "    vertex $VARwidth $VARheight 0.0"
   puts $f "    vertex $VARwidth $VARheight $VARdepth"
   puts $f "  end loop"
   puts $f "endfacet"
   incr CNTpolygons

   ## Facet7:
   puts $f "facet normal  1.0   0.0   0.0"
   puts $f "  outer loop"
   puts $f "    vertex 0.0 0.0 $VARdepth"
   puts $f "    vertex 0.0 $VARheight $VARdepth"
   puts $f "    vertex 0.0 $VARheight 0.0"
   puts $f "  end loop"
   puts $f "endfacet"
   incr CNTpolygons

   ## Facet8:
   puts $f "facet normal  1.0   0.0   0.0"
   puts $f "  outer loop"
   puts $f "    vertex 0.0 0.0 $VARdepth"
   puts $f "    vertex 0.0 $VARheight 0.0"
   puts $f "    vertex 0.0 0.0 0.0"
   puts $f "  end loop"
   puts $f "endfacet"
   incr CNTpolygons

   ## Facet9:
   puts $f "facet normal  1.0   0.0   0.0"
   puts $f "  outer loop"
   puts $f "    vertex 0.0 $VARheight 0.0"
   puts $f "    vertex 0.0 $VARheight $VARdepth"
   puts $f "    vertex $VARwidth $VARheight $VARdepth"
   puts $f "  end loop"
   puts $f "endfacet"
   incr CNTpolygons

   ## Facet10:
   puts $f "facet normal  1.0   0.0   0.0"
   puts $f "  outer loop"
   puts $f "    vertex 0.0 $VARheight 0.0"
   puts $f "    vertex $VARwidth $VARheight $VARdepth"
   puts $f "    vertex $VARwidth $VARheight 0.0"
   puts $f "  end loop"
   puts $f "endfacet"
   incr CNTpoints 3
   incr CNTpolygons

   ## Facet11:
   puts $f "facet normal  1.0   0.0   0.0"
   puts $f "  outer loop"
   puts $f "    vertex 0.0 0.0 0.0"
   puts $f "    vertex $VARwidth 0.0 0.0"
   puts $f "    vertex $VARwidth 0.0 $VARdepth"
   puts $f "  end loop"
   puts $f "endfacet"
   incr CNTpolygons

   ## Facet12:
   puts $f "facet normal  1.0   0.0   0.0"
   puts $f "  outer loop"
   puts $f "    vertex 0.0 0.0 0.0"
   puts $f "    vertex $VARwidth 0.0 $VARdepth"
   puts $f "    vertex 0.0 0.0 $VARdepth"
   puts $f "  end loop"
   puts $f "endfacet"
   incr CNTpolygons

   ## Write the last record of the STL file

   puts $f "endsolid BOX-12-TRIAS   Facets (triangles) : $CNTpolygons"

   ## Close the file.
   close $f

   ## FOR TESTING:
   #   puts "proc 'write_model_BOX_STL' >  CNTpoints: $CNTpoints  CNTpolygons: $CNTpolygons"

   .fRright.fRbuttons.labelINFO configure -text "Counts for STL file and model
BOX-12-TRIAS -   Polygons (facets, triangles) : $CNTpolygons"

   ## Display the file in a GUI text-editor.

   edit_outfile "$OUTfilename"

}
## END OF PROC 'write_model_BOX12TRIAS_STL'


##+#########################################################################
## proc  'write_model_ICOSA20TRIAS_OBJ'
############################################################################
## PURPOSE: Write out a model file for model-type 'ICOSA-20-TRIAS' (12 vertices,
##          20 triangular faces) --- to a file with 'OBJ' data format.
##
## CALLED BY: 'write_model_file' proc when 'WriteFile' button is clicked.
###########################################################################
## METHOD:  Uses the 'puts' command to write records.
##
##          For a description of some of the output file formats,
##          see the 'HELPtext' variable near the bottom of this script
#############################################################################

proc write_model_ICOSA20TRIAS_OBJ {} {

   global env outDIR VARx VARz

   set userID "$env(USER)"

   set OUTfilename "$outDIR/${userID}_icosa20trias.obj"

   set f [open $OUTfilename w]

   set CNTpoints 0
   set CNTpolygons  0

   ## Write a heading comment record to the file.

   puts $f "## 'ICOSA-20-TRIAS' ICOSAHEDRON MODEL.  X: $VARx  Z: $VARz"
   puts $f "## OBJ file."
   puts $f "## 'v' (vertex) records follow."

   set VARminusx [expr {-$VARx}]
   set VARminusz [expr {-$VARz}]

   incr CNTpoints
   puts $f "v $VARminusx  0.0  $VARz"
   incr CNTpoints
   puts $f "v $VARx  0.0  $VARz"
   incr CNTpoints
   puts $f "v $VARminusx  0.0  $VARminusz"
   incr CNTpoints
   puts $f "v $VARx  0.0  $VARminusz"
   incr CNTpoints
   puts $f "v 0.0  $VARz  $VARx"
   incr CNTpoints
   puts $f "v 0.0 $VARz  $VARminusx"
   incr CNTpoints
   puts $f "v 0.0  $VARminusz  $VARx"
   incr CNTpoints
   puts $f "v 0.0 $VARminusz $VARminusx"
   incr CNTpoints
   puts $f "v $VARz  $VARx 0.0"
   incr CNTpoints
   puts $f "v $VARminusz  $VARx 0.0"
   incr CNTpoints
   puts $f "v $VARz  $VARminusx 0.0"
   incr CNTpoints
   puts $f "v $VARminusz $VARminusx 0.0"


   ## Write 20 faces (triangles) of the icosahedron.
   ## OBJ FACE RECORD FORMAT:
   ## f pointNUM1 pointNUM2 pointNUM3 ...
   ## NOTE: These indexes of the points are based at zero, not one.

   puts $f "##"
   puts $f "## 'f' (face) OBJ records follow."

   incr CNTpolygons
   puts $f "f  1 5 2"
   incr CNTpolygons
   puts $f "f  1 10 5"
   incr CNTpolygons
   puts $f "f  10 6 5"
   incr CNTpolygons
   puts $f "f  5 6 9"
   incr CNTpolygons
   puts $f "f  5 9 2"
   incr CNTpolygons
   puts $f "f  9 11 2"
   incr CNTpolygons
   puts $f "f  9 4 11"
   incr CNTpolygons
   puts $f "f  6 4 9"
   incr CNTpolygons
   puts $f "f  6 3 4"
   incr CNTpolygons
   puts $f "f  3 8 4"
   incr CNTpolygons
   puts $f "f  8 11 4"
   incr CNTpolygons
   puts $f "f  8 7 11"
   incr CNTpolygons
   puts $f "f  8 12 7"
   incr CNTpolygons
   puts $f "f  12 1 7"
   incr CNTpolygons
   puts $f "f  1 2 7"
   incr CNTpolygons
   puts $f "f  7 2 11"
   incr CNTpolygons
   puts $f "f  10 1 12"
   incr CNTpolygons
   puts $f "f  10 12 3"
   incr CNTpolygons
   puts $f "f  10 3 6"
   incr CNTpolygons
   puts $f "f  8 3 12"


   ## Write the vertex and face counts into the last record in the file.
   puts $f "## OBJ Vertices: $CNTpoints    OBJ faces (triangles): $CNTpolygons"

   ## Close the file.
   close $f

   ## FOR TESTING:
   #   puts "proc 'write_model_ICOSA20TRIAS_OBJ' >  CNTpoints: $CNTpoints   CNTpolygons: $CNTpolygons"

   .fRright.fRbuttons.labelINFO configure -text "Counts for OBJ file and model
ICOSA-20-TRIAS - Points: $CNTpoints  Polygons: $CNTpolygons"

   ## Display the file in a GUI text-editor.

   edit_outfile "$OUTfilename"

}
## END OF PROC  'write_model_ICOSA20TRIAS_OBJ'



##+########################################################################
## PROC 'popup_msg_var_scroll'
##+########################################################################
## PURPOSE: Report help or error conditions to the user.
## CALLED BY: 'help' button
##+########################################################################
## To have more control over the formatting of the message (esp.
## words per line), we use this 'toplevel-text' method, 
## rather than the 'tk_dialog' method -- like on page 574 of the book 
## by Hattie Schroeder & Mike Doyle,'Interactive Web Applications
## with Tcl/Tk', Appendix A "ED, the Tcl Code Editor".
##+########################################################################

proc popup_msg_var_scroll { VARtext } {

   ## global fontTEMP_varwidth #; Not needed. 'wish' makes this global.
   ## global env

   # bell
   # bell
  
   #################################################
   ## Set VARwidth & VARheight from $VARtext.
   #################################################
   ## To get VARheight,
   ##    split at '\n' (newlines) and count 'lines'.
   #################################################
 
   set VARlist [ split $VARtext "\n" ]

   ## For testing:
   #  puts "VARlist: $VARlist"

   set VARheight [ llength $VARlist ]

   ## For testing:
   #  puts "VARheight: $VARheight"


   #################################################
   ## To get VARwidth,
   ##    loop through the 'lines' getting length
   ##     of each; save max.
   #################################################

   set VARwidth 0

   #############################################
   ## LOOK AT EACH LINE IN THE LIST.
   #############################################
   foreach line $VARlist {

      #############################################
      ## Get the length of the line.
      #############################################
      set LINEwidth [ string length $line ]

      if { $LINEwidth > $VARwidth } {
         set VARwidth $LINEwidth 
      }

   }
   ## END OF foreach line $VARlist

   ## For testing:
   #   puts "VARwidth: $VARwidth"


   ###############################################################
   ## NOTE: VARwidth works for a fixed-width font used for the
   ##       text widget ... BUT the programmer may need to be
   ##       careful that the contents of VARtext are all
   ##       countable characters by the 'string length' command.
   ###############################################################


   #####################################
   ## SETUP 'TOP LEVEL' HELP WINDOW.
   #####################################

   catch {destroy .fRtopmsg}
   toplevel  .fRtopmsg

   # wm geometry .fRtopmsg 600x400+100+50

   wm geometry .fRtopmsg +100+50

   wm title     .fRtopmsg "Note"
   # wm title   .fRtopmsg "Note to $env(USER)"

   wm iconname  .fRtopmsg "Note"


   #####################################
   ## In the frame '.fRtopmsg' -
   ## DEFINE THE TEXT WIDGET and
   ## its two scrollbars --- and
   ## DEFINE an OK BUTTON widget.
   #####################################

   text .fRtopmsg.text \
      -wrap none \
      -font fontTEMP_varwidth \
      -width  $VARwidth \
      -height $VARheight \
      -bg "#f0f0f0" \
      -relief raised \
      -bd 2 \
      -yscrollcommand ".fRtopmsg.scrolly set" \
      -xscrollcommand ".fRtopmsg.scrollx set"

   scrollbar .fRtopmsg.scrolly \
                 -orient vertical \
      -command ".fRtopmsg.text yview"

   scrollbar .fRtopmsg.scrollx \
                -orient horizontal \
                -command ".fRtopmsg.text xview"

   button .fRtopmsg.butt \
      -text "OK" \
      -font fontTEMP_varwidth \
      -command  "destroy .fRtopmsg"

   ###############################################
   ## PACK *ALL* the widgets in frame '.fRtopmsg'.
   ###############################################

   ## Pack the bottom button BEFORE the
   ## bottom x-scrollbar widget,

   pack  .fRtopmsg.butt \
      -side bottom \
      -anchor center \
      -fill none \
      -expand 0

   ## Pack the scrollbars BEFORE the text widget,
   ## so that the text does not monopolize the space.

   pack .fRtopmsg.scrolly \
      -side right \
      -anchor center \
      -fill y \
      -expand 0

   ## DO NOT USE '-expand 1' HERE on the Y-scrollbar.
   ## THAT ALLOWS Y-SCROLLBAR TO EXPAND AND PUTS
   ## BLANK SPACE BETWEEN Y-SCROLLBAR & THE TEXT AREA.
                
   pack .fRtopmsg.scrollx \
      -side bottom \
      -anchor center \
      -fill x  \
      -expand 0

   ## DO NOT USE '-expand 1' HERE on the X-scrollbar.
   ## THAT KEEPS THE TEXT AREA FROM EXPANDING.

   pack .fRtopmsg.text \
      -side top \
      -anchor center \
      -fill both \
      -expand 1


   #####################################
   ## LOAD MSG INTO TEXT WIDGET.
   #####################################

   ##  .fRtopmsg.text delete 1.0 end
 
   .fRtopmsg.text insert end $VARtext
   
   .fRtopmsg.text configure -state disabled
  
}
## END OF PROC 'popup_msg_var_scroll'


set HELPtext "\
\ \ \ \ \ ** HELP for this 3D-Model-File-Generator Utility **

When the 3D-Model-Generator GUI comes up, the user can specify
3 types of items:

  1) FROM A LISTBOX:

     Specify a MODEL TYPE to be generated. Examples: box, tetrahedron, pyramid,
     prism, octahedron, icosahedron, ... , sphere, cone, cylinder, torus, ... ,
     spiral staircase, capital-A, ....

     The model-type ID usually includes some indication of the polygon types
     (triangles, quads, ...) that are to be generated --- and in some cases,
     the ID indicates the numbers of those polygon types.

     Example model-type ID's:  BOX-6-QUADS, SPHERE-QUADS-TRIAS, CONE-N-TRIAS, BUCKYBALL

  3) VIA ENTRY WIDGETS (and perhaps other widgets), with labels:

     Specify some PARAMETERS associated with that model type  --- such as width,
     height, depth, radius, number of longitudinal segments, ....

     The ENTRY widgets presented depend on the 'model-type' chosen.

  2) VIA RADIOBUTTONS:

     Specify an OUTPUT FILE TYPE (OBJ, PLY, OFF, STL, ...).


When the parameters are set and the desired output file type is
specified, then click on the 'WriteFile' button to create
the file of the specified type.

The file will be automatically displayed in a GUI text-editor.

    (The user can edit a variable named 'EDITOR_text' near the
     bottom of this script to specify the GUI editor to use.)

The output file may be put in a temporary-file directory,
such as '/tmp' on Linux/Unix. (That directory is specified by
a variable 'outDIR' at the bottom of the script.)

After 'touching up' the file with the editor (for example,
adding comment lines starting with '#'),
the user can use the 'SaveAs...' option of their text editor
to save the 3D model-data file in a directory of the user's choice.

---

ADDING MODEL-TYPES TO THIS UTILITY:

Over time, some additonal 'model-types' (and perhaps 'output-types')
may be added to this utility.

Within the script, this means that, along with each new model-type
entry inserted in the model-types listbox, an additional
'load_parameters_frame_XXX' proc and one or more additional
'write_model_XXX_YYY' procs will need to be added.

Adding a model type XXX also means that a new '.fRright.fRparameters_XXX'
frame needs to be defined in the 'load_parameters_frame_XXX' proc.
And appropriate widgets need to be defined and packed in
the frame. Usually, the widgets are labels and entry-field widgets ---
in which the user can enter parameters for creation of the XXX model type.

The user-programmer can use existing 'load_parameters_frame_XXX' and
'write_model_XXX_YYY' procs as a model for building new procs.

----------------------------------------------------------------------

DESCRIPTION OF SOME OF THE OUTPUT FILE FORMATS (OBJ, PLY, OFF, STL, CAD-like):

***************
OBJ FILE FORMAT:
***************

The Wavefront '.obj' file format is a standard 3D object
file format created for use with Wavefront's Advanced Visualizer.

'Object' Files are text based files supporting both polygonal and
free-form geometry (curves and surfaces).

The 'OBJ-model-writer' procs in this utility support a subset of
the file format (not the curves and surfaces, currently).

The supported geometry items (vertices, faces, lines) are enough
to create a wide variety of useful '.obj' Files.
 
The following text is a very brief description of the 'v' and 'f'
records in '.obj' files.
 
The model-writer procs in this utility write 'v ' records, but no
'vn' and 'vt' records.  The 'basic' '.obj' records are:

    # some text
      This line is for a comment, until the end of the line.

    v float float float
        A single vertex's geometric position in space. The first
        vertex listed in the file has index 1, and subsequent
        vertices are numbered sequentially.

    vn float float float
        A normal. The first normal in the file is index 1,
        and subsequent normals are numbered sequentially.

    vt float float
        A texture coordinate. The first texture coordinate in
        the file is index 1, and subsequent textures are numbered
        sequentially.

    f int int int ...
        or
    f int/int int/int int/int . . .
        or
    f int/int/int int/int/int int/int/int ...
        A polygonal face. The numbers are indexes into the arrays
        of vertex positions, texture coordinates, and normals
        respectively. A number may be omitted if, for example,
        texture coordinates are not being defined in the model.
        There is no maximum number of vertices that a single
        polygon may contain. The .obj file specification says
        that each face must be flat and convex.

A very elementary example file is given below (it is a box):

v 1 1 1
v 1 1 -1
v 1 -1 1
v 1 -1 -1
v -1 1 1
v -1 1 -1
v -1 -1 1
v -1 -1 -1
f 1 3 4 2
f 5 7 8 6
f 1 5 6 2
f 3 7 8 4
f 1 5 7 3
f 2 6 8 4

It is also quite common to see 'f' records in one of the
following formats:

    f 1//1 2//2 3//3 4//4
    f 1/1 2/2 3/3 4/4
    f 6/4/1 3/5/3 7/6/5

The 'OBJ-model-writer' procs of this utility simply write
'f' records in the

    f int int int ... 

format.

----------------------------------------------------------------------

***************
PLY FILE FORMAT:
***************

The Cyberware '.ply' file format for most files that one
encounters is fairly simple. It is tyically composed of
three sections:

     - A set of about 11 ASCII text records declaring that
       the file is a 'ply' file and indicating the number
       of 'vertex' and 'face' records in the file.

     - Vertex records containing xyz coordinates.

     - Face records containing polygon data --- typically
       the number 3 (indicating that it is a triangular face)
       followed by 3 integers referring to the vertex records
       with vertex count starting at 0, not 1.

Here is an example PLY file:

ply
format ascii 1.0
element vertex 162
property float x
property float y
property float z
element face 320
property list uchar int vertex_indices
end_header
0 -0.525731 0.850651 
0.850651 0 0.525731 
...
... (more vertex records in here)
...
-0.688191 -0.425325 0.587785 
-0.425325 -0.587785 0.688191 
3 0 42 43 
3 0 43 44 
...
... (more face rectords in here)
...
3 156 157 158 
3 159 160 161 


You can get more info on the format of '.ply' files from
the Cyberware website: www.cyberware.com


----------------------------------------------------------------------

***************
OFF FILE FORMAT:
***************

The OFF file format is similar to the PLY file format.

Example OFF header records:

  OFF
  #created by dirichlet domain computation.
  36   20        54

I have seen 'OFF'-type files that start with
'NOFF' and '4OFF' instead of 'OFF'.

The 'OFF-writer' procs in this utility simply put
'OFF' in the first record of an OFF file.

NOTE: The 3 integers in the 3rd header record above
      are for vertices,faces,edges.

The header records are followed by vertex records,
then face records.

Example OFF 'vertex' records:

  0.272269        0.118712        0.520703
  -0.000222        0.002388        0.617335
  0.000222        -0.002388        0.617335


Example OFF 'face' record format:

  3   14 51 54              (for a triangle)
  4        8   7         10        9         (for a quadrangle)
  5         27        6        5        1        0         (for a pentagon)
  6        29        20         21        9        10        28  (for a hexagon)
  7         32        15        18        25        24        2        35  (for a 7-gon)
 ..
 ..
 10   128 48 49 45 46 47 125 126 129 127  (for a 10-gon)


I have seen an extra integer at the end of face records
in an OFF file. This does not appear to be normal.

Unless other information recommends otherwise,
the 'OFF-writer' procs in this utility will normally
not put more integers in a face record than are
indicated by the first integer.


----------------------------------------------------------------------

***************
STL FILE FORMAT:
***************

The format of STL files is very simple.
Here is an example of a 'facet group' of data:

facet normal  0.0   0.0  -1.0    
  outer loop
    vertex    0.0   0.0   0.0    
    vertex    1.0   1.0   0.0    
    vertex    1.0   0.0   0.0    
  endloop
endfacet

These are usually the only types of data records in the file,
except for a record like 'solid MYSOLID' at the top of the file
and a record like 'endsolid MYSOLID' at the bottom of the file.

All facets are triangles. To write out an object like a box, 
each rectangular side needs to be split into at least 2 triangles. 

This model-file-writers of this utility may put dummy values,
like 1.0 0.0 0.0, in the 'facet normal' records.

There are programs (example: 'admesh') that can
read through an STL file and write out a new STL file with
corrected normals --- giving each normal an orientation
corresponding to the order of the 3 vertices.

NOTE: These STL-write procs do not write out 'true' STL files.
'True' STL files ordinarily consist of triangles making up
the four surfaces of tetrahedrons. Most of the faces of the
tetrahedrons match up with the faces of other tetrahedrons
--- thus yielding many triangles which are 'coinciding
duplicates' (but with opposite normals, i.e. different
ordering of the vertices of the 2 triangles).

In contrast, these 'STL-writers' just write one of the two
triangles --- which is sufficient for the purpose of
using the STL file in a 3D model viewer utility, to
simply see the triangles making up the surface(s)
of a 3D model.

----------------------------------------------------------------------

**********************
A CAD-like FILE FORMAT:
**********************

The file formats above (OBJ,PLY,OFF,STL) do not put vertex numbers or
face numbers in the vertex and face records. HOWEVER, that is a common
type of format in various CAD, CAE, FEA 'exchange' files.

So this utility may eventually support a 'CAD-like' file format ---
something like the following description.

We define a 'CAD-like' 'pseudo' file format as follows.

The file may contain 'PT' (point), 'LN' (line), and 'PG' (polygon)
record types.

FORMAT OF THE DATA RECORDS:  (fields are separated by 'white-space')

In FIELD 1 (2 characters):

   'PT' denotes a 'point' record,
   'LN' denotes a 'line' record,
   'PG' denotes a 'polygon' record.
     (We may use 'PL' for 'polyline', if needed.)

In FIELD 2 (a string with no embedded spaces):

   The hex RGB values for a color. Example: #ff00ff for magenta.

   We could support decimal RGB values between 0 and 1 by allowing hyphens, say,
   to separate the 3 RGB decimals. Example: 0.25-0.39-0.0

   Similarly, we could support integer RGB values, between 0 and 255.
   Example: 15-25-0

   Alternatively, this field could contain a group ID or material ID,
   typically an integer.

In FIELD 3 (an integer):

   An integer ID, unique for that record-type.
   Example: 83 in a PT record denotes point number 83.

   These integers may have meaning to the user or person who prepared the data,
   but they do not necessarily have to be used to identify the data or record
   by programs processing the records. Those programs may keep their own
   counts/indexes to the records and their data.

For FIELD 4 and greater (numbers - real or integer):

   For 'PT' (point)    recs, cols 4,5,6 contain the x,y,z coords of the point.
   For 'LN' (line)     recs, cols 4,5 contain point ID numbers.
   For 'PG' (polygon)  recs, col 4 contains the number of points in the
                             polygon (3 for triangle, 4 for quadrilateral, etc.),
                             and cols 5,6,7,... contain point ID numbers.
                             NOTE: We will ordinarily restrict ourselves
                                   to using triangles or quads for 3D models.

EXAMPLES of 'PT', 'L ', and 'PG' record formats:

POINT RECORD FORMAT:
PT  hexcolor point#   x         y         z
Example:
PT #ffffff    23       0.0       0.0       0.0

LINE RECORD FORMAT:
L  hexcolor line#   pointNUM1 pointNUM2
Example:
L  #ff0000    10     1     2

POLYGON RECORD FORMAT:
PG hexcolor polygon# numPoints pointNUM1 pointNUM2 pointNUM3 ...
Example (a triangle):
PG #ff0000    19     3     2     5    17
"

## END of setting var 'HELPtext'.

##+#####################################################
## The Additional-GUI-Initialization SECTION:
##+#####################################################

############################################################
## We set some 'universal' constants that may be used in the
## 'write' procs for some of the models.
############################################################

set pi [expr {4.0 * atan(1.0)}]
set twopi [expr { 2.0 * $pi }]
set pihalf  [expr { $pi / 2.0 }]
# set minuspihalf [expr {-$pihalf}]

#########################################################
## Set a default directory for the model output files ---
## OBJ, PLY, OFF, STL, whatever.
#########################################################

set outDIR "/tmp"


#########################################################
## Set a GUI text editor to be used to show the user
## the model output files.
#########################################################

# set EDITOR_text "gedit"
# set EDITOR_text "/usr/bin/gedit"
set EDITOR_text "$env(HOME)/apps/gscite_2.27/SciTE"


##################################################
## Set the name of the current 'parameters' frame.
##################################################

set curPARMframe ".fRright.fRparameters"


####################################
## Set a default output-file-type.
####################################

set VARouttype "OBJ"

##################################################################
## OPTIONAL:
## Set a default model-type and use its 'load_parameters_frame'
## proc to replace the current '.fRright.fRparmeters' frame
## by the appropriate parameters frame '.fRright.fRparameters_XXX'
## --- by calling the proc 'load_parameters_frame_XXX'.
##
## We also set an appropriate default output-file-type for this
## model-type.
##
## For the specified model-type XXX, the 'load_parameters_frame_XXX'
## proc should define the '.fRright.fRparameters_XXX' frame and
## define-and-pack the widgets of that frame.
## Then, the 'load_parameters_frame_XXX' proc simply has to
## 'pack forget' the current frame (in $curPARMframe) and pack the
## frame '.fRright.fRparameters_XXX' and the parameter-prompting
## widgets will suddenly, magically appear.
#################################################################

set VARmodeltype "BOX-6-QUADS"
set VARouttype "OBJ"
load_parameters_frame_BOX6QUADS

Setting the Text Editor

Another not-so-common feature of this Tk script --- besides the 'pack forget' technique that is used to 'dynamically' change the parameters frame in the GUI --- is that, after the model file is written, it is displayed to the user in a GUI text editor of the user's choice.

You can see in the text of the 'HELPtext' variable, set at the bottom of the script above, that the user can edit the proc 'edit_outfile' to set the text-editor to be the user's preferred editor.

The editor is called up from within the Tk script by using the Tcl 'exec' command.

---

The 'writers' are so fast that IMMEDIATELY after clicking on the 'WriteFile' button, the text-editor pops up displaying the model file.


Some Possible Enhancements

As I work on some 3D model and 3D surface viewing utilities in coming months, I may add several 'model types' to this GUI. So, if you find this utility of use, you may want to check back here every few months in 2012 and 2013 to see if an updated version of the script is available.

On the subject of enhancements/additions, I have put a random-color proc in the code, but I have not used it yet. I may eventually add a checkbutton (or two) to the GUI so that the user can choose to add colors chosen from a wide range (or from a restricted range, such as the spectrum of the rainbow). For example, the colors could be put into the the polygon and/or point records in the model file formats that support colors of polygons and/or points.

Adding colors to these models may help me test out 3D model viewers when I add features for color shading of polygons. Initially, I plan to support solid colors in the polygons --- perhaps adding 'solid shading' depending on the height/depth of the polygons or depending on the angles that the polygons make with a direction vector from a light source.


Conclusion

It took a couple of days to get this code in shape. But now I am in a position to generate model files (of various dimensions and shapes and numbers of polygons/ponts) at about 30 microseconds per model file, instead of over 30 minutes per model file. :-) :-) :-) :-) :-)

Thank you, Tcl-Tk developers, for making it possible for me to make nice quality images of geometry in 3-space.


uniquename 2013jan27 UPDATE

I changed the GUI rather significantly by changing the model-type selection from using a couple of rows of radiobuttons to a using a listbox on the left of the GUI.

I have replaced the two GUI images above with the new images, and I have replaced the code above with the new images.

You can see the 'set' statement for the 'HELPtext' variable, near the bottom of the script, for a description of how to add new 'model-types' to the listbox.

Some 'model-types' that I have not implemented yet can be seen in the listbox of the images above. They are indicated by a hash-sign at the beginning of those listbox lines.

Over the coming months, I plan to implement more of those 'model-types'.