[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 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'''. After a couple of days of coding, 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 (currently grayed out) that may be implemented in the future. [3DmodelGenerator_box_screenshot_644x173.jpg] and [3DmodelGenerator_icosahedron_screenshot_643x173.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. ___ 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 availabe '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 parameters such as distances (length, radius, etc.) ## ## and then click a button to generate a 3D model data file ## according to a chosen format type ('CAD-like', OBJ, etc.). ## ## The 'CAD-like' format includes records for points,lines,polygons ## that are in a format typical of 3D CAD and CAE data files. ## ## Namely, the 'point' records are typically of a format like ## ## PT hexcolor point# x y z ## Example: ## PT #ffffff 40 -1.0 0.0 0.0 ## ## The 'line' records, if any, are typically for a format like ## ## L hexcolor line# pointNUM1 pointNUM2 ## Example: ## L #00ff00 20 1 3 ## ## The 'polygon' records are typically of a format like ## ## PG hexcolor polygon# numPoints pointNUM1 pointNUM2 pointNUM3 ... ## Example: ## PG #0000ff 30 3 0 3 4 ## ## The record-type indicator in the first field may vary ## in various CAD/CAE systems. For example, points may ## be denoted by names other than 'PT' --- such as ## 'POINT', 'NODE', 'VERTEX', 'v', etc. ## ## But the format of these records is meant to be 'generic-enough' ## so that the records could be fairly easily changed into ## the format of most CAD/CAE systems. ## ######### ## 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. ## (The user can edit the line in the 'write_model_file' proc ## that specifies the editor to use.) ## ## The output file may be put in a temporary-file directory, ## such as /tmp on Linux/Unix. The user can use the 'SaveAs...' ## option of their text editor to save the file in a directory ## of the user's choice. ## ################# ## THE GUI LAYOUT: ## The GUI contains the following frames and widgets: ## ## - 1 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 frame for some RADIOBUTTONS --- used to specify ## 3D model-file type --- box, octahedron, sphere, whatever. ## ## - 1 frame for LABEL and ENTRY widgets and whatever ## other kinds of widgets are appropriate for the ## user-specified output type. ## ## (Note: There may actually be frame(s) (with widgets) ## defined for each output type. This frame will be ## replaced by the frame(s) appropriate to the ## user-selected output type. A button1-release ## binding on each of the radiobuttons above 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.) ## ## - 1 frame for some RADIOBUTTONS --- used to specify ## output-file type --- 'CAD-like', OBJ, whatever. ## ##+###################################################################### ## '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 : '.fRbuttons' '.fRmodeltypes' '.fRparameters' '.filetypes' ## ## Sub-frames: none ## ## 1b) Pack ALL frames. ## ## 2) Define all widgets in the frames (and pack them): ## ## - In '.fRbuttons': 3 button widgets ('Exit','Help','Write') and ## 1 label widget ## ## - In '.fRmodeltypes1': several radiobutton widgets ## - In '.fRmodeltypes2': several radiobutton widgets ## (more?) ## ## - In '.fRparameters_XXX': label,entry(,other?) widgets, ## where XXX is a name indicating the model-type ## --- BOX, OCTAHEDRON, SPHERE, whatever ## (This frame is overlaid by a frame chosen ## via one of the model-types radiobuttons.) ## ## - In '.fRouttypes' frame: several radiobutton widgets, ## for output type --- 'CAD-like', OBJ, PLY, DXF, ## STL, whatever. ## ## 3) Define BINDINGS: ## ## button1-release bindings on the model-type radiobuttons, ## to call on a 'load_parameters_frame_for_XXX' to change ## the '.fRparameters_XXX' frame. ## ## 4) Define PROCS: ## ## 'load_parameters_frame_for_XXX' - called by the bindings above, ## according to which model-type radio-button ## is clicked. Example name: ## 'load_parameters_frame_BOX' ## ## 'write_model_XXX_YYY' - called when the 'Write' button is clicked. ## Writes the output file according to the ## XXX model-type radiobutton setting and the ## YYY output-type radiobutton setting. ## Example name: ## 'write_model_BOX_CADlike' ## ## 'popup_msg_var_scroll' - for a Help button ## ## 5) Additional GUI initialization: ## We set initial, radiobutton setting for model-type and for ## output-type. Example: Box and CADlike. ## ## The 'load_parameters_frame_for_XXX' for Box (or whatever) ## is also called here, to display the '.fRparameters_XXX' frame, ## which will 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 'Write' 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 'Write'. ## ##+######################################################################## ## 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: ...... ......... 2012nov ##+####################################################################### ##+####################################################################### ## 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_entry "#ffffff" set BKGD_radbutt "#ffffff" # set BKGD_scale "#ffffff" ##+######################################################## ## 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. width and height of canvas, and padding for Buttons) ##+########################################################### ## For LABEL widgets: set PADYpx_label 0 set PADXpx_label 0 # set BDwidth_label 2 set BDwidthPx_label 0 ## For BUTTON widgets: set PADXpx_button 0 set PADYpx_button 0 set BDwidthPx_button 2 ## For ENTRY widgets: set BDwidthPx_entry 2 ## For SCALE widgets: # set BDwidthPx_scale 2 # set scaleLengthPx 200 # set scaleLabelChars 10 ## 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 ## 1 char high for the 'fRbuttons' frame ## 1 char high for the 'fRmodeltypes1' frame ## 1 char high for the 'fRmodeltypes2' frame ## 1 char high for the 'fRparameters_XXX' frame ## 1 char high for the 'fRouttypes' frame set charHeightPx [font metrics fontTEMP_varwidth -linespace] set minWinHeightPx [expr {5 * $charHeightPx}] ## Add some pixels to account for top-bottom window decoration ## (about 28 pixels) and frame/widget padding vertically ## (about 2 pixels/frame x 5 frames). set minWinHeightPx [expr {38 + $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"} set aRtext(buttonEXIT) "Exit" set aRtext(buttonHELP) "Help" set aRtext(buttonWRITE) "WriteFile" set aRtext(labelMODELTYPES) "3D Model Type:" set aRtext(radbuttBOX) "Box" set aRtext(radbuttPRISM) "Prism" set aRtext(radbuttTETRAHEDRON) "Tetrahedron" set aRtext(radbuttOCTAHEDRON) "Octahedron" set aRtext(radbuttICOSAHEDRON) "Icosahedron" set aRtext(radbuttBUCKYBALL) "Buckyball" set aRtext(radbuttSPHERE) "Sphere" set aRtext(radbuttCONE) "Cone" set aRtext(radbuttCYLINDER) "Cylinder" set aRtext(radbuttELLIPSOID) "Ellipsoid" set aRtext(radbuttTORUS) "Torus" set aRtext(labelWIDTH) "Width:" set aRtext(labelHEIGHT) "Height:" set aRtext(labelDEPTH) "Depth:" set aRtext(labelOUTTYPES) "Output File Format:" set aRtext(radbuttCADlike) "CAD-like" set aRtext(radbuttOBJ) "OBJ" set aRtext(radbuttPLY) "PLY" set aRtext(radbuttSTL) "STL" ## END OF if { "$VARlocale" == "en"} ##+################################################################ ## DEFINE *ALL* THE FRAMES: ## ## Top-level : 'fRbuttons', 'fRmodeltypes1', 'fRmodeltypes2', ## 'fRparameters_XXX' for many XXX, 'fRouttypes' ## ## Sub-frames: none ##+################################################################ ## 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 .fRbuttons -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRmodeltypes -relief raised -borderwidth 2 frame .fRparameters -relief $RELIEF_frame -borderwidth $BDwidth_frame ## The following '.fRparameters_XXX' frames are not packed here. ## Each one may be packed by a 'load_parameters_frame_XXX' proc, when ## the proc is triggered by a click on a model-types radiobutton. ## ## The frame will be packed AFTER the previous parameters-frame is REMOVED ## by a 'pack forget' command, called in the 'load_parameters_frame_XXX' proc. frame .fRparameters_BOX -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRparameters_TETRAHEDRON -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRparameters_OCTAHEDRON -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRparameters_ICOSAHEDRON -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRparameters_SPHERE -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRparameters_CONE -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRparameters_CYLINDER -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRparameters_TORUS -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRouttypes -relief raised -borderwidth 2 ## Define sub-frames. frame .fRmodeltypes.fRmodeltypes1 -relief $RELIEF_frame -borderwidth $BDwidth_frame frame .fRmodeltypes.fRmodeltypes2 -relief $RELIEF_frame -borderwidth $BDwidth_frame ##+############################## ## PACK the top-level FRAMES. ##+############################## pack .fRbuttons \ -side top \ -anchor nw \ -fill none \ -expand 0 pack .fRmodeltypes \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRparameters \ -side top \ -anchor nw \ -fill none \ -expand 0 pack .fRouttypes \ -side top \ -anchor nw \ -fill x \ -expand 0 ## Pack the sub-frames. pack .fRmodeltypes.fRmodeltypes1 \ .fRmodeltypes.fRmodeltypes2 \ -side top \ -anchor nw \ -fill x \ -expand 0 ## OK. ALL frames are defined. 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 THE '.fRbuttons' frame - ## DEFINE several BUTTONS (Exit,Help,Write) and a LABEL widget. ##+################################################################ button .fRbuttons.buttEXIT \ -text "$aRtext(buttonEXIT)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {exit} button .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 .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 .fRbuttons.labelINFO \ -text "" \ -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 .fRbuttons.buttEXIT \ .fRbuttons.buttHELP \ .fRbuttons.buttWRITE \ .fRbuttons.labelINFO \ -side left \ -anchor w \ -fill none \ -expand 0 ##+################################################################ ## IN THE '.fRmodeltypes1' frame - ## DEFINE several RADIOBUTTON widgets. ## Then PACK them. ##+################################################################ label .fRmodeltypes.fRmodeltypes1.labelMODELTYPES \ -text "$aRtext(labelMODELTYPES)" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief raised \ -bd 2 ## The radiobuttons' variable, VARmodeltype, is ## initialized in the additional-GUI-initialization ## section at the bottom of this script. radiobutton .fRmodeltypes.fRmodeltypes1.radbuttBOX \ -text "$aRtext(radbuttBOX)" \ -font fontTEMP_varwidth \ -anchor w \ -variable VARmodeltype \ -value "BOX" \ -selectcolor "$BKGD_radbutt" \ -relief flat \ -bd $BDwidthPx_button radiobutton .fRmodeltypes.fRmodeltypes1.radbuttTETRAHEDRON \ -text "$aRtext(radbuttTETRAHEDRON)" \ -font fontTEMP_varwidth \ -anchor w \ -variable VARmodeltype \ -value "TETRAHEDRON" \ -selectcolor "$BKGD_radbutt" \ -relief flat \ -bd $BDwidthPx_button \ -state disabled radiobutton .fRmodeltypes.fRmodeltypes1.radbuttOCTAHEDRON \ -text "$aRtext(radbuttOCTAHEDRON)" \ -font fontTEMP_varwidth \ -anchor w \ -variable VARmodeltype \ -value "OCTAHEDRON" \ -selectcolor "$BKGD_radbutt" \ -relief flat \ -bd $BDwidthPx_button \ -state disabled radiobutton .fRmodeltypes.fRmodeltypes1.radbuttICOSAHEDRON \ -text "$aRtext(radbuttICOSAHEDRON)" \ -font fontTEMP_varwidth \ -anchor w \ -variable VARmodeltype \ -value "ICOSAHEDRON" \ -selectcolor "$BKGD_radbutt" \ -relief flat \ -bd $BDwidthPx_button radiobutton .fRmodeltypes.fRmodeltypes1.radbuttBUCKYBALL \ -text "$aRtext(radbuttBUCKYBALL)" \ -font fontTEMP_varwidth \ -anchor w \ -variable VARmodeltype \ -value "BUCKYBALL" \ -selectcolor "$BKGD_radbutt" \ -relief flat \ -bd $BDwidthPx_button \ -state disabled ## Pack ALL the widgets in frame 'fRfile'. pack .fRmodeltypes.fRmodeltypes1.labelMODELTYPES \ .fRmodeltypes.fRmodeltypes1.radbuttBOX \ .fRmodeltypes.fRmodeltypes1.radbuttTETRAHEDRON \ .fRmodeltypes.fRmodeltypes1.radbuttOCTAHEDRON \ .fRmodeltypes.fRmodeltypes1.radbuttICOSAHEDRON \ .fRmodeltypes.fRmodeltypes1.radbuttBUCKYBALL \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## In the '.fRmodelypes2' frame - ## DEFINE MORE RADIOBUTTON widgets. ## Then PACK them. ##+######################################################## ## The radiobuttons' variable, VARmodeltype, is ## initialized in the additional-GUI-initialization ## section at the bottom of this script. radiobutton .fRmodeltypes.fRmodeltypes2.radbuttSPHERE \ -text "$aRtext(radbuttSPHERE)" \ -font fontTEMP_varwidth \ -anchor w \ -variable VARmodeltype \ -value "SPHERE" \ -selectcolor "$BKGD_radbutt" \ -relief flat \ -bd $BDwidthPx_button \ -state disabled radiobutton .fRmodeltypes.fRmodeltypes2.radbuttCONE \ -text "$aRtext(radbuttCONE)" \ -font fontTEMP_varwidth \ -anchor w \ -variable VARmodeltype \ -value "CONE" \ -selectcolor "$BKGD_radbutt" \ -relief flat \ -bd $BDwidthPx_button \ -state disabled radiobutton .fRmodeltypes.fRmodeltypes2.radbuttCYLINDER \ -text "$aRtext(radbuttCYLINDER)" \ -font fontTEMP_varwidth \ -anchor w \ -variable VARmodeltype \ -value "CYLINDER" \ -selectcolor "$BKGD_radbutt" \ -relief flat \ -bd $BDwidthPx_button \ -state disabled radiobutton .fRmodeltypes.fRmodeltypes2.radbuttELLIPSOID \ -text "$aRtext(radbuttELLIPSOID)" \ -font fontTEMP_varwidth \ -anchor w \ -variable VARmodeltype \ -value "ELLIPSOID" \ -selectcolor "$BKGD_radbutt" \ -relief flat \ -bd $BDwidthPx_button \ -state disabled radiobutton .fRmodeltypes.fRmodeltypes2.radbuttTORUS \ -text "$aRtext(radbuttTORUS)" \ -font fontTEMP_varwidth \ -anchor w \ -variable VARmodeltype \ -value "TORUS" \ -selectcolor "$BKGD_radbutt" \ -relief flat \ -bd $BDwidthPx_button \ -state disabled ## Pack ALL the widgets in frame 'fRmodeltypes2'. pack .fRmodeltypes.fRmodeltypes2.radbuttSPHERE \ .fRmodeltypes.fRmodeltypes2.radbuttCONE \ .fRmodeltypes.fRmodeltypes2.radbuttCYLINDER \ .fRmodeltypes.fRmodeltypes2.radbuttELLIPSOID \ .fRmodeltypes.fRmodeltypes2.radbuttTORUS \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## In the '.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. ##+######################################################## label .fRparameters.labelDUMMY \ -text "XXX" \ -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 .fRparameters.labelDUMMY \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## In the '.fRparameters_BOX' 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 .fRparameters_BOX.labelWIDTH \ -text "$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 .fRparameters_BOX.entWIDTH \ -textvariable VARwidth \ -font fontTEMP_fixedwidth \ -width 7 \ -bg "$BKGD_entry" \ -relief sunken \ -bd $BDwidthPx_entry label .fRparameters_BOX.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 .fRparameters_BOX.entHEIGHT \ -textvariable VARheight \ -font fontTEMP_fixedwidth \ -width 7 \ -bg "$BKGD_entry" \ -relief sunken \ -bd $BDwidthPx_entry label .fRparameters_BOX.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 .fRparameters_BOX.entDEPTH \ -textvariable VARdepth \ -font fontTEMP_fixedwidth \ -width 7 \ -bg "$BKGD_entry" \ -relief sunken \ -bd $BDwidthPx_entry ## Pack the 3 entry widgets. pack .fRparameters_BOX.labelWIDTH \ .fRparameters_BOX.entWIDTH \ .fRparameters_BOX.labelHEIGHT \ .fRparameters_BOX.entHEIGHT \ .fRparameters_BOX.labelDEPTH \ .fRparameters_BOX.entDEPTH \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## In the '.fRparameters_ICOSAHEDRON' frame - ## DEFINE 2 ENTRY widgets, for 'X' and 'Z' ## --- along with 2 LABEL widgets. ## PACK the 4 label and entry widgets. ## ## Reference: OpenGL Programming Guide, Chapter 2, Drawing ## Geometric Objects ## ## 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 .fRparameters_ICOSAHEDRON.labelX \ -text "X:" \ -font fontTEMP_varwidth \ -padx $PADXpx_label \ -pady $PADYpx_label \ -justify left \ -anchor w \ -relief raised \ -bd $BDwidthPx_label set VARx "0.525731" entry .fRparameters_ICOSAHEDRON.entX \ -textvariable VARx \ -font fontTEMP_fixedwidth \ -width 10 \ -bg $BKGD_entry \ -relief sunken \ -bd $BDwidthPx_entry label .fRparameters_ICOSAHEDRON.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 .fRparameters_ICOSAHEDRON.entZ \ -textvariable VARz \ -font fontTEMP_fixedwidth \ -width 10 \ -bg $BKGD_entry \ -relief sunken \ -bd $BDwidthPx_entry ## Pack the 2 entry widgets, with labels. pack .fRparameters_ICOSAHEDRON.labelX \ .fRparameters_ICOSAHEDRON.entX \ .fRparameters_ICOSAHEDRON.labelZ \ .fRparameters_ICOSAHEDRON.entZ \ -side left \ -anchor w \ -fill none \ -expand 0 ##+###################################################### ## In frame '.fRouttypes' - ## DEFINE a LABEL widget and several RADIOBUTTON widgets. ## Then PACK them. ##+##################################################### label .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 .fRouttypes.radbuttCADlike \ -text "$aRtext(radbuttCADlike)" \ -font fontTEMP_varwidth \ -anchor w \ -variable VARouttype \ -value "CAD-like" \ -selectcolor "$BKGD_radbutt" \ -relief flat \ -bd $BDwidthPx_button radiobutton .fRouttypes.radbuttOBJ \ -text "$aRtext(radbuttOBJ)" \ -font fontTEMP_varwidth \ -anchor w \ -variable VARouttype \ -value "OBJ" \ -selectcolor "$BKGD_radbutt" \ -relief flat \ -bd $BDwidthPx_button radiobutton .fRouttypes.radbuttPLY \ -text "$aRtext(radbuttPLY)" \ -font fontTEMP_varwidth \ -anchor w \ -variable VARouttype \ -value "PLY" \ -selectcolor "$BKGD_radbutt" \ -relief flat \ -bd $BDwidthPx_button radiobutton .fRouttypes.radbuttSTL \ -text "$aRtext(radbuttSTL)" \ -font fontTEMP_varwidth \ -anchor w \ -variable VARouttype \ -value "STL" \ -selectcolor "$BKGD_radbutt" \ -relief flat \ -bd $BDwidthPx_button ## Pack ALL the widgets in frame 'fRouttypes'. pack .fRouttypes.labelOUTTYPES \ .fRouttypes.radbuttCADlike \ .fRouttypes.radbuttOBJ \ .fRouttypes.radbuttPLY \ .fRouttypes.radbuttSTL \ -side left \ -anchor w \ -fill none \ -expand 0 ##+##################################################################### ## END OF SECTIONS TO SETUP THE GUI. ## ALL FRAMES and WIDGETS are DEFINED. ##+##################################################################### ##+####################################################################### ## DEFINE BINDINGS SECTION: ## - button1-release on each of the model-type radiobutton widgets, ## to call on a 'load_parameters_frame_XXX' proc. ##+####################################################################### bind .fRmodeltypes.fRmodeltypes1.radbuttBOX "load_parameters_frame_BOX" # bind .fRmodeltypes.fRmodeltypes1.radbuttTETRAHEDRON "load_parameters_frame_TETRAHEDRON" # bind .fRmodeltypes.fRmodeltypes1.radbuttOCTAHEDRON "load_parameters_frame_OCTAHEDRON" bind .fRmodeltypes.fRmodeltypes1.radbuttICOSAHEDRON "load_parameters_frame_ICOSAHEDRON" # bind .fRmodeltypes.fRmodeltypes2.radbuttSPHERE "load_parameters_frame_SPHERE" # bind .fRmodeltypes.fRmodeltypes2.radbuttCONE "load_parameters_frame_CONE" # bind .fRmodeltypes.fRmodeltypes2.radbuttCYLINDER "load_parameters_frame_CYLINDER" # bind .fRmodeltypes.fRmodeltypes2.radbuttTORUS "load_parameters_frame_TORUS" if {0} { bind .fRmodeltypes.fRmodeltypes1.radbuttBUCKYBALL "load_parameters_frame_BUCKYBALL" } ## END OF 'if {0/1}' SECTION ## To easily disable some bindings. ##+###################################################################### ## DEFINE PROCS SECTION: ## ##----------------------------------------------------------------------- ## For various model-types XXX, ## where XXX = BOX, TETRAHEDRON, etc. ::::: ## ## 'load_parameters_frame_for_XXX' - called by the bindings above, ## according to which model-type radio-button ## is clicked. Example name: ## 'load_parameters_frame_BOX' ##----------------------------------------------------------------------- ## ## '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 'Write' button. ## Then it calls on a 'write_model_XXX_YYY' proc ## depending on the current settings of the ## model-types and out-types radiobuttons. ## ##----------------------------------------------------------------------- ## For various model-types XXX and for various out-types YYY, ## where YYY = CADlike, OBJ, PLY, etc. ::::: ## ## 'write_model_XXX_YYY' - called when the 'Write' button is clicked. ## Writes the output file according to the ## XXX model-type radiobutton setting and the ## YYY output-type radiobutton setting. ## Example name: ## 'write_model_BOX_CADlike' ##----------------------------------------------------------------------- ## ## '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. ## ## 'popup_msg_var_scroll' - for a Help button ## ##+######################################################################## ##+######################################################################### ## proc 'load_parameters_frame_BOX' ############################################################################ ## 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 BOX model-types radiobutton ##+######################################################################### proc load_parameters_frame_BOX {} { global curPARMframe ## FOR TESTING: # puts "proc 'load_parameters_frame_BOX' > curPARMframe WAS: $curPARMframe" ## Remove the current 'fRparameters' frame (and the frames below it) ## and re-pack using the 'fRparameters_BOX' frame. pack forget $curPARMframe .fRouttypes pack .fRparameters_BOX \ -side top \ -anchor nw \ -fill none \ -expand 0 pack .fRouttypes \ -side top \ -anchor nw \ -fill x \ -expand 0 set curPARMframe ".fRparameters_BOX" .fRouttypes.radbuttCADlike configure -state normal .fRouttypes.radbuttOBJ configure -state normal .fRouttypes.radbuttPLY configure -state normal .fRouttypes.radbuttSTL configure -state normal ## FOR TESTING: # puts "proc 'load_parameters_frame_BOX' > 'pack forget' and re-pack was done." # puts "proc 'load_parameters_frame_BOX' > curPARMframe IS: $curPARMframe" } ## END OF proc 'load_parameters_frame_BOX' ##+######################################################################### ## proc 'load_parameters_frame_TETRAHEDRON' ############################################################################ ## 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 TETRAHEDRON model-types radiobutton ##+######################################################################### proc load_parameters_frame_TETRAHEDRON {} { global curPARMframe ## Remove the current 'fRparameters' frame (and the frames below it) ## and re-pack using the 'fRparameters_TETRAHEDRON' frame. pack forget $curPARMframe .fRouttypes pack .fRparameters_TETRAHEDRON \ -side top \ -anchor nw \ -fill none \ -expand 0 pack .fRouttypes \ -side top \ -anchor nw \ -fill x \ -expand 0 set curPARMframe ".fRparameters_TETRAHEDRON" .fRouttypes.radbuttCADlike configure -state disabled .fRouttypes.radbuttOBJ configure -state disabled .fRouttypes.radbuttPLY configure -state disabled .fRouttypes.radbuttSTL configure -state disabled } ## END OF proc 'load_parameters_frame_TETRAHEDRON' ##+######################################################################### ## proc 'load_parameters_frame_ICOSAHEDRON' ############################################################################ ## 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-types radiobutton ##+######################################################################### proc load_parameters_frame_ICOSAHEDRON {} { global curPARMframe ## Remove the current 'fRparameters' frame (and the frames below it) ## and re-pack using the 'fRparameters_ICOSAHEDRON' frame. pack forget $curPARMframe .fRouttypes pack .fRparameters_ICOSAHEDRON \ -side top \ -anchor nw \ -fill none \ -expand 0 pack .fRouttypes \ -side top \ -anchor nw \ -fill x \ -expand 0 set curPARMframe ".fRparameters_ICOSAHEDRON" .fRouttypes.radbuttCADlike configure -state normal .fRouttypes.radbuttOBJ configure -state disabled .fRouttypes.radbuttPLY configure -state disabled .fRouttypes.radbuttSTL configure -state disabled } ## END OF proc 'load_parameters_frame_ICOSAHEDRON' ##+######################################################################### ## proc 'load_parameters_frame_CONE' ############################################################################ ## PURPOSE: Replace the current parameters-frame by the frame with ## data entry widgets for generation of a 3D model file for a ## CONE. ## ## Also enable/disable the outtype radiobuttons according to ## whether we have an CONE-write proc for the outtype. ## ## CALLED BY: button1-release on the CONE model-types radiobutton ##+######################################################################### proc load_parameters_frame_CONE {} { global curPARMframe ## Remove the current 'fRparameters' frame (and the frames below it) ## and re-pack using the 'fRparameters_CONE' frame. pack forget $curPARMframe .fRouttypes pack .fRparameters_CONE \ -side top \ -anchor nw \ -fill none \ -expand 0 pack .fRouttypes \ -side top \ -anchor nw \ -fill x \ -expand 0 set curPARMframe ".fRparameters_CONE" .fRouttypes.radbuttCADlike configure -state disabled .fRouttypes.radbuttOBJ configure -state disabled .fRouttypes.radbuttPLY configure -state disabled .fRouttypes.radbuttSTL configure -state disabled } ## END OF proc 'load_parameters_frame_CONE' ######################################################### ## 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 # set EDITOR_text "gedit" # set EDITOR_text "/usr/bin/gedit" set EDITOR_text "$env(HOME)/apps/gscite_2.27/SciTE" # 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 the model-type and out-type radiobuttons. ## ## CALLED BY: called by the 'Write' button ##+######################################################################### proc write_model_file {} { global VARmodeltype VARouttype if {"$VARmodeltype" == "BOX" && "$VARouttype" == "CAD-like"} { write_model_BOX_CADlike return } if {"$VARmodeltype" == "BOX" && "$VARouttype" == "OBJ"} { write_model_BOX_OBJ return } if {"$VARmodeltype" == "BOX" && "$VARouttype" == "PLY"} { write_model_BOX_PLY return } if {"$VARmodeltype" == "BOX" && "$VARouttype" == "STL"} { write_model_BOX_STL return } if {"$VARmodeltype" == "TETRAHEDRON" && "$VARouttype" == "CAD-like"} { # write_model_TETRAHEDRON_CADlike return } if {"$VARmodeltype" == "ICOSAHEDRON" && "$VARouttype" == "CAD-like"} { write_model_ICOSAHEDRON_CADlike return } if {"$VARmodeltype" == "SPHERE" && "$VARouttype" == "CAD-like"} { # write_model_SPHERE_CADlike return } if {"$VARmodeltype" == "CONE" && "$VARouttype" == "CAD-like"} { # write_model_CONE_CADlike return } } ## END OF proc 'write_model_file' ##+######################################################################### ## proc 'write_model_BOX_CADlike' ############################################################################ ## PURPOSE: Write out a model file for a BOX, in ## a file with 'CADlike' data format. ## ## CALLED BY: 'write_model_file' proc ########################################################################### ## METHOD: Uses the 'puts' command to write records. ## ## Writes 'basic' data to 'PT' (point) and 'PG' (polygon) records ## to make CAD/CAE-like '.cadlike' files. ## ## NOTE: We define the 'CAD-like' file format as follows. ## The file may contain 'PT' (point), 'L ' (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, ## 'L ' denotes a 'line' record, ## 'PG' denotes a 'polygon' record. ## (We may use PL for 'polyline', in the future.) ## ## In FIELD 2 (7 characters): ## The hex RGB values for a color. Example: #ff00ff for magenta. ## ## In FIELD 3 (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 'L ' (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 ############################################################################# proc write_model_BOX_CADlike {} { global env outDIR VARwidth VARheight VARdepth set userID "$env(USER)" set OUTfilename "$outDIR/${userID}_box.cadlike" set f [open $OUTfilename w] set CNTpoints 0 set CNTpolygons 0 ## Write a heading comment record to the file. puts $f "## BOX MODEL. WIDTH: $VARwidth HEIGHT: $VARheight DEPTH: $VARdepth" ## Write 8 points for the corners of the box. ## POINT RECORD FORMAT: ## PT hexcolor point# x y z puts $f "## CAD-like file." puts $f "## Points ('PT') records follow." incr CNTpoints puts $f "PT #ffffff $CNTpoints 0.0 0.0 0.0" incr CNTpoints puts $f "PT #ffffff $CNTpoints $VARwidth 0.0 0.0" incr CNTpoints puts $f "PT #ffffff $CNTpoints $VARwidth $VARheight 0.0" incr CNTpoints puts $f "PT #ffffff $CNTpoints 0.0 $VARheight 0.0" incr CNTpoints puts $f "PT #ffffff $CNTpoints 0.0 0.0 $VARdepth" incr CNTpoints puts $f "PT #ffffff $CNTpoints $VARwidth 0.0 $VARdepth" incr CNTpoints puts $f "PT #ffffff $CNTpoints $VARwidth $VARheight $VARdepth" incr CNTpoints puts $f "PT #ffffff $CNTpoints 0.0 $VARheight $VARdepth" ## Write 6 faces (rectangles) of the box. ## POLYGON RECORD FORMAT: ## PG hexcolor polygon# numPoints pointNUM1 pointNUM2 pointNUM3 ... puts $f "##" puts $f "## Polygon ('PG') records follow." incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 4 1 4 3 2" ;# back incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 4 5 6 7 8" ;# front incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 4 6 2 3 7" ;# right incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 4 1 5 8 4" ;# left incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 4 4 8 7 3" ;# top incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 4 1 2 6 5" ;# bottom ## Close the file. close $f ## FOR TESTING: # puts "proc 'write_model_BOX_CADlike' > CNTpoints: $CNTpoints" # puts " CNTpolygons: $CNTpolygons" .fRbuttons.labelINFO configure -text "Counts for 'CAD-like' file and model BOX - Points: $CNTpoints Polygons: $CNTpolygons" ## Display the file in a GUI text-editor. edit_outfile "$OUTfilename" } ## END OF PROC 'write_model_BOX_CADlike' ##+######################################################################## ## PROC 'write_model_BOX_OBJ' ##+######################################################################## ## PURPOSE: Write out a model file for a BOX, in ## a file with 'CADlike' data format. ## ## CALLED BY: 'write_model_file' proc ########################################################################### ## METHOD: Uses the 'puts' command to write records. ## ## Writes 'basic' data to 'v' (vertex) and 'f' (face) records ## to make Wavefront '.obj' files. ## ## NOTE: 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). This 'model-writer' ## supports a subset of the file format (not the curves and surfaces), ## but it is 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. ## ## Lines other than 'v ' and 'f ' lines are not written. ## In particular, this mode-writer writes 'v ' records, but no ## 'vn' and 'vt' records. The 'basic' '.obj' records are: ## ## # some text ## Line is 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 ## ## This mode-writer simply writes 'f' records in the ## f int int int ... ## format. ###################################################################### proc write_model_BOX_OBJ {} { global env outDIR VARwidth VARdepth VARheight set userID "$env(USER)" set OUTfilename "$outDIR/${userID}_box.obj" set f [open $OUTfilename w] set CNTpoints 0 set CNTpolygons 0 ## Write a heading comment record to the file. puts $f "## BOX MODEL. WIDTH: $VARwidth HEIGHT: $VARheight DEPTH: $VARdepth" ## Write 8 points for the corners of the box. puts $f "## OBJ file." puts $f "## Vertex ('v') 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 "## Face ('f') records follow." 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_BOX_OBJ' > CNTpoints: $CNTpoints" # puts " CNTpolygons: $CNTpolygons" .fRbuttons.labelINFO configure -text "Counts for '.obj' file and model BOX - Points (vertices) : $CNTpoints Polygons (faces) : $CNTpolygons" ## Display the file in a GUI text-editor. edit_outfile "$OUTfilename" } ## END OF PROC 'write_model_BOX_OBJ' ##+######################################################################## ## PROC 'write_model_BOX_PLY' ##+######################################################################## ## PURPOSE: Write out a model file for a BOX, in ## a file with Cyberware '.ply' data format. ## ## CALLED BY: 'write_model_file' proc ########################################################################### ## METHOD: Uses the 'puts' command to write records. ## ## Writes 'basic' data to vertex and face records ## to make a Cyberware '.ply' file. ## ## NOTE: 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. ## ## 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 ## ... ## -0.688191 -0.425325 0.587785 ## -0.425325 -0.587785 0.688191 ## 3 0 42 43 ## 3 0 43 44 ## ... ## 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 ###################################################################### proc write_model_BOX_PLY {} { global env outDIR VARwidth VARdepth VARheight set userID "$env(USER)" set OUTfilename "$outDIR/${userID}_box.ply" set f [open $OUTfilename w] set CNTpoints 0 set CNTpolygons 0 ## Write the first eleven header records. puts $f "ply" puts $f "format ascii 1.0" puts $f "element vertex **SEE-THE-VERTEX-COUNT-AT-BOTTOM-OF-FILE**" puts $f "property float x" puts $f "property float y" puts $f "property float z" puts $f "element face **SEE-THE-POLYGON-COUNT-AT-BOTTOM-OF-FILE**" 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 "## 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 face records. # puts $f "##" # puts $f "## Face records follow." ## For faces, 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 incr CNTpolygons puts $f "3 1 3 2" incr CNTpolygons puts $f "3 1 4 3" incr CNTpolygons puts $f "3 5 6 7" incr CNTpolygons puts $f "3 5 7 8" incr CNTpolygons puts $f "3 6 2 3" incr CNTpolygons puts $f "3 6 3 7" incr CNTpolygons puts $f "3 5 8 4" incr CNTpolygons puts $f "3 5 4 1" incr CNTpolygons puts $f "3 4 8 7" incr CNTpolygons puts $f "3 4 7 3" incr CNTpolygons puts $f "3 1 2 6" incr CNTpolygons puts $f "3 1 6 5" ## 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_BOX_PLY' > CNTpoints: $CNTpoints" # puts " CNTpolygons: $CNTpolygons" .fRbuttons.labelINFO configure -text "Counts for '.ply' file and model BOX - Points (vertices) : $CNTpoints Polygons (faces) : $CNTpolygons" ## Display the file in a GUI text-editor. edit_outfile "$OUTfilename" } ## END OF PROC 'write_model_BOX_PLY' ##+######################################################################## ## PROC 'write_model_BOX_STL' ##+######################################################################## ## PURPOSE: Write out a model file for a BOX --- a file ## with 'ASCII STL' (stereolithography) data format. ## ## NOTE: This proc does 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, this 'writer' just writes 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. ## ## CALLED BY: 'write_model_file' proc ########################################################################### ## METHOD: Uses the Tcl 'puts' command to write records. ## 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 square side is split into 2 triangles. ## ## This model-file-writer currently puts 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. ########################################################################### proc write_model_BOX_STL {} { global env outDIR VARwidth VARdepth VARheight set userID "$env(USER)" set OUTfilename "$outDIR/${userID}_box.stl" set f [open $OUTfilename w] set CNTpoints 0 set CNTpolygons 0 ## Write the top record of the STL file puts $f "solid BOX 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 Facets (triangles) : $CNTpolygons" ## Close the file. close $f ## FOR TESTING: # puts "proc 'write_model_BOX_STL' > CNTpoints: $CNTpoints" # puts " CNTpolygons: $CNTpolygons" .fRbuttons.labelINFO configure -text "Counts for '.stl' file and model BOX - Polygons (facets, triangles) : $CNTpolygons" ## Display the file in a GUI text-editor. edit_outfile "$OUTfilename" } ## END OF PROC 'write_model_BOX_STL' ##+######################################################################### ## proc 'write_model_ICOSAHEDRON_CADlike' ############################################################################ ## PURPOSE: Write out a model file for an ICOSAHEDRON (12 vertices, ## 20 triangular faces) --- a file with 'CADlike' data format. ## ## CALLED BY: 'write_model_file' proc ########################################################################### ## METHOD: Uses the 'puts' command to write records. ## See the proc 'write_model_BOX_CADlike' above for ## a description of the format of the 'CAD-like' records --- ## 'PT' (point) and 'PG' (polygon) records. ## ############################################################################# proc write_model_ICOSAHEDRON_CADlike {} { global env outDIR VARx VARz set userID "$env(USER)" set OUTfilename "$outDIR/${userID}_icosahedron.cadlike" set f [open $OUTfilename w] set CNTpoints 0 set CNTpolygons 0 ## Write a heading comment record to the file. puts $f "## ICOSAHEDRON MODEL. X: $VARx Z: $VARz" ## Write 12 points for the vertices of the icosahedron. ## POINT RECORD FORMAT: ## PT hexcolor point# x y z puts $f "## CAD-like file." puts $f "## Points ('PT') records follow." set VARminusx [expr {-$VARx}] set VARminusz [expr {-$VARz}] incr CNTpoints puts $f "PT #ffffff $CNTpoints $VARminusx 0.0 $VARz" incr CNTpoints puts $f "PT #ffffff $CNTpoints $VARx 0.0 $VARz" incr CNTpoints puts $f "PT #ffffff $CNTpoints $VARminusx 0.0 $VARminusz" incr CNTpoints puts $f "PT #ffffff $CNTpoints $VARx 0.0 $VARminusz" incr CNTpoints puts $f "PT #ffffff $CNTpoints 0.0 $VARz $VARx" incr CNTpoints puts $f "PT #ffffff $CNTpoints 0.0 $VARz $VARminusx" incr CNTpoints puts $f "PT #ffffff $CNTpoints 0.0 $VARminusz $VARx" incr CNTpoints puts $f "PT #ffffff $CNTpoints 0.0 $VARminusz $VARminusx" incr CNTpoints puts $f "PT #ffffff $CNTpoints $VARz $VARx 0.0" incr CNTpoints puts $f "PT #ffffff $CNTpoints $VARminusz $VARx 0.0" incr CNTpoints puts $f "PT #ffffff $CNTpoints $VARz $VARminusx 0.0" incr CNTpoints puts $f "PT #ffffff $CNTpoints $VARminusz $VARminusx 0.0" ## Write 20 faces (triangles) of the icosahedron. ## POLYGON RECORD FORMAT: ## PG hexcolor polygon# numPoints pointNUM1 pointNUM2 pointNUM3 ... puts $f "##" puts $f "## Polygon ('PG') records follow." if {0} { ## NOTE: These indexes of the points are based at zero, not one. incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 0 4 1" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 0 9 4" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 9 5 4" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 4 5 8" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 4 8 1" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 8 10 1" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 8 3 10" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 5 3 8" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 5 2 3" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 2 7 3" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 7 10 3" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 7 6 10" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 7 11 6" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 11 0 6" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 0 1 6" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 6 1 10" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 9 0 11" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 9 11 2" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 9 2 5" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 7 2 11" } ## END OF if {0/1} if {1} { ## NOTE: These indexes of the points are based at one, not zero. incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 1 5 2" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 1 10 5" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 10 6 5" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 5 6 9" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 5 9 2" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 9 11 2" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 9 4 11" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 6 4 9" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 6 3 4" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 3 8 4" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 8 11 4" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 8 7 11" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 8 12 7" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 12 1 7" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 1 2 7" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 7 2 11" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 10 1 12" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 10 12 3" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 10 3 6" incr CNTpolygons puts $f "PG #ffffff $CNTpolygons 3 8 3 12" } ## END OF if {0/1} ## Close the file. close $f ## FOR TESTING: # puts "proc 'write_model_ICOSAHEDRON_CADlike' > CNTpoints: $CNTpoints" # puts " CNTpolygons: $CNTpolygons" .fRbuttons.labelINFO configure -text "Counts for 'CAD-like' file and model ICOSAHEDRON - Points: $CNTpoints Polygons: $CNTpolygons" ## Display the file in a GUI text-editor. edit_outfile "$OUTfilename" } ## END OF PROC 'write_model_ICOSAHEDRON_CADlike' ##+##################################################################### ## proc 'randomColor' ##+##################################################################### ## PURPOSE: To generate random hexcolor codes for use in writing ## models and applying colors to points and faces, randomly. ## ## CALLED BY: The 'write_model_XXX_YYY' procs, dependent upon a VAR ## set by a checkbutton widget on the GUI. ## ## REFERENCE: http://wiki.tcl.tk/1424 - 'colors' ######################################################################## ## Alternatively, modify the following to get colors spread out ## about 16 units apart and no dark colors (total RGB values above 300, say). ## set r [expr { int(256 * rand()) }] ## set g [expr { int(256 * rand()) }] ## set b [expr { int(256 * rand()) }] ## ## set newColor [format "#%02x%02x%02x" $r $g $b] ####################################################################### proc randomColor {} { format #%06x [expr {int(rand() * 0xFFFFFF)}] } ## END OF PROC 'randomColor' ##+######################################################################## ## 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-Generator Utility ** When the 3D-Model-Generator GUI comes up, the user can specify 3 types of items: 1) a MODEL TYPE (box, tetrahedron, octahedron, ocosahedron, ..., sphere, cone, cylinder, torus, ...) --- to be generated 2) some PARAMETERS associated with that model type (such as width, height, depth, radius, ...) 3) an OUTPUT FILE TYPE ('CAD-like', OBJ, PLY, STL, ...). 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 line in the 'edit_outfile' proc in this script $argv0 to specify the GUI editor to use.) The output file can be put in a temporary-file directory, such as /tmp on Linux/Unix. 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. (The user can edit this script and change the variable 'outDIR' at the bottom of this script to specify an output directory.) --- Over time, some additonal 'model-types' and 'output-types' may be added to this utility. Within the script, this means that additional 'write_model_XXX_YYY' procs and 'load_parameters_frame_XXX' procs may be added --- for various model-types XXX and various output-types YYY. Adding a model type XXX also means that a new '.fRparameters_XXX' frame needs to be added, with appropriate widgets defined in the frame --- such as entry-field widgets --- in which the user can enter parameters for creation of the XXX model type. " ##+##################################################### ## Additional GUI initialization, if needed (or wanted). ##+##################################################### ## FOR THIS 3D-MODEL-GENERATOR-UTILITY: ## Set a default model-type XXX and output-file-type YYY ## (i.e. set the variables for those two radiobutton types). ## Then replace the current 'fRparmeters' frame by the ## appropriate parameters frame '.fRparameters_XXX' --- ## by calling the proc 'load_parameters_frame_XXX'. ## ## For the specified model-type XXX, the widgets of the ## frame '.fRparameters_XXX' should already be defined ## above. Then, the 'load_parameters_frame_XXX' proc ## simply has to pack frame '.fRparameters_XXX' and ## the widgets will suddenly, magically appear. set VARmodeltype "BOX" set VARouttype "CAD-like" set outDIR "/tmp" set curPARMframe ".fRparameters" load_parameters_frame_BOX ====== 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. As 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. 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) at about 30 microseconds per model file, instead of over 30 minutes per model file. :-) :-) :-) :-) :-) ___ As I work on some 3D model and 3D surface viewing utilities in coming months, I may add several 'writers' 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. And, 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 point and/or the polygon records in the 'CAD-like' output format. This would help me test out 3D model viewers when I add features for smooth color shading over polygons. And intially, I plan to support solid colors in the polygons --- perhaps with shading depending on the angle that the polygons make with a direction vector from a light source. Thank you, Tcl-Tk developers, for making it possible for me to make nice quality images of geometry in 3-space.