#!/usr/bin/wish -f ##+######################################################################## ## ## SCRIPT: tkPtolemysTheorem_QuadrangleInCircle.tk ## ## PURPOSE: This Tk GUI script is meant to demonstrate Ptolemy's Theorem ## for a quadrangle inscribed in a circle: ## ## The product of the two diagonals of a quadrangle inscribed ## in a circle is equal to the sum of the products of the ## opposite sides. ## ## In other words: ## ## Let the quadrangle have vertex points A,B,C,D. ## ## Let AC be one diagonal across the quadrangle and let BD ## be the other diagonal. ## ## Let AB and CD be opposite sides and BC and DA be opposite sides. ## ## Ptolemy showed that the lengths satisfy the equality ## ## AC * BD = (AB * CD) + (BC * DA) ## ## This GUI script is meant to 'dynamically' demonstrate the ## equality by drawing a circle on a Tk 'canvas' on the GUI and ## allowing the user to place the 4 vertices --- A,B,C,D --- ## around the circumference of the circle. ## ## After the user moves one or more of the vertices of the ## quadrangle, the six lengths --- AC, BD, AB, CD, BC, DA ## --- are recalculated and the left- and right-hand sides of ## the above equation are recalculated and shown to be equal. ## ############# ## REFERENCES: ## There is a nice write-up of a proof of Ptolemy's theorem ## in the last half of Chapter 6 of the book 'Trigonometric ## Delights' by Eli Maor. Maor points out that several other truths ## of plane geometry and plane trigonometry are quick deductions ## from Ptolemy's theorem --- including the Pythagorean Theorem ## and the sine sum and difference formulas. ## ######################## ## HOW PTOLEMY USED THIS: ## ## The book 'Great Calculations' by Colin Pask points out that ## Ptolemy used this relationship to make a table of chord lengths ## for angles between 0 and 180 degrees. ## ## Ptolemy used the relationship (the equality) to ## a) Get the chord length subtending half an angle, given the chord ## length subtending the original angle. ## b) Get the chord length subtending the difference of 2 angles, ## given the chord lengths subtending the 2 angles. ## ## Those two capabilities allowed Ptolemy to make a table of ## chord lengths of at least 180 equally-spaced angles between ## 0 and 180 degrees. ## ## Tables of chord lengths were the precursors of 'sin' tables. ## ############### ## GUI FEATURES: ## This GUI uses a Tk 'canvas' widget to show the circle and ## the quadrangle --- and its 2 diagonals. ## ## We fix point A on the right side of the circle and let ## the user position B,C,D around the circle. ## ## Three 'scale' widgets on the GUI allow the user to easily specify ## the locations of vertices B,C,D around the cirumference of ## the circle --- in terms of percents of 'the remaining arc'. ## ## In particular, ## - B is located as a percent of the 360 degree arc, ## counter-clockwise from point A back to point A. ## - C is located as a percent of the arc between point B ## and point A, going counter-clockwise from point B. ## - D is located as a percent of the arc between point C ## and point A, going counter-clockwise from point C. ## ## We use a square drawing area (Tk 'canvas' widget), and we allow ## the user to specify the size (in pixels) of that drawing area --- ## via a 'scale' widget on the GUI. ## ################# ## DRAWING METHOD: ## ## The circle is drawn on the canvas with a 'create oval' command. ## The line segments of the quadrangle (and its diagonals) are drawn ## with 'create line' commands on the canvas. ## ## After the user positions one or more of the several scale widgets ## as desired (and after choosing colors with the several color buttons, ## and after choosing an image size in pixels via a scale widget). ## the user clicks a 'ReDraw' button on the GUI. ## ## A 'redraw' proc may then ## - clear the canvas ## - redraw the circle ## - redraw the quadrangle ## - redraw the 2 diagonals ## and ## - the left-hand and right-hand sides of the expression above ## are computed (from the lengths of the six line segments) ## to demonstrate that the equality 'holds'. ## ## Since the drawing of the several geometric elements and the ## length computations go so quickly (in a fraction of a second), ## we may not try to optimize the redrawing by drawing only the ## components affected by various combinations of ## point-position/color/image-size change. ## ## A 'Help' button on the GUI describes how the GUI operates ## and how the computations are performed --- as well as various ## facts about Ptolemy and the equality relationship that has ## been named Ptolemy's Theorem. ## ##+######################### ## PLANNED LAYOUT OF THE GUI: ## ## FrameNames ## VVVVVVVVVV ## ----------------------------------------------------------------------------- ## tkPtolemysTheorem --- for a quadrangle inscribed in a circle ## [window title] ## ----------------------------------------------------------------------------- ## ## .fRbuttons {Exit} {Help} {ReDraw} {Reset {Backgd {Circle {Straight Line {Text Point A is at ## Parms} Color} Color} Segments Color} Color} zero degrees. ## ## .fRmsg [ .......... Message line --- for advice to the user .................. ] ## ## 0.0 100.0 ## .fRpointB Locate B (% of arc A to A): <--------O---------> B degrees: [number here] ## ## 0.0 100.0 ## .fRpointC Locate C (% of arc B to A): <--------O---------> C degrees: [number here] ## ## 0.0 100.0 ## .fRpointD Locate D (% of arc C to A): <--------O---------> D degrees: [number here] ## ## Image width and 50 4000 {WriteImg ## .fRimgsize height (pixels): <----------O---------> File ->} O PS O PDF O GIF O PNG ## ## .fRresult [ .. Calculated Left-Hand and Right-Hand sides of the equation are shown here .. ] ## ## .fRcanvas ----------------------------------------------------------------------------- ## | A ## | | ## | | ## | 'Canvas' for displaying the circle and inscribed quadrangle | ## | in a square image area. (with scrollbars) | ## | | ## | | ## | | ## | V ## <---------------------------------------------------------------------------> ## ## SKETCH CONVENTIONS for this GUI sketch: ## ## SQUARE-BRACKETS indicate a comment (not to be placed on the GUI). ## BRACES indicate a Tk 'button' widget. ## A COLON indicates that the text before the colon is on a 'label' widget. ## <---O---> indicates a (horzontal) Tk 'scale' widget. ## CAPITAL-X indicates a Tk 'checkbutton' widget (if any). ## CAPITAL-O indicates a Tk 'radiobutton' widget (if any). ## UNDERSCORES indicate a Tk 'entry' widget (if any). ## ## A LINE (HYPHENS or VERTICAL-BARS) WITH AN 'ARROW-HEAD' AT EACH END indicates ## a Tk 'scale' widget. ## ## A combination of VERTICAL-BAR CHARACTERS AND HYPHEN (or UNDERSCORE) CHARACTERS, ## that outline a RECTANGULAR SHAPE, are used to indicate either a Tk 'canvas' or ## a Tk 'listbox' widget or a Tk 'text' widget. ## ## SCROLL-BAR 'ARROW-HEADS' (for a 'canvas', 'listbox', or 'text' Tk widget) ## are drawn as follows: ## ## UP ARROW-HEAD is drawn with a CAPITAL-A. ## DOWN ARROW-HEAD is drawn with a CAPITAL-V. ## LEFT ARROW-HEAD is drawn with a LESS-THAN sign. ## RIGHT ARROW-HEAD is drawn with a GREATER-THAN sign. ## ## UP-and-DOWN ARROW-HEADS at the right/left of the box shape indicate ## a VERTICAL SCROLL-BAR there. ## ## LEFT-and-RIGHT ARROW-HEADS at the bottom/top of the box shape indicate ## a HORIZONTAL SCROLL-BAR there. ## ## The arrow-heads on a horizontal scrollbar are joined by hyphens, rather than ## underscores. ## ##+################## ## GUI WIDGET SUMMARY: ## ## This GUI will contain about: ## ## 9 'button' widgets ## 6 'label' widgets ## 4 'scale' widgets ## 1 'canvas' widget (with x-y scrollbars) ## 4 'radiobutton' widgets (in one group) ## ## 0 'checkbutton' widgets ## 0 'entry' widgets ## 0 'listbox' widgets ## 0 'text' widgets ## ##+################################## ## METHOD USED to perform the drawing: ## ## A square image area (canvas) is defined in the canvas frame. ## ## A set of drawing procs are used apply 'world coordinates' to ## the pixel coordinates of the Tk canvas. ## ## Two procs -- 'Xwc2px' and 'Ywc2px' -- are used to convert world ## coordinates to pixel coordinates that are to be used in ## 'create oval' and 'create line' commands --- to draw the ## circle and the quadrangle, respectively. ## ## The center of the circle is defined in the center of ## the image area at (0.0,0.0) in 'world coordinates'. ## ## We draw a circle of unit radius, R = 1.0. ## The world coordinates of the four corners of the ## square image area will be ## - top left (-H,+H) ## - top right (+H,+H) ## - bottom left (-H,-H) ## - bottom right (+H,-H) ## where H is somewhat bigger than R, such as H = 1.2 * R = 1.2. ## The factor 1.2 is a 'margin factor'. ## ## The world coordinates of the vertices of the inscribed quadrangle ## are given by sin() and cos() functions. ## ## Some text items might be put on the canvas, such as labels ## for points or values of angles (or x,y coordinates). ## ##+####################### ## CAPTURING THE GUI IMAGE: ## ## A screen/window capture utility (like 'gnome-screenshot' ## on Linux) can be used to capture the GUI image in a PNG ## or GIF file, say. ## ## If necessary, an image editor (like 'mtpaint' on Linux) ## can be used to crop the window capture image. The image ## could also be down-sized --- say to make a smaller image ## suitable for use in a web page or an email. ## ## A 'WritePSfile' button could be placed on the GUI ## to write a (color) Postscript file from the canvas image. ## ##+####################################################################### ## 'CANONICAL' STRUCTURE OF THIS CODE: ## ## 0) Set general window parms (win-name, win-position, win-color-scheme, ## fonts, widget-geom-parms, win-size-control, text-array-for-labels-etc). ## ## 1a) Define ALL frames (and sub-frames, if any). ## 1b) Pack the frames. ## ## 2) Define & pack all widgets in the frames, frame by frame. ## After all the widgets for a frame are defined, pack them in the frame. ## ## 3) Define keyboard or mouse/touchpad/touch-sensitive-screen 'event' ## BINDINGS, if needed. ## ## 4) Define PROCS, if needed. ## ## 5) Additional GUI INITIALIZATION (typically with one or more of ## the procs), if needed. ## ##+################################# ## Some detail of the code structure of this particular script: ## ## 1a) Define ALL frames: ## ## Top-level : ## '.fRbuttons' - to contain 'Exit', 'Help', 'Redraw', 'Reset' and ## several color buttons. ## ## '.fRmsg' - to contain a label widget ## ## '.fRpointB' - to contain a label and a scale widget ## ## '.fRpointC' - to contain a label and a scale widget ## ## '.fRpointD' - to contain a label and a scale widget ## ## '.fRimgsize' - to contain a label and a scale widget --- ## and a button and 4 radiobuttons ## ## '.fRresult' - to contain a label widget ## ## '.fRcanvas' - to contain a canvas widget, which will display the ## circle and inscribed quadrangle, with diagonals. ## ## 1b) Pack ALL frames. ## ## 2) Define & pack all widgets in the frames -- basically going through ## frames & their interiors in left-to-right, or top-to-bottom order. ## ## 3) Define BINDINGS: such as button1-release on the 'scale' widgets ## (for details on bindings, see the BINDINGS section below) ## ## 4) Define PROCS: ## ## ************* ## Drawing procs: ## ************* ## ## 'redraw' - This proc is used to erase and redraw the ## circle and quadrangle components on the canvas. ## This proc calls the following 'draw_circle' and ## 'draw_quadrilateral' procs. ## ## This proc is called by the 'Redraw' button and ## in the 'Additional GUI Initialization' section ## at the bottom of this script to put an initial ## drawing on the canvas. ## ## 'draw_circle' - Given radius R=1 in 'world coordinates', ## this proc draws a circle on the canvas. ## ## 'draw_quadrangle' - Given percents for B,C,D, this proc draws an ## inscribed quadrangle on the canvas, with diagonals. ## ## ***************************************** ## World-Coordinates-to-Pixels utility procs: ## ***************************************** ## ## 'setMappingVars_for_px2wc' - can be called in drawing proc(s) below to set ## up parameters to be used in converting between ## 'world coordinates' and 'pixel coordinates' ## on the Tk canvas. ## ## 'set_MappingVars_forImageSquare' - In this app/script, we use a square drawing area. ## This proc uses the 'setMappingVars_for_px2wc' ## proc to set up the conversion parameters ## to use in this square drawing area. ## ## 'Xwc2px' - To convert an x world-coordinate to pixels, ## to be used in the following 'draw_*' procs. ## ## 'Ywc2px' - To convert a y world-coordinate to pixels, ## to be used in the following 'draw_*' procs. ## ## ******************* ## Other utility procs: ## ******************* ## ## 'reset_parms' - Sets (initial) values for the scale widgets. ## ## 'set_background_color' - Sets the color for the canvas (background). ## ## 'set_circle_color' - Sets the color for the circle on the canvas. ## ## 'set_line_color' - Sets the color for the line-segments of the quadrangle ## and its diagonals. ## ## 'update_button_colors' - Sets the color of the several color buttons. ## ## 'write_img_file' - Writes an image file (PS or PDF or GIF or PNG) ## ## 'popup_msgVarWithScroll' - called by 'Help' button to show HELPtext var. ## ## (for more details on these procs, see the PROCS section below) ## ## ## 5) Additional GUI Initialization: performs operations such as ## - call proc 'reset_parms' to initialize the scale widgets ## - call proc 'redraw' to put an initial drawing on the canvas, ## for given initial settings of the Tk scale widgets. ## ##+####################################################################### ## 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 ## but this script should work in most previous 8.x versions, and probably ## even in some 7.x versions (if font handling is made 'old-style'). ##+####################################################################### ## MAINTENANCE HISTORY: ## Started by: Blaise Montandon 2016sep11 Started coding the GUI. ## Changed by: Blaise Montandon 2016dec24 Changed 3 scales to use percents ## rather than angles. ## Added a 'ResetParms' button and ## 'reset_parms' proc. ## Added a 'TextColor' button. ## Changed by: Blaise Montandon 2016dec30 Added 'WriteImgFile' button. ## Changed by: Blaise Montandon 2017Jan09 Added 3 labels on which to show ## Bdegrees, Cdegrees, Ddegrees. ##+######################################################################## ##+###################################################### ## Set WINDOW TITLE and POSITION. ##+###################################################### wm title . "tkPtolemyTheorem - for a quadrangle inscribed in a circle" wm iconname . "Ptolemy" wm geometry . +8+30 ##+###################################################### ## Set the COLOR SCHEME for the window and its widgets --- ## such as listbox and entry field background color. ##+###################################################### tk_setPalette "#f0f0f0" set scaleBKGD "#f0f0f0" set radbuttBKGD "#c0c0c0" # set chkbuttBKGD "#c0c0c0" # set entryBKGD "#ffffff" # set listboxBKGD "#ffffff" ##+######################################################## ## DEFINE (temporary) FONT NAMES. ## ## We use a VARIABLE-WIDTH font for text on LABEL and ## BUTTON widgets. ## ## We use a FIXED-WIDTH font for LISTBOX lists, ## for Help-text in a TEXT widget, and for ## the text in ENTRY fields, if any. ##+######################################################## font create fontTEMP_varwidth \ -family {comic sans ms} \ -size -14 \ -weight bold \ -slant roman font create fontTEMP_SMALL_varwidth \ -family {comic sans ms} \ -size -12 \ -weight bold \ -slant roman ## Some other possible (similar) variable width fonts: ## Arial ## Bitstream Vera Sans ## DejaVu Sans ## Droid Sans ## FreeSans ## Liberation Sans ## Nimbus Sans L ## Trebuchet MS ## Verdana font create fontTEMP_fixedwidth \ -family {liberation mono} \ -size -14 \ -weight bold \ -slant roman font create fontTEMP_SMALL_fixedwidth \ -family {liberation mono} \ -size -12 \ -weight bold \ -slant roman ## Some other possible fixed width fonts (esp. on Linux): ## Andale Mono ## Bitstream Vera Sans Mono ## Courier 10 Pitch ## DejaVu Sans Mono ## Droid Sans Mono ## FreeMono ## Nimbus Mono L ## TlwgMono ##+########################################################### ## SET GEOM VARS FOR THE VARIOUS WIDGET DEFINITIONS. ## (e.g. width and height of canvas, and padding for Buttons) ##+########################################################### ## LABEL widget geom settings: set PADXpx_label 0 set PADYpx_label 0 set BDwidthPx_label 2 set RELIEF_label_lo "flat" ## BUTTON widget geom settings: set PADXpx_button 0 set PADYpx_button 0 set BDwidthPx_button 2 ## We generally default to relief "raised" for all 'button' widgets. ## BUT, in case you want to experiment: set RELIEF_button "raised" ## SCALE widget geom parameters: set BDwidthPx_scale 2 set scaleThicknessPx 10 set scaleLengthPx 400 ## square CANVAS widget geom settings: set initCanWidthPx 300 set initCanHeightPx $initCanWidthPx # set BDwidthPx_canvas 2 set BDwidthPx_canvas 0 set RELIEF_canvas "flat" ## RADIOBUTTON geom parameters: set PADXpx_radbutt 0 set PADYpx_radbutt 0 set BDwidthPx_radbutt 1 set RELIEF_radbutt_hi "raised" ## COMMENT some geom parameters, for now. if {0} { ## CHECKBUTTON geom parameters: set PADXpx_chkbutt 0 set PADYpx_chkbutt 0 set BDwidthPx_chkbutt 1 set RELIEF_chkbutt_hi "raised" ## ENTRY widget geom settings: # set BDwidthPx_entry 2 } ## COMMENTED geom parameters. ##+############################################################## ## 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 the '.fRbuttons' frame: set aRtext(buttonEXIT) "Exit" set aRtext(buttonHELP) "Help" set aRtext(buttonREDRAW) "ReDraw" set aRtext(buttonRESET) "Reset Parms" set aRtext(buttonCOLORbkgd) "Backgrnd Color" set aRtext(buttonCOLORcircle) "Circle Color" set aRtext(buttonCOLORline) "Straight Line Segments Color" set aRtext(buttonCOLORtext) "Text Color" set aRtext(labelPointA) " Point A is at zero degrees." ## For the '.fRpointB' frame: set aRtext(labelPointB) "Locate B (% of 360 degree arc, A to A):" ## For the '.fRpointC' frame: set aRtext(labelPointC) "Locate C (% of arc from B to A):" ## For the '.fRpointD' frame: set aRtext(labelPointD) "Locate D (% of arc from C to A):" ## For the '.fRimgsize' frame: set aRtext(labelIMGSIZE) "Image width & height (pixels):" set aRtext(buttonWRITEIMG) "Write Img File ->" set aRtext(radbuttPS) "PS" set aRtext(radbuttPDF) "PDF" set aRtext(radbuttGIF) "GIF" set aRtext(radbuttPNG) "PNG" ## For messages in the '.fRmsg' frame: set aRtext(msgCHGVERTEX) \ "** Change a vertex location --- B,C, or D --- to see if the Ptolemy equality 'holds'. **" # "** Change the locations of B, C, and/or D to show the equation still holds. **" set aRtext(msgREDRAW) "** Click on the 'Redraw' button to update the image. **" ## END OF if { "$VARlocale" == "en"} ##+###################################################################### ## Set a MIN-SIZE of the window (roughly). ## ## For WIDTH, allow for the min-width of the '.fRbuttons' frame. ## ## For HEIGHT, allow for the stacked frames: ## 2 chars high for the '.fRbuttons' frame, ## 1 char high for the '.fRmsg' frame, ## 2 chars high for the '.fRpointB' frame, ## 2 chars high for the '.fRpointC' frame, ## 2 chars high for the '.fRpointD' frame, ## 2 chars high for the '.fRimgsize' frame, ## 1 char high for the '.fRresult' frame, ## at least 50 pixels high for the '.fRcanvas' frame. ##+##################################################################### ## FOR WIDTH: set minWidthPx [font measure fontTEMP_varwidth \ " $aRtext(buttonEXIT) $aRtext(buttonHELP) $aRtext(buttonREDRAW) $aRtext(buttonRESET) \ Color Color Color Color"] ## We add some pixels to account for right-left-size of ## window-manager decoration (~8 pixels) and some pixels for ## frame/widget borders (6 widgets x 4 pixels/widget = 24 pixels). set minWinWidthPx [expr {32 + $minWidthPx}] ## For HEIGHT --- for ## 2 chars high for the '.fRbuttons' frame, ## 1 char high for the '.fRmsg' frame, ## 2 chars high for the '.fRpointB' frame, ## 2 chars high for the '.fRpointC' frame, ## 2 chars high for the '.fRpointD' frame, ## 2 chars high for the '.fRimgsize' frame, ## 1 char high for the '.fRresult' frame, ## 50 pixels high for the '.fRcanvas' frame. set charHeightPx [font metrics fontTEMP_varwidth -linespace] set minWinHeightPx [expr {12 * $charHeightPx}] ## Add about 50 pixels for height of the canvas ## AND add about 20 pixels for top-bottom window decoration -- ## and some pixels for top-and-bottom of frame/widget borders ## (8 frames/widgets x 4 pixels/widget = 32 pixels). set minWinHeightPx [expr {102 + $minWinHeightPx}] ## FOR TESTING: # puts "minWinWidthPx = $minWinWidthPx" # puts "minWinHeightPx = $minWinHeightPx" wm minsize . $minWinWidthPx $minWinHeightPx ## If you want to make the window un-resizable, ## you can use the following statement. # wm resizable . 0 0 ##+################################################################ ## DEFINE *ALL* THE FRAMES: ## ## Top-level : '.fRbuttons' '.fRmsg' ## '.fRpointB' '.fRpointC' '.fRpointD' ## '.fRimgsize' '.fRresult' '.fRcanvas' ## ## Sub-frames: none ##+################################################################ ## FOR TESTING change 0 to 1: ## (Example1: To see appearance of frames when borders are drawn.) ## (Example2: To see sizes of frames for various '-fill' options.) ## (Example3: To see how frames expand as window is resized.) if {0} { set RELIEF_frame raised set BDwidthPx_frame 2 } else { set RELIEF_frame flat set BDwidthPx_frame 0 } frame .fRbuttons -relief $RELIEF_frame -bd $BDwidthPx_frame # frame .fRmsg -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRmsg -relief raised -bd 2 frame .fRpointB -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRpointC -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRpointD -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRimgsize -relief $RELIEF_frame -bd $BDwidthPx_frame # frame .fRresult -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRresult -relief raised -bd 2 # frame .fRcanvas -relief $RELIEF_frame -bd $BDwidthPx_frame frame .fRcanvas -relief raised -bd 2 ##+############################## ## PACK the FRAMES. ##+############################## pack .fRbuttons \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRmsg \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRpointB \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRpointC \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRpointD \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRimgsize \ -side top \ -anchor nw \ -fill x \ -expand 0 pack .fRresult \ -side top \ -anchor nw \ -fill both \ -expand 1 pack .fRcanvas \ -side top \ -anchor n \ -fill both \ -expand 1 ##+########################################################## ## The FRAMES ARE PACKED. START PACKING WIDGETS IN THE FRAMES. ##+########################################################## ##+########################################################## ## In FRAME '.fRbuttons' - ## DEFINE-and-PACK 'BUTTON' WIDGETS ## --- Exit, Help, Redraw, Reset --- and several color buttons. ##+########################################################## 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_msgVarWithScroll .topHelp "$HELPtext" +150+50} button .fRbuttons.buttREDRAW \ -text "$aRtext(buttonREDRAW)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {redraw} button .fRbuttons.buttRESET \ -text "$aRtext(buttonRESET)" \ -font fontTEMP_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {reset_parms} button .fRbuttons.buttCOLORbkgd \ -text "$aRtext(buttonCOLORbkgd)" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {set_background_color} button .fRbuttons.buttCOLORcircle \ -text "$aRtext(buttonCOLORcircle)" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {set_circle_color} button .fRbuttons.buttCOLORline \ -text "$aRtext(buttonCOLORline)" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {set_line_color} button .fRbuttons.buttCOLORtext \ -text "$aRtext(buttonCOLORtext)" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {set_text_color} label .fRbuttons.labelPointA \ -text "$aRtext(labelPointA)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## Pack the widgets in frame '.fRbuttons'. pack .fRbuttons.buttEXIT \ .fRbuttons.buttHELP \ .fRbuttons.buttREDRAW \ .fRbuttons.buttRESET \ .fRbuttons.buttCOLORbkgd \ .fRbuttons.buttCOLORcircle \ .fRbuttons.buttCOLORline \ .fRbuttons.buttCOLORtext \ .fRbuttons.labelPointA \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## In FRAME '.fRmsg' - ## DEFINE one LABEL widget. Then PACK it. ##+####################################################### label .fRmsg.labelMSG \ -text "" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bg "#ff9999" \ -bd $BDwidthPx_label ## Pack the widgets in frame '.fRmsg'. pack .fRmsg.labelMSG \ -side top \ -anchor nw \ -fill both \ -expand 1 ##+########################################################## ## In FRAME '.fRpointB' - ## DEFINE 1 pair of LABEL and SCALE widgets. ## Then PACK THEM. ##+########################################################## label .fRpointB.labelPointB \ -text "$aRtext(labelPointB)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## Set this widget var in the GUI initialization section ## at the bottom of this script. # set VARpercentB 40.0 scale .fRpointB.scalePointB \ -from 0.0 -to 100.0 \ -resolution 0.01 \ -font fontTEMP_SMALL_varwidth \ -variable VARpercentB \ -showvalue true \ -orient horizontal \ -bd $BDwidthPx_scale \ -length $scaleLengthPx \ -width $scaleThicknessPx \ -command {show_Bdegrees} label .fRpointB.labelBdegrees \ -text "" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## PACK the widgets in the '.fRpointB' frame. pack .fRpointB.labelPointB \ .fRpointB.scalePointB \ .fRpointB.labelBdegrees \ -side left \ -anchor w \ -fill none \ -expand 0 ##+########################################################## ## In FRAME '.fRpointC' - ## DEFINE 1 pair of LABEL and SCALE widgets. ## Then PACK THEM. ##+########################################################## label .fRpointC.labelPointC \ -text "$aRtext(labelPointC)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## Set this widget var in the GUI initialization section ## at the bottom of this script. # set VARpercentC 50.0 scale .fRpointC.scalePointC \ -from 0.0 -to 100.0 \ -resolution 0.01 \ -font fontTEMP_SMALL_varwidth \ -variable VARpercentC \ -showvalue true \ -orient horizontal \ -bd $BDwidthPx_scale \ -length $scaleLengthPx \ -width $scaleThicknessPx \ -command {show_Cdegrees} label .fRpointC.labelCdegrees \ -text "" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## PACK the widgets in the '.fRpointC' frame. pack .fRpointC.labelPointC \ .fRpointC.scalePointC \ .fRpointC.labelCdegrees \ -side left \ -anchor w \ -fill none \ -expand 0 ##+########################################################## ## In FRAME '.fRpointD' - ## DEFINE 1 pair of LABEL and SCALE widgets. ## Then PACK THEM. ##+########################################################## label .fRpointD.labelPointD \ -text "$aRtext(labelPointD)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## Set this widget var in the GUI initialization section ## at the bottom of this script. # set VARpercentD 10.0 scale .fRpointD.scalePointD \ -from 0.0 -to 100.0 \ -resolution 0.01 \ -font fontTEMP_SMALL_varwidth \ -variable VARpercentD \ -showvalue true \ -orient horizontal \ -bd $BDwidthPx_scale \ -length $scaleLengthPx \ -width $scaleThicknessPx \ -command {show_Ddegrees} label .fRpointD.labelDdegrees \ -text "" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## PACK the widgets in the '.fRpointD' frame. pack .fRpointD.labelPointD \ .fRpointD.scalePointD \ .fRpointD.labelDdegrees \ -side left \ -anchor w \ -fill none \ -expand 0 ##+########################################################## ## In FRAME '.fRimgsize' - ## DEFINE a LABEL widget and a SCALE widget. ## Then PACK THEM. ##+########################################################## label .fRimgsize.labelIMGSIZE \ -text "$aRtext(labelIMGSIZE)" \ -font fontTEMP_SMALL_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bd $BDwidthPx_label ## Set this widget var in the GUI initialization section ## at the bottom of this script. # set VARimgpixels 300 scale .fRimgsize.scaleIMGSIZE \ -from 50 -to 4000 \ -resolution 1 \ -font fontTEMP_SMALL_varwidth \ -variable VARimgpixels \ -showvalue true \ -orient horizontal \ -bd $BDwidthPx_scale \ -length 250 \ -width $scaleThicknessPx button .fRimgsize.buttWRITEIMG \ -text "$aRtext(buttonWRITEIMG)" \ -font fontTEMP_SMALL_varwidth \ -padx $PADXpx_button \ -pady $PADYpx_button \ -relief raised \ -bd $BDwidthPx_button \ -command {write_img_file} ## We initialize this radiobuttons widget var ## in the GUI initialization section at the ## bottom of this script. # set VARimgFormat "PS" radiobutton .fRimgsize.radbuttPS \ -text "$aRtext(radbuttPS)" \ -font fontTEMP_varwidth \ -variable VARimgFormat \ -value "PS" \ -anchor w \ -selectcolor "$radbuttBKGD" \ -padx $PADXpx_radbutt \ -pady $PADYpx_radbutt \ -relief $RELIEF_radbutt_hi radiobutton .fRimgsize.radbuttPDF \ -text "$aRtext(radbuttPDF)" \ -font fontTEMP_varwidth \ -variable VARimgFormat \ -value "PDF" \ -anchor w \ -selectcolor "$radbuttBKGD" \ -padx $PADXpx_radbutt \ -pady $PADYpx_radbutt \ -relief $RELIEF_radbutt_hi radiobutton .fRimgsize.radbuttGIF \ -text "$aRtext(radbuttGIF)" \ -font fontTEMP_varwidth \ -variable VARimgFormat \ -value "GIF" \ -anchor w \ -selectcolor "$radbuttBKGD" \ -padx $PADXpx_radbutt \ -pady $PADYpx_radbutt \ -relief $RELIEF_radbutt_hi radiobutton .fRimgsize.radbuttPNG \ -text "$aRtext(radbuttPNG)" \ -font fontTEMP_varwidth \ -variable VARimgFormat \ -value "PNG" \ -anchor w \ -selectcolor "$radbuttBKGD" \ -padx $PADXpx_radbutt \ -pady $PADYpx_radbutt \ -relief $RELIEF_radbutt_hi ## Pack ALL widgets in the '.fRimgsize' frame. pack .fRimgsize.labelIMGSIZE \ .fRimgsize.scaleIMGSIZE \ -side left \ -anchor w \ -fill none \ -expand 0 pack .fRimgsize.buttWRITEIMG \ .fRimgsize.radbuttPS \ .fRimgsize.radbuttPDF \ .fRimgsize.radbuttGIF \ .fRimgsize.radbuttPNG \ -side left \ -anchor w \ -fill none \ -expand 0 ##+######################################################## ## In FRAME '.fRresult' - ## DEFINE one LABEL widget. Then PACK it. ##+####################################################### label .fRresult.labelRESULT \ -text "" \ -font fontTEMP_varwidth \ -justify left \ -anchor w \ -relief flat \ -padx $PADXpx_label \ -pady $PADYpx_label \ -bg "#99ff99" \ -bd $BDwidthPx_label ## Pack the widgets in frame '.fRresult'. pack .fRresult.labelRESULT \ -side top \ -anchor nw \ -fill both \ -expand 1 ##+######################################################## ## In FRAME '.fRcanvas' - ## DEFINE a CANVAS WIDGET (no scrollbars). Then PACK it. ## ## We highlightthickness & borderwidth of the canvas to ## zero, as suggested on page 558, Chapter 37, 'The Canvas ## Widget', in the 4th edition of the book 'Practical ## Programming in Tcl and Tk'. ##+####################################################### canvas .fRcanvas.can \ -width $initCanWidthPx \ -height $initCanHeightPx \ -relief flat \ -highlightthickness 0 \ -borderwidth 0 \ -yscrollcommand ".fRcanvas.scrolly set" \ -xscrollcommand ".fRcanvas.scrollx set" scrollbar .fRcanvas.scrolly \ -orient vertical \ -command ".fRcanvas.can yview" scrollbar .fRcanvas.scrollx \ -orient horizontal \ -command ".fRcanvas.can xview" ##+####################################################### ## Pack the widgets in frame '.fRcanvas'. ## ## NOTE: ## NEED TO PACK THE SCROLLBARS BEFORE THE CANVAS WIDGET. ## OTHERWISE THE CANVAS WIDGET TAKES ALL THE FRAME SPACE. ##+####################################################### pack .fRcanvas.scrolly \ -side right \ -anchor e \ -fill y \ -expand 0 pack .fRcanvas.scrollx \ -side bottom \ -anchor s \ -fill x \ -expand 0 ## !!!NEED TO USE '-expand 0' FOR THE X AND Y SCROLLBARS, so that ## the canvas is allowed to fill the remaining frame-space nicely ## --- without a gap between the canvas and its scrollbars. pack .fRcanvas.can \ -side top \ -anchor n \ -fill none \ -expand 0 ##+################################################## ## END OF DEFINITION of the GUI widgets. ##+################################################## ## Start of BINDINGS, PROCS, Added-GUI-INIT sections. ##+################################################## ##+################################################################## ##+################################################################## ## BINDINGS SECTION: ##+################################################################## bind .fRpointB.scalePointB {advise_user \ "LOCATION B may have CHANGED. $aRtext(msgREDRAW)"} bind .fRpointC.scalePointC {advise_user \ "LOCATION C may have CHANGED. $aRtext(msgREDRAW)"} bind .fRpointD.scalePointD {advise_user \ "LOCATION D may have CHANGED. $aRtext(msgREDRAW)"} bind .fRimgsize.scaleIMGSIZE {advise_user \ "REQUESTED IMAGE SIZE may have CHANGED. $aRtext(msgREDRAW)"} bind .fRbuttons.buttCOLORbkgd {advise_user \ "BACKGROUND COLOR may have CHANGED. $aRtext(msgREDRAW)"} bind .fRbuttons.buttCOLORcircle {advise_user \ "CIRCLE COLOR may have CHANGED. $aRtext(msgREDRAW)"} bind .fRbuttons.buttCOLORline {advise_user \ "LINE SEGMENT COLOR may have CHANGED. $aRtext(msgREDRAW)"} bind .fRbuttons.buttCOLORtext {advise_user \ "TEXT COLOR may have CHANGED. $aRtext(msgREDRAW)"} ##+################################################################## ##+################################################################## ## PROCS SECTION: ## ## ************* ## Drawing procs: ## ************* ## ## 'redraw' - This proc is used to erase and redraw the circle and ## the quadrangle (and its diagonals) on the canvas --- ## for example, after the user has changed one or more ## of the locations of vertices B,C,D via a scale widget ## and/or after a color change and/or a new size for the ## image square via a scale widget. ## ## This proc is called by the 'Redraw' button and ## in the 'Additional GUI Initialization' section ## at the bottom of this script to put an initial drawing ## on the canvas. ## ## 'draw_circle' - Assuming circle radius R=1.0 in 'world coordinates', ## this proc deletes and draws a circle on the canvas. ## (A hex-RGB color specification is used ## to specify the color of the circle.) ## (The circle drawn may be given a tag, such as ## 'TAGcircle'.) ## ## 'draw_quadrangle' - For given locations of A,B,C,D, this proc deletes ## and draws an inscribed quadrangle (and its 2 diagonals) ## on the canvas. ## (A hex-RGB color specification is used ## to specify the color of the quadrangle lines.) ## (The quadrangle drawn may be given a tag, such as ## 'TAGquad'.) ## ## ********************************************* ## World-Coordinates-to-Pixels mapping utilities: ## ********************************************* ## ## 'setMappingVars_for_px2wc' - to set up constants to be used in converting ## between 'world coordinates' and 'pixel coordinates' ## on the Tk canvas. ## ## Called by the 'redraw' proc. ## ## This proc takes the coordinates of an UpperLeft (UL) ## point and a LowerRight (LR) point --- in both ## 'world coordinates' and 'pixel coordinates' and ## sets some global variables to be used by the ## other drawing procs --- mainly the 2 ratios: ## 'PXperWCx' and 'PXperWCy' --- ## the number-of-pixels-per-world-coordinate-unit, ## in global variables. ## ## The global variables set by this proc are used ## in the 'Xwc2px' and 'Ywc2px' procs below. ## ## For more detail on this proc, see the ## comments in the proc (below). ## ## 'Xwc2px' - Converts an x world-coordinate to pixel units. ## Called in the 'draw' procs. ## ## 'Ywc2px' - Converts a y world-coordinate to pixel units. ## Called in the 'draw' procs. ## ## (Note: The 'draw' procs use world-coordinates ## as input. The 'Xwc2px' and 'Ywc2px' procs are used ## by the 'draw' procs to calculate pixel coordinates for ## the Tk canvas 'create circle' and 'create line' commands ## --- and 'create text' commands.) ## ## ******************* ## Other utility procs: ## ******************* ## ## 'reset_parms' - Sets (initial) values for some scale widgets. ## Called by the 'ResetParms' button. ## ## 'set_background_color' - Sets the color for the canvas (background). ## Called by the 'BackgroundColor' button. ## ## 'set_circle_color' - Sets the color for the circle on the canvas. ## Called by the 'CircleColor' button. ## ## 'set_line_color' - Sets the color for the inscribed quadrilateral ## and its diagonals. ## Called by the 'LineColor' button. ## ## 'set_text_color' - Sets the color for any text drawn on the canvas. ## Called by the 'TextColor' button. ## ## 'update_button_colors' - Sets the color of the several color buttons. ## ## 'advise_user' - Used by various procs and bindings to put a message ## for the user on a message line of the GUI. ## ## 'show_Bdegrees' - Called by a command option of the B scale widget. ## 'show_Cdegrees' - Called by a command option of the C scale widget. ## 'show_Ddegrees' - Called by a command option of the D scale widget. ## ## 'popup_msgVarWithScroll' - called by 'Help' button to show HELPtext var. ## ##+################################################################# ##+################################################################ ## PROC 'redraw' ##+################################################################ ## PURPOSE: Used to erase and redraw the circle, quadrilateral, ## and its diagonals (and some text --- point labels, etc.) ## --- on the canvas. ## ## This proc also is used to update the text in ## the label of the '.fRresult' frame. ## ## CALLED BY: the 'Redraw' button and by ## the 'Additional GUI Initialization' section to draw ## an initial circle and quadrilateral on the canvas. ##+################################################################ proc redraw {} { ## FOR TESTING: (dummy out this routine) # return global VARradius marginFactor VARimgpixels decimalDIGITS aRtext \ lenAC lenBD lenAB lenCD lenBC lenDA \ VARpercentB VARpercentC VARpercentD ################################################# ## Clear the canvas and the msg and results lines. ################################################# .fRcanvas.can delete all .fRmsg.labelMSG configure -text "" .fRresult.labelRESULT configure -text "" ################################################################## ## Set height parameter of the image square, in world coordinates. ################################################################## set tempH [expr {$marginFactor * $VARradius}] ################################################################ ## Initialize the width & height of the image area that we ## are going to use --- from the 'imgsize' scale widget variable. ################################################################ set imgWidthPx $VARimgpixels set imgHeightPx $VARimgpixels .fRcanvas.can configure -width $imgWidthPx .fRcanvas.can configure -height $imgHeightPx ############################################################ ## Set the 'scrollregion' of the canvas according to the ## requested canvas size. (A simple 'update' does not work.) ############################################################ .fRcanvas.can configure -scrollregion "0 0 $imgWidthPx $imgHeightPx" ######################################################## ## Make the canvas area as big as we can to accomodate ## a large image. ## (We use a 'wm' command to get the window to resize ## according to the canvas resize --- even after the ## user has manually resized the top window.) ## ## Reference: wiki.tcl.tk/10720 and wiki.tcl.tk/44 ## and page 237 of Ousterhout's book 'Tcl and the Tk Toolkit': ## "If you would like to restore a window to its natural ## size, you can invoke 'wm geometry' with an empty ## geometry string." ######################################################## wm geometry . {} ################################################################ ## Set the global variables for converting world-coords to pixels ## --- or vice versa.The variables are PXperWCx and PXperWCy. ## ## We do this is in case the user changed the requested ## image size before the next draw on the image area ## and since the last time we set the mapping parms. ################################################################ ## Recall input vars for proc 'setMappingVars_for_px2wc' are: ## xORy,ULwcX,ULwcY,ULpxX,ULpxY,LRwcX,LRwcY,LRpxX,LRpxY ## ## We map world-coord X-limits -tempH and +tempH ## TO pixel-coord X-limits 0 and imgWidthPx ## ## AND ## ## We map world-coord Y-limits +tempH and -tempH (top and bottom) ## TO pixel-coord Y-limits 0 and imgHeightPx (top and bottom) ## ## See code in 'setMappingVars_for_px2wc' for details. ######################################################################## set XwcUL [expr {-$tempH}] set YwcUL $tempH set XwcLR $tempH set YwcLR [expr {-$tempH}] ## FOR TESTING: if {0} { puts "" puts "proc 'set_MappingVars_forImageSquare':" puts "XwcUL: $XwcUL YwcUL: $YwcUL XwcLR: $XwcLR YwcLR: $YwcLR" puts "imgWidthPx: $imgWidthPx imgHeightPx: $imgHeightPx" } setMappingVars_for_px2wc xy $XwcUL $YwcUL 0 0 $XwcLR $YwcLR \ $imgWidthPx $imgHeightPx ################################################ ## Redraw the circle. ################################################ draw_circle ################################################### ## Redraw the quadrilateral and its diagonals ## and the text labels of its vertices. Also ## calculate the lengths of the 6 line segments, ## for use in calculating the left- and right-hand ## sides of the Ptolemy equation: ## AC * BD = (AB * CD) + (BC * DA) ################################################### draw_quadrilateral ################################################### ## Compute the left- and right-hand sides of the ## Ptolemy eqaution AC * BD = (AB * CD) + (BC * DA) ## and put the results in the '.fRresult' frame. ################################################### set LHS [expr {$lenAC * $lenBD}] set RHS [expr {($lenAB * $lenCD) + ($lenBC * $lenDA)}] set LHSfmt [format "%.${decimalDIGITS}f" $LHS] set RHSfmt [format "%.${decimalDIGITS}f" $RHS] set lenACfmt [format "%.${decimalDIGITS}f" $lenAC] set lenBDfmt [format "%.${decimalDIGITS}f" $lenBD] set lenABfmt [format "%.${decimalDIGITS}f" $lenAB] set lenCDfmt [format "%.${decimalDIGITS}f" $lenCD] set lenBCfmt [format "%.${decimalDIGITS}f" $lenBC] set lenDAfmt [format "%.${decimalDIGITS}f" $lenDA] .fRresult.labelRESULT configure -text \ "Diagonals: AC*BD = $lenACfmt * $lenBDfmt = $LHSfmt Sides: (AB*CD) + (BC*DA) = ($lenABfmt * $lenCDfmt) + ($lenBCfmt * $lenDAfmt) = $RHSfmt So AC*BD = (AB*CD) + (BC*DA) or, in words: The PRODUCT of the two DIAGONAL LENGTHS of a quadrangle inscribed in a circle is equal to the SUM of the PRODUCTS of the OPPOSITE SIDE LENGTHS." advise_user "$aRtext(msgCHGVERTEX)" } ## END OF proc 'redraw' ##+######################################################################## ## PROC 'draw_circle' ##+######################################################################## ## PURPOSE: Draws the circle on the square image area on the canvas. ## ## METHOD: We draw the circle at the center of the image area --- using the ## specified radius (in the 'world coordinates'). ## ## We use 'create oval' on the square canvas, after converting ## the bounding box from world coordinates to pixel coordinates. ## ## CALLED BY: proc 'redraw' ##+####################################################################### proc draw_circle {} { ## FOR TESTING: (to dummy out this proc) # return global VARradius COLORCIRCLEhex lineWidthPx ################################################################## ## In preparation for the 'create arc' command below: ## Set coords for the corners of the square circumscribing the ## circle (the bounding square), in world coordinates. ################################################################## set topleftX [expr {-$VARradius}] set topleftY $VARradius set botrightX $VARradius set botrightY [expr {-$VARradius}] ############################################### ## Convert world-coords to pixels. ############################################### set topleftXpx [Xwc2px $topleftX] set topleftYpx [Ywc2px $topleftY] set botrightXpx [Xwc2px $botrightX] set botrightYpx [Ywc2px $botrightY] ## FOR TESTING: if {0} { puts "" puts "proc 'draw_circle':" puts "topleftX: $topleftX topleftY: $topleftY" puts "botrightX: $botrightX botrightY: $botrightY" puts "topleftXpx: $topleftXpx topleftYpx: $topleftYpx" puts "botrightXpx: $botrightXpx botrightYpx: $botrightYpx" } ##################################################### ## Draw a circle at origin (0.0,0.0) in world coords, ## using pixel coords. Use 'create oval'. ################################################ .fRcanvas.can create oval \ $topleftXpx $topleftYpx $botrightXpx $botrightYpx \ -outline $COLORCIRCLEhex -width $lineWidthPx -tag TAGcircle } ## END OF PROC 'draw_circle' ##+######################################################################## ## PROC 'draw_quadrilateral' ##+######################################################################## ## PURPOSE: Draws the inscribed quadrangle (and 2 diagonals) on the canvas ## using the user-specified locations of A,B,C,D. ## ## MEHOD: We draw the quadrangle sides with vertices touching (lying on) ## the circle. We use sin() and cos() functions to calculate ## the 'world' xy-coordinates of A,B,C,D. We use a mapping ## of world-coordinates to pixel-coordinates in order to get ## the pixel coordinates for the 'create oval' and ## 'create line' commands. ## ## CALLED BY: proc 'redraw' ##+######################################################################## proc draw_quadrilateral {} { ## FOR TESTING: (dummy out this proc) # return ## INPUT vars: global pi twopi piOver2 3piOver2 VARradius \ lineWidthPx dashPatternDiag COLORLINEhex \ COLORTEXThex TEXToffsetPx \ lenAC lenBD lenAB lenCD lenBC lenDA \ VARpercentB VARpercentC VARpercentD ####################################################### ## Set the coordinates of points A, B, C, D of the ## inscribed quadrilateral --- world-coords and ## pixel-coords --- from which we will start drawing. ####################################################### set angArads 0.0 set Ax $VARradius set AxPx [Xwc2px $Ax] set Ay 0.0 set AyPx [Ywc2px $Ay] set angBrads [expr {($VARpercentB/100.0) * $twopi}] set Bx [expr {$VARradius * cos($angBrads)}] set BxPx [Xwc2px $Bx] set By [expr {$VARradius * sin($angBrads)}] set ByPx [Ywc2px $By] set angCrads [expr {$angBrads + (($VARpercentC/100.0) * ($twopi - $angBrads))}] set Cx [expr {$VARradius * cos($angCrads)}] set CxPx [Xwc2px $Cx] set Cy [expr {$VARradius * sin($angCrads)}] set CyPx [Ywc2px $Cy] set angDrads [expr {$angCrads + (($VARpercentD/100.0) * ($twopi - $angCrads))}] set Dx [expr {$VARradius * cos($angDrads)}] set DxPx [Xwc2px $Dx] set Dy [expr {$VARradius * sin($angDrads)}] set DyPx [Ywc2px $Dy] ## FOR TESTING: if {0} { puts "" puts "proc 'draw_quadrilateral':" puts "Ax : $Ax Ay : $Ay angArads: $angArads" puts "AxPx: $AxPx AyPx: $AyPx" puts "Bx : $Bx By : $By angBrads: $angBrads" puts "BxPx: $BxPx ByPx: $ByPx" puts "Cx : $Cx Cy : $Cy angCrads: $angCrads" puts "CxPx: $CxPx CyPx: $CyPx" puts "Dx : $Dx Dy : $Dy angDrads: $angDrads" puts "DxPx: $DxPx DyPx: $DyPx" } ####################################################### ## Draw the 2 diagonals: AC and BD ## Draw the 4 opposite sides: AB and CD ; BC and DA ####################################################### .fRcanvas.can create line \ $AxPx $AyPx $CxPx $CyPx \ -fill $COLORLINEhex -width $lineWidthPx \ -dash $dashPatternDiag -tags TAGdiagAC .fRcanvas.can create line \ $BxPx $ByPx $DxPx $DyPx \ -fill $COLORLINEhex -width $lineWidthPx \ -dash $dashPatternDiag -tags TAGdiagBD .fRcanvas.can create line \ $AxPx $AyPx $BxPx $ByPx \ -fill $COLORLINEhex -width $lineWidthPx \ -tags TAGsideAB .fRcanvas.can create line \ $CxPx $CyPx $DxPx $DyPx \ -fill $COLORLINEhex -width $lineWidthPx \ -tags TAGsideCD .fRcanvas.can create line \ $BxPx $ByPx $CxPx $CyPx \ -fill $COLORLINEhex -width $lineWidthPx \ -tags TAGsideBC .fRcanvas.can create line \ $DxPx $DyPx $AxPx $AyPx \ -fill $COLORLINEhex -width $lineWidthPx \ -tags TAGsideDA ###################################### ## Label the 4 points --- A,B,C,D. ###################################### if {$angArads <= $pi} { set YoffsetPx [expr {-$TEXToffsetPx}] } else { set YoffsetPx $TEXToffsetPx } if {($angArads <= $piOver2 && $angArads >= 0.0) || \ ($angArads >= $3piOver2 && $angArads <= $twopi) } { set XoffsetPx $TEXToffsetPx } else { set XoffsetPx [expr {-$TEXToffsetPx}] } .fRcanvas.can create text \ [expr {$AxPx + $XoffsetPx}] [expr {$AyPx + $YoffsetPx}] \ -anchor center -justify center -fill $COLORTEXThex \ -text "A" -font fontTEMP_SMALL_varwidth -tags TAGtextA if {$angBrads <= $pi} { set YoffsetPx [expr {-$TEXToffsetPx}] } else { set YoffsetPx $TEXToffsetPx } if {($angBrads <= $piOver2 && $angBrads >= 0.0) || \ ($angBrads >= $3piOver2 && $angBrads <= $twopi) } { set XoffsetPx $TEXToffsetPx } else { set XoffsetPx [expr {-$TEXToffsetPx}] } .fRcanvas.can create text \ [expr {$BxPx + $XoffsetPx}] [expr {$ByPx + $YoffsetPx}] \ -anchor center -justify center -fill $COLORTEXThex \ -text "B" -font fontTEMP_SMALL_varwidth -tags TAGtextB if {$angCrads <= $pi} { set YoffsetPx [expr {-$TEXToffsetPx}] } else { set YoffsetPx $TEXToffsetPx } if {($angCrads <= $piOver2 && $angCrads >= 0.0) || \ ($angCrads >= $3piOver2 && $angCrads <= $twopi) } { set XoffsetPx $TEXToffsetPx } else { set XoffsetPx [expr {-$TEXToffsetPx}] } .fRcanvas.can create text \ [expr {$CxPx + $XoffsetPx}] [expr {$CyPx + $YoffsetPx}] \ -anchor center -justify center -fill $COLORTEXThex \ -text "C" -font fontTEMP_SMALL_varwidth -tags TAGtextC if {$angDrads <= $pi} { set YoffsetPx [expr {-$TEXToffsetPx}] } else { set YoffsetPx $TEXToffsetPx } if {($angDrads <= $piOver2 && $angDrads >= 0.0) || \ ($angDrads >= $3piOver2 && $angDrads <= $twopi) } { set XoffsetPx $TEXToffsetPx } else { set XoffsetPx [expr {-$TEXToffsetPx}] } .fRcanvas.can create text \ [expr {$DxPx + $XoffsetPx}] [expr {$DyPx + $YoffsetPx}] \ -anchor center -justify center -fill $COLORTEXThex \ -text "D" -font fontTEMP_SMALL_varwidth -tags TAGtextD ################################################## ## Calculate the lengths of ## the 2 diagonals: AC and BD ## and the 4 opposite sides: AB and CD ; BC and DA ################################################## set lenAC [expr {hypot($Ax - $Cx, $Ay - $Cy)}] set lenBD [expr {hypot($Bx - $Dx, $By - $Dy)}] set lenAB [expr {hypot($Ax - $Bx, $Ay - $By)}] set lenCD [expr {hypot($Cx - $Dx, $Cy - $Dy)}] set lenBC [expr {hypot($Bx - $Cx, $By - $Cy)}] set lenDA [expr {hypot($Dx - $Ax, $Dy - $Ay)}] } ## END OF PROC 'draw_quadrilateral' ##+######################################################################## ## PROC 'setMappingVars_for_px2wc' ##+######################################################################## ## PURPOSE: Sets up 'constants' to be used in converting between x,y ## 'world coordinates' and 'pixel coordinates' on a Tk canvas. ## ## Puts the constants in global variables: ## PXperWCx PXperWCy BASEwcX BASEwcY BASEpxX BASEpxY ## ## These variables are for use by 'Xwc2px' and 'Ywc2px' procs ## and 'Xpx2wc' and 'Ypx2wc' procs. ## ## The 'BASE' variables are coordinates of the upper-left point ## of the 'plotting rectangle' --- in world coordinates and ## in pixel coordinates. ## ## METHOD: This proc takes the coordinates of an UpperLeft (UL) ## point and a LowerRight (LR) point --- in both ## 'world coordinates' and 'pixel coordinates' and ## sets some global variables to the used by the ## other drawing procs --- mainly the ratio: ## ## the number-of-pixels-per-world-coordinate-unit, ## in global variable 'PXperWC' ## ## (This will generally include a fractional amount, ## i.e. it is not necessarily an integer.) ## ## INPUTS: ## ## At least eight numbers are input to this proc, as indicated by: ## ## ULwcX ULwcY ULpxX ULpxY LRwcX LRwcY LRpxX LRpxY ## ## Generally, the 'wc' inputs may be floating point numbers, and the ## 'px' inputs will generally be (non-negative) integers. ## ## Example: (for a plot area with x between -1.2 and +1.2 ## and with y between -0.2 and +1.2) ## setMappingVars_for_px2wc xy -1.2 1.2 0 0 1.2 -0.2 $canvasWidthPx $canvasHeightPx ## ## The first argument can be either 'x' or 'y' or 'xy'. This determines whether ## global variable 'PXperWC' is detemined by just the X-numbers, just the Y-numbers, ## or both. In this script, we use 'xy' (both). ## ## An 'adjustYpx' global variable can be used to adjust if the pixels ## on a user's monitor are not square. ## ## OUTPUTS: global variables PXperWC BASEwcX BASEwcY BASEpxX BASEpxY ## ## CALLED BY: by the 'setMappingVars_forImageSquare' proc. ##+######################################################################## proc setMappingVars_for_px2wc {xORy ULwcX ULwcY ULpxX ULpxY LRwcX LRwcY LRpxX LRpxY} { global PXperWCx PXperWCy BASEwcX BASEwcY BASEpxX BASEpxY adjustYpx ## FOR TESTING: (to dummy out this proc) # return ############################################################ ## Calculate PXperWCx and PXperWCy ## (pixels-per-world-coordinate-unit) --- the ratio ## of pixels-per-world-coordinate in the x and y directions, ## for the given UL and LR values. ############################################################ set PXperWCx [expr {abs(($LRpxX - $ULpxX) / ($LRwcX - $ULwcX))}] set PXperWCy [expr {abs(($LRpxY - $ULpxY) / ($LRwcY - $ULwcY))}] ## FOR TESTING: if {0} { puts "" puts "proc 'setMappingVars_for_px2wc':" puts "ULwcX: $ULwcX ULwcY: $ULwcY LRwcX: $LRwcX LRwcY: $LRwcY" puts "PXperWCx: $PXperWCx" puts "ULpxX: $ULpxX ULpxY: $ULpxY LRpxX: $LRpxX LRpxY: $LRpxY" puts "PXperWCy: $PXperWCy" } ############################################################# ## Reset PXperWCx and PXperWCy according to whether input ## variable 'xORy' is 'x' or 'y' or 'min' or 'xy'. ## ## For 'x', we set PXperWCy equal to PCperWcx. ## ## For 'y', we set PXperWCx equal to PCperWcy. ## ## For 'min', we set PXperWCx and PXperWCy to the smaller ## of PXperWCx and PXperWCy. ## ## For 'max', we set PXperWCx and PXperWCy to the larger ## of PXperWCx and PXperWCy. ## ## For 'xy', we will leave PXperWCx and PXperWCy unchanged. ############################################################# if {$xORy == "x"} { set PXperWCy $PXperWCx } elseif {$xORy == "y"} { set PXperWCx $PXperWCy } elseif {$xORy == "min"} { if {$PXperWCx > $PXperWCy} { set PXperWCx $PXperWCy } else { set PXperWCy $PXperWCx } } elseif {$xORy == "max"} { if {$PXperWCx > $PXperWCy} { set PXperWCy $PXperWCx } else { set PXperWCx $PXperWCy } } ## END OF if {$xORy == "x"} ############################################################ ## In case the pixels are not square, provide a factor ## that can be used to adjust in the Y direction. ############################################################ set adjustYpx 1.0 ############################################################ ## Set BASEwcX, BASEwcY, BASEpxX and BASEpxY. ############################################################ set BASEwcX $ULwcX set BASEwcY $ULwcY set BASEpxX $ULpxX set BASEpxY $ULpxY ## FOR TESTING: if {0} { puts "proc 'setMappingVars_for_px2wc':" puts "PXperWCx: $PXperWCx PXperWCy: $PXperWCy" puts "BASEwcX: $BASEwcX BASEwcY: $BASEwcY" puts "BASEpxX: $BASEpxX BASEpxY: $BASEpxY" } } ## END OF PROC 'setMappingVars_for_px2wc' ##+######################################################################## ## PROC 'Xwc2px' ##+######################################################################## ## PURPOSE: Converts an x world-coordinate to pixel units. ## ## CALLED BY: the 'draw' procs ##+######################################################################## proc Xwc2px {x} { global PXperWCx BASEwcX BASEpxX set px [expr {($x - $BASEwcX) * $PXperWCx + $BASEpxX}] return $px } ## END OF PROC 'Xwc2px' ##+######################################################################## ## PROC 'Ywc2px' ##+######################################################################## ## PURPOSE: Converts an y world-coordinate to pixel units. ## ## CALLED BY: the 'draw' procs ##+######################################################################## proc Ywc2px {y} { global PXperWCy BASEwcY BASEpxY adjustYpx set px [expr {($BASEwcY - $y) * $PXperWCy * $adjustYpx + $BASEpxY}] return $px } ## END OF PROC 'Ywc2px' ##+##################################################################### ## PROC 'reset_parms' ##+##################################################################### ## PURPOSE: To initalize several 'scale' widget variables. ## ## CALLED BY: the 'ResetParms' button ##+##################################################################### proc reset_parms {} { global VARpercentB VARpercentC VARpercentD set VARpercentB 40.0 set VARpercentC 50.0 set VARpercentD 45.0 } ## END OF PROC 'reset_parms' ##+##################################################################### ## PROC 'set_background_color' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set the (background) color of the canvas --- ## on which all the geometry is to be drawn. ## ## Arguments: none ## ## CALLED BY: .fRbuttons.buttCOLORbkgd button ##+##################################################################### proc set_background_color {} { global COLORBKGDr COLORBKGDg COLORBKGDb COLORBKGDhex ColorSelectorScript ## FOR TESTING: # puts "COLORBKGDr: $COLORBKGDr" # puts "COLORBKGDg: $COLORBKGDb" # puts "COLORBKGDb: $COLORBKGDb" set TEMPrgb [ exec $ColorSelectorScript $COLORBKGDr $COLORBKGDg $COLORBKGDb] ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB set COLORBKGDhex "#$hexRGB" set COLORBKGDr $r255 set COLORBKGDg $g255 set COLORBKGDb $b255 ## Set color of background-color button. update_button_colors ## Set the color of the canvas. .fRcanvas.can config -bg $COLORBKGDhex } ## END OF PROC 'set_background_color' #+##################################################################### ## PROC 'set_circle_color' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set the color of the circle to be ## drawn on the canvas. ## ## Arguments: none (global variables are used) ## ## CALLED BY: .fRbuttons.buttCOLORcircle button ##+##################################################################### proc set_circle_color {} { global COLORCIRCLEr COLORCIRCLEg COLORCIRCLEb COLORCIRCLEhex ColorSelectorScript ## FOR TESTING: # puts "COLORCIRCLEr: $COLORCIRCLEr" # puts "COLORCIRCLEg: $COLORCIRCLEb" # puts "COLORCIRCLEb: $COLORCIRCLEb" set TEMPrgb [ exec $ColorSelectorScript $COLORCIRCLEr $COLORCIRCLEg $COLORCIRCLEb] ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB set COLORCIRCLEhex "#$hexRGB" set COLORCIRCLEr $r255 set COLORCIRCLEg $g255 set COLORCIRCLEb $b255 ## Set color of the circle-color button. update_button_colors } ## END OF PROC 'set_circle_color' #+##################################################################### ## PROC 'set_line_color' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set the color of the inscribed quadrilateral ## drawn on the canvas. ## ## Arguments: none (global variables are used) ## ## CALLED BY: .fRbuttons.buttCOLORline button ##+##################################################################### proc set_line_color {} { global COLORLINEr COLORLINEg COLORLINEb COLORLINEhex ColorSelectorScript ## FOR TESTING: # puts "COLORLINEr: $COLORLINEr" # puts "COLORLINEg: $COLORLINEb" # puts "COLORLINEb: $COLORLINEb" set TEMPrgb [ exec $ColorSelectorScript $COLORLINEr $COLORLINEg $COLORLINEb] ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB set COLORLINEhex "#$hexRGB" set COLORLINEr $r255 set COLORLINEg $g255 set COLORLINEb $b255 ## Set color of the line-color button. update_button_colors } ## END OF PROC 'set_line_color' #+##################################################################### ## PROC 'set_text_color' ##+##################################################################### ## PURPOSE: ## ## This procedure is invoked to get an RGB triplet ## via 3 RGB slider bars on the FE Color Selector GUI. ## ## Uses that RGB value to set the color of any text ## drawn on the canvas. ## ## Arguments: none (global variables are used) ## ## CALLED BY: .fRbuttons.buttCOLORtext button ##+##################################################################### proc set_text_color {} { global COLORTEXTr COLORTEXTg COLORTEXTb COLORTEXThex ColorSelectorScript ## FOR TESTING: # puts "COLORTEXTr: $COLORTEXTr" # puts "COLORTEXTg: $COLORTEXTb" # puts "COLORTEXTb: $COLORTEXTb" set TEMPrgb [ exec $ColorSelectorScript $COLORTEXTr $COLORTEXTg $COLORTEXTb] ## FOR TESTING: # puts "TEMPrgb: $TEMPrgb" if { "$TEMPrgb" == "" } { return } scan $TEMPrgb "%s %s %s %s" r255 g255 b255 hexRGB set COLORTEXThex "#$hexRGB" set COLORTEXTr $r255 set COLORTEXTg $g255 set COLORTEXTb $b255 ## Set color of the text-color button. update_button_colors } ## END OF PROC 'set_text_color' ##+##################################################################### ## PROC 'update_button_colors' ##+##################################################################### ## PURPOSE: ## This procedure is invoked to set the background color of each of ## 4 color buttons to its current color --- and sets foreground color, ## for text on the 4 buttons, to a suitable(?) black or white color, ## so that the label text is readable. ## ## (We might need to weight the RGB colors differently when summing ## them, to get a better choice of black or white for the wide ## range of colors that are possible on these color buttons.) ## ## Arguments: global color vars ## ## CALLED BY: 4 colors procs: ## 'set_background_color' 'set_draw_color' ## 'set_line_color' 'set_text_color' ## and ## in the additional-GUI-initialization section at ## the bottom of this script to initialize the color of ## the buttons. ##+##################################################################### proc update_button_colors {} { global COLORBKGDr COLORBKGDg COLORBKGDb COLORBKGDhex \ COLORCIRCLEr COLORCIRCLEg COLORCIRCLEb COLORCIRCLEhex \ COLORLINEr COLORLINEg COLORLINEb COLORLINEhex \ COLORTEXTr COLORTEXTg COLORTEXTb COLORTEXThex \ # set colorBREAK 300 set colorBREAK 375 .fRbuttons.buttCOLORbkgd configure -bg $COLORBKGDhex set sumCOLOR [expr {$COLORBKGDr + $COLORBKGDg + $COLORBKGDb}] if {$sumCOLOR > $colorBREAK} { .fRbuttons.buttCOLORbkgd configure -fg "#000000" } else { .fRbuttons.buttCOLORbkgd configure -fg "#f0f0f0" } .fRbuttons.buttCOLORcircle configure -bg $COLORCIRCLEhex set sumCOLOR [expr {$COLORCIRCLEr + $COLORCIRCLEg + $COLORCIRCLEb}] if {$sumCOLOR > $colorBREAK} { .fRbuttons.buttCOLORcircle configure -fg "#000000" } else { .fRbuttons.buttCOLORcircle configure -fg "#f0f0f0" } .fRbuttons.buttCOLORline configure -bg $COLORLINEhex set sumCOLOR [expr {$COLORLINEr + $COLORLINEg + $COLORLINEb}] if {$sumCOLOR > $colorBREAK} { .fRbuttons.buttCOLORline configure -fg "#000000" } else { .fRbuttons.buttCOLORline configure -fg "#f0f0f0" } .fRbuttons.buttCOLORtext configure -bg $COLORTEXThex set sumCOLOR [expr {$COLORTEXTr + $COLORTEXTg + $COLORTEXTb}] if {$sumCOLOR > $colorBREAK} { .fRbuttons.buttCOLORtext configure -fg "#000000" } else { .fRbuttons.buttCOLORtext configure -fg "#f0f0f0" } } ## END OF PROC 'update_button_colors' ##+######################################################################## ## PROC: 'write_img_file' ##+######################################################################## ## PURPOSE: Write an image file from the image on the canvas: ## - PS = (color) Adobe Postscript ## or ## - PDF = Adobe Portable Document Format ## or ## - GIF = Graphical Image Format ## or ## - PNG = Portable Network Graphics ## ## CALLED BY: the 'WriteImgFile' button ##+######################################################################## proc write_img_file {} { global DIRtemp env VARimgpixels VARimgFormat \ PS2PDFcmd CONVERTcmd \ PSviewer PDFviewer GIFviewer PNGviewer ########################################################## ## Make a (color) Postscript file, from which the other ## 3 types of image files can be made. ## ## Set a name for the output '.ps' file and write the color ## Postscript file from the canvas contents. ########################################################## set PSfile "$DIRtemp/$env(USER)_PtolemyTheorem.ps" ## FOR TESTING: if {0} { puts "" puts "PROC 'write_img_file':" puts " PSfile: $PSfile" } ############################################################## ## Tk canvas 'postscript' option NOTES: ## ## Color modes: color, gray, mono ## ## It is important to use the '-width' and -height' parms to ## give the size of the image in pixels --- otherwise the ## image may be clipped or positioned improperly on the 'page'. ############################################################## set err [.fRcanvas.can postscript -file "$PSfile" \ -colormode color \ -width $VARimgpixels \ -height $VARimgpixels \ -x 0 -y 0 \ -rotate false \ -pageheight 11.0i \ -pagewidth 8.5i \ -pagey 0.5i \ -pagex 0.5i \ -pageanchor sw] if {"$err" != ""} { advise_user \ "** Postscript file write may have FAILED. ** ** Message: $err **" return } ########################################################## ## According to the variable VARimgFormat, ## 1) use an image conversion utility to create the ## requested image file format (if necessary), ## 2) show the file in a viewer, ## 3) put an explanatory msg in the msg frame. ########################################################## if {"$VARimgFormat" == "PS"} { advise_user \ "** Postscript file was written to file $PSfile ** Started viewer $PSviewer **" exec $PSviewer "$PSfile" & return } ## END OF if {"$VARimgFormat" == "PS"} ########################## ## Write and show PDF file. ########################## if {"$VARimgFormat" == "PDF"} { set PDFfile "$DIRtemp/$env(USER)_PtolemyTheorem.pdf" set err [exec $PS2PDFcmd "$PSfile" "$PDFfile"] if {"$err" != ""} { advise_user \ "** Postscript to PDF file conversion may have FAILED. ** ** Message: $err **" return } advise_user \ "** PDF file was written to file $PDFfile ** Started viewer $PDFviewer **" exec $PDFviewer "$PDFfile" & return } ## END OF if {"$VARimgFormat" == "PDF"} ########################## ## Write and show GIF file. ########################## if {"$VARimgFormat" == "GIF"} { set GIFfile "$DIRtemp/$env(USER)_PtolemyTheorem.gif" set err [eval exec $CONVERTcmd "$PSfile" "$GIFfile"] if {"$err" != ""} { advise_user \ "** Postscript to GIF file conversion may have FAILED. ** ** Message: $err **" return } advise_user \ "** GIF file was written to file $GIFfile ** Started viewer $GIFviewer **" exec $GIFviewer "$GIFfile" & return } ## END OF if {"$VARimgFormat" == "GIF"} ########################## ## Write and show PNG file. ########################## if {"$VARimgFormat" == "PNG"} { set PNGfile "$DIRtemp/$env(USER)_PtolemyTheorem.png" set err [eval exec $CONVERTcmd "$PSfile" "$PNGfile"] if {"$err" != ""} { advise_user \ "** Postscript to PNG file conversion may have FAILED. ** ** Message: $err **" return } advise_user \ "** PNG file was written to file $PNGfile ** Started viewer $PNGviewer **" exec $PNGviewer "$PNGfile" & return } ## END OF if {"$VARimgFormat" == "PNG"} } ## END OF PROC 'write_img_file' ##+##################################################################### ## PROC 'advise_user' ##+##################################################################### ## PURPOSE: Puts a message to the user on the GUI. ## ## CALLED BY: in the additional-GUI-initialization section at ## the bottom of this script ## and in some procs. ##+##################################################################### proc advise_user {text} { .fRmsg.labelMSG configure -text "$text" ## Make sure the text is displayed on the GUI. update ## Alternatively, we could put the message in the title-bar ## of the GUI window. (But it is easy for the user to ## fail to see the message there. Besides, we have more ## options in displaying the message by putting it on a ## Tk widget in the GUI.) ## # wm title . "$text" } ## END OF PROC 'advise_user' ##+##################################################################### ## PROC 'show_Bdegrees' ##+##################################################################### ## PURPOSE: Calculates and shows the location of point B on the circle, ## in degrees. ## ## CALLED BY: the command option of the B scale widget ##+##################################################################### proc show_Bdegrees {pcentB} { global Bdegrees decimalDIGITS set Bdegrees [expr {360.0 * $pcentB / 100.0}] set BdegreesFMTD [format "%.${decimalDIGITS}f" $Bdegrees] .fRpointB.labelBdegrees configure -text "B degrees: $BdegreesFMTD" } ## END OF PROC 'show_Bdegrees' ##+##################################################################### ## PROC 'show_Cdegrees' ##+##################################################################### ## PURPOSE: Calculates and shows the location of point C on the circle, ## in degrees. ## ## CALLED BY: the command option of the C scale widget ##+##################################################################### proc show_Cdegrees {pcentC} { global Cdegrees Bdegrees decimalDIGITS set Cdegrees [expr {((360.0 - $Bdegrees) * $pcentC / 100.0) + $Bdegrees}] set CdegreesFMTD [format "%.${decimalDIGITS}f" $Cdegrees] .fRpointC.labelCdegrees configure -text "C degrees: $CdegreesFMTD" } ## END OF PROC 'show_Cdegrees' ##+##################################################################### ## PROC 'show_Ddegrees' ##+##################################################################### ## PURPOSE: Calculates and shows the location of point D on the circle, ## in degrees. ## ## CALLED BY: the command option of the D scale widget ##+##################################################################### proc show_Ddegrees {pcentD} { global Cdegrees decimalDIGITS set Ddegrees [expr {((360.0 - $Cdegrees) * $pcentD / 100.0) + $Cdegrees}] set DdegreesFMTD [format "%.${decimalDIGITS}f" $Ddegrees] .fRpointD.labelDdegrees configure -text "D degrees: $DdegreesFMTD" } ## END OF PROC 'show_Ddegrees' ##+######################################################################## ## PROC 'popup_msgVarWithScroll' ##+######################################################################## ## PURPOSE: Report help or error conditions to the user. ## ## We do not use focus,grab,tkwait in this proc, ## because we use it to show help when the GUI is idle, ## and we may want the user to be able to keep the Help ## window open while doing some other things with the GUI ## such as putting a filename in the filename entry field ## or clicking on a radiobutton. ## ## For a similar proc with focus-grab-tkwait added, ## see the proc 'popup_msgVarWithScroll_wait' in a ## 3DterrainGeneratorExaminer Tk script. ## ## REFERENCE: page 602 of 'Practical Programming in Tcl and Tk', ## 4th edition, by Welch, Jones, Hobbs. ## ## ARGUMENTS: A toplevel frame name (such as .fRhelp or .fRerrmsg) ## and a variable holding text (many lines, if needed). ## ## 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 Doyel,'Interactive Web Applications ## with Tcl/Tk', Appendix A "ED, the Tcl Code Editor". ##+######################################################################## proc popup_msgVarWithScroll { toplevName VARtext ULloc} { ## 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 $toplevName} toplevel $toplevName # wm geometry $toplevName 600x400+100+50 # wm geometry $toplevName +100+50 wm geometry $toplevName $ULloc wm title $toplevName "Note" # wm title $toplevName "Note to $env(USER)" wm iconname $toplevName "Note" ##################################### ## In the frame '$toplevName' - ## DEFINE THE TEXT WIDGET and ## its two scrollbars --- and ## DEFINE an OK BUTTON widget. ##################################### if {$VARheight > 10} { text $toplevName.text \ -wrap none \ -font fontTEMP_fixedwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 \ -yscrollcommand "$toplevName.scrolly set" \ -xscrollcommand "$toplevName.scrollx set" scrollbar $toplevName.scrolly \ -orient vertical \ -command "$toplevName.text yview" scrollbar $toplevName.scrollx \ -orient horizontal \ -command "$toplevName.text xview" } else { text $toplevName.text \ -wrap none \ -font fontTEMP_fixedwidth \ -width $VARwidth \ -height $VARheight \ -bg "#f0f0f0" \ -relief raised \ -bd 2 } button $toplevName.butt \ -text "OK" \ -font fontTEMP_varwidth \ -command "destroy $toplevName" ############################################### ## PACK *ALL* the widgets in frame '$toplevName'. ############################################### ## Pack the bottom button BEFORE the ## bottom x-scrollbar widget, pack $toplevName.butt \ -side bottom \ -anchor center \ -fill none \ -expand 0 if {$VARheight > 10} { ## Pack the scrollbars BEFORE the text widget, ## so that the text does not monopolize the space. pack $toplevName.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 $toplevName.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 $toplevName.text \ -side top \ -anchor center \ -fill both \ -expand 1 } else { pack $toplevName.text \ -side top \ -anchor center \ -fill both \ -expand 1 } ################################################ ## Set some 'event' bindings to allow for ## easy scrolling through huge listings. ## is a press of the Page-Down key. ## is a press of the Page-Up key. ## is a press of the Home key ## to go to the top of the listing. ## is a press of the End key ## to go to the bottom of the listing. ## is a press of the Up-arrow key. ## is a press of the Down-arrow key. ################################################ bind $toplevName "$toplevName.text yview scroll +1 page" bind $toplevName "$toplevName.text yview scroll -1 page" bind $toplevName "$toplevName.text see 1.0" bind $toplevName "$toplevName.text see end" bind $toplevName "$toplevName.text yview scroll -1 unit" bind $toplevName "$toplevName.text yview scroll +1 unit" ##################################### ## LOAD MSG INTO TEXT WIDGET. ##################################### ## $toplevName.text delete 1.0 end $toplevName.text insert end $VARtext $toplevName.text configure -state disabled } ## END OF PROC 'popup_msgVarWithScroll' ##+######################## ## END of PROC definitions. ##+######################## ##+######################## ## Set the 'HELPtext' var. ##+######################## set HELPtext "\ \ \ ** HELP for this 'tkPtolemysTheorem' App ** This Tk GUI script is meant to demonstrate Ptolemy's Theorem for a quadrangle inscribed in a circle: The product of the two diagonal lengths of the quadrangle is equal to the sum of the products of the opposite side lengths. In other words: Let the quadrangle have vertex points A,B,C,D --- say labelled counter-clockwise around the quadrangle (and around the circle). AC is one diagonal across the quadrangle, and BD is the other diagonal. AB and CD are opposite sides, and BC and DA are opposite sides. Ptolemy showed that the lengths satisfy the equality AC * BD = (AB * CD) + (BC * DA) B . . . . | . . . . | . ('Without loss of generality', C . -------------------- . A we fix A and allow the user . | . to move B,C,D.) . . . | . . . . | . . D This GUI script is meant to 'dynamically' demonstrate the equality by drawing a circle on a Tk 'canvas' on the GUI and allowing the user to place the 3 vertices --- B,C,D --- around the circumference of the circle. Whenever the user moves a vertex, some (or all) of the six lengths --- AC,BD,AB,CD,BC,DA --- are recalculated, and the two sides of the above expression are recalculated and shown to be equal. NOTE: We are NOT using this GUI to show a proof of Ptolemy's theorem. We use the GUI to VERIFY that the equation always holds --- no matter where we position the vertex points of the quadrangle around the circle. You can consult the following references for proofs (with diagrams). ********** REFERENCES: ********** There is a nice write-up of a proof of Ptolemy's theorem in the last half of Chapter 6 of the book 'Trigonometric Delights' by Eli Maor. Maor points out that several other truths of plane geometry and plane trigonometry are quick deductions from Ptolemy's theorem --- including the Pythagorean Theorem and the sine sum and difference formulas. Also see https://en.wikipedia.org/wiki/Ptolemy%27s_theorem ********************* HOW Ptolemy USED THIS: ********************* The book 'Great Calculations' by Colin Pask points out that Ptolemy used this relationship to make a table of chord lengths for angles between 0 and 180 degrees. Ptolemy used the relationship (the equality) to essentially a) Get the chord length subtending half an angle, given the chord length subtending the original angle. b) Get the chord length subtending the difference of 2 angles, given the chord lengths subtending the 2 angles. Those two capabilities allowed Ptolemy to make a table of chord lengths of at least 180 equally-spaced angles between 0 and 180 degrees. Tables of chord lengths were the precursors of sine tables. (In a unit circle, the length of a chord of an angle is twice the sine of half the angle.) ************ GUI FEATURES: ************ This GUI uses a Tk 'canvas' widget to show the circle and the quadrangle --- and its 2 diagonals. We fix point A on the right side of the circle and let the user position B,C,D around the circle. This does not affect the result because, for ANY quadrilateral in the circle, we could rotate the circle around until the point A is on the right side of the circle. To make a different quadrilateral, it is only necessary to move the points B, C, or D. Three 'scale' widgets on the GUI allow the user to easily specify the locations of vertices B,C,D around the cirumference of the circle --- in terms of percents of 'the remaining arc'. In particular, - B is located as a percent of the 360 degree arc, counter-clockwise from point A back to point A. - C is located as a percent of the arc between point B and point A, going counter-clockwise from point B. - D is located as a percent of the arc between point C and point A, going counter-clockwise from point C. We use a square drawing area (Tk 'canvas' widget), and we allow the user to specify the size (in pixels) of that drawing area --- via a 'scale' widget on the GUI. ************* GUI OPERATION: ************* When the user changes a B, C, and/or D vertex location --- and/or changes a color --- and/or changes the image size (in pixels), the user can click the 'Redraw' button on the GUI so that - the quadrangle (including its diagonals) is redrawn in the circle and - the left-hand and right-hand sides of the expression above are computed (from the lengths of the six line segments) to demonstrate that the equality 'holds'. *********************************** METHOD USED to perform the drawing: *********************************** A square image area (a Tk 'canvas' widget) is defined in a Tk 'frame' widget that contains the canvas. A 'mapping' proc is used to associate 'world coordinates' to the pixel coordinates of the Tk canvas. Two procs -- 'Xwc2px' and 'Ywc2px' -- are used to convert world coordinates to pixel coordinates that are to be used in 'create oval' and 'create line' commands --- to draw the circle and the quadrangle components, respectively. 'Without loss of generality', we can define the center of the circle to be in the center of the square image area --- at (0.0,0.0) in 'world coordinates'. We draw a circle of unit radius, R = 1.0. The world coordinates of the four corners of the square image area will be - top left (-H,+H) - top right (+H,+H) - bottom left (-H,-H) - bottom right (+H,-H) where H is somewhat bigger than R, such as H = 1.2 * R = 1.2 * 1.0 = 1.2. The factor 1.2 is a 'margin factor'. The world coordinates of the vertices of the inscribed quadrangle are calculated using sin() and cos() functions. Some text items may be put on the canvas, such as labels for the points A,B,C,D --- and values of their position angles around the circle --- and/or x,y coordinates of the vertices. ************************* IMAGE (and WINDOW) RESIZE: ************************* We allow the user to resize the drawing rather than using a fixed drawing size. If the user wants to resize the drawing, the 'ImageSize' 'scale' widget can be used to request a larger or smaller image size. Then the 'ReDraw' button can be used to force the drawing to be resized according to the requested image size. The GUI window will be resized to accomodate the requested image size --- up to the screen size available. If the GUI, including the image square, become too large for the screen, the scrollbars of the canvas can be used to examine various parts of the image. *************************************** USING THE SLIDER ON THE 'SCALE' WIDGETS: *************************************** You can click MB1 (mouse-button-1) on the 'slider' of a 'scale' widget and drag the slider. When you get the settings of the 'scale' widgets where you want them, click the 'ReDraw' button to redraw the image (chord and 2 angles in the circle). If you want FINE CONTROL of moving the slider, there are a couple of other options based on clicking in the trough on either side of the slider: 1) Click-and-release on either side of the slider, to change the figure by one increment of the scale per click-release. OR 2) Click-and-hold on either side of the slider. The slider moves until you release MB1. As the slider goes from 0 to 180, the 'chord' is drawn, horizontally, at the bottom of the circle going to the top of the circle. ******************************** CAPTURING THE GUI IMAGE: ******************************** A screen/window capture utility (like 'gnome-screenshot' on Linux) can be used to capture the GUI image in a PNG file, say. If necessary, an image editor (like 'mtpaint' on Linux) can be used to crop the window capture image. The image could also be down-sized --- say to make a smaller image suitable for use in a web page or an email. --- Alternatively, the 'WriteImageFile' button can be used to write a (color) Postscript file from the circle and line-segments and text items drawn on the canvas. Via four 'image-format' radiobuttons on the GUI, that color Postscript file can be created --- and, optionally, it can be converted to a PDF or GIF or PNG file. The PDF file can be created by a 'ps2pdf' file conversion utility. And the GIF or PNG file can be created by the ImageMagick 'convert' utility, via the command 'convert -background white -flatten'. The conversion utilities can be changed by editing this Tk script and changing a 'PS2PDFcmd' or 'CONVERTcmd' variables that are set near the bottom of this script. In addition, the viewers used to show the PS, PDF, GIF, and PNG files can be changed by editing the 'set' statements for 'PSviewer', 'PDFviewer', 'GIFviewer', and 'PNGviewer' variables near the bottom of this script. " ##+################################################################ ##+################################################################ ## ADDITIONAL GUI INITIALIZATION SECTION: Mainly to ## - Draw an initial circle and quadrangle on the canvas. ##+################################################################ ##+################################################################ ##+##################################################### ## Set the full-name of the RGB color-selector Tk script ## that is used in several procs above. ##+##################################################### ## FOR TESTING: # puts "argv0: $argv0" set DIRthisScript "[file dirname $argv0]" ## For ease of testing in a Linux/Unix terminal and located at the ## directory containing this Tk script. Set the full directory name. if {"$DIRthisScript" == "."} { set DIRthisScript "[pwd]" } set DIRupOne "[file dirname "$DIRthisScript"]" set DIRupTwo "[file dirname "$DIRupOne"]" set ColorSelectorScript "$DIRupTwo/SELECTORtools/tkRGBselector/sho_colorvals_via_sliders3rgb.tk" ## Alternatively: Put the RGB color-selector Tk script in the ## same directory as this Tk script and uncomment the following. # set ColorSelectorScript "$DIRthisScript/sho_colorvals_via_sliders3rgb.tk" ############################################################### ## Initialize the drawing colors --- for background, circle, ## line-segments and text. ############################################################### # Bluish background: set COLORBKGDr 90 set COLORBKGDg 90 set COLORBKGDb 255 set COLORBKGDhex [format "#%02X%02X%02X" $COLORBKGDr $COLORBKGDg $COLORBKGDb] # Black circle: set COLORCIRCLEr 0 set COLORCIRCLEg 0 set COLORCIRCLEb 0 set COLORCIRCLEhex [format "#%02X%02X%02X" $COLORCIRCLEr $COLORCIRCLEg $COLORCIRCLEb] # Red lines: set COLORLINEr 255 set COLORLINEg 0 set COLORLINEb 0 set COLORLINEhex [format "#%02X%02X%02X" $COLORLINEr $COLORLINEg $COLORLINEb] # Orange text: set COLORTEXTr 255 set COLORTEXTg 200 set COLORTEXTb 0 set COLORTEXThex [format "#%02X%02X%02X" $COLORTEXTr $COLORTEXTg $COLORTEXTb] ###################################### ## Set the color of the color buttons. ###################################### update_button_colors ##+############################################################ ## We need following command because the 'redraw' proc ## (called below) does not (re)set the background/canvas color. ## Only the background-color button-proc sets the canvas color. ##+############################################################ .fRcanvas.can config -bg $COLORBKGDhex ##+################################################### ## Initialize the scale widget variables for ## location percents of B,C,D. ##+################################################### reset_parms ##+################################################### ## Initialize the scale widget variable for image size. ##+################################################### set VARimgpixels 300 ##+################################################### ## Set an offset to use in placing text point labels. ##+################################################### set TEXToffsetPx 8 ##+###################################################### ## Set a 'margin-factor' to apply to the circle radius ## to set the world coordinates of the square image area. ##+###################################################### set VARradius 1.0 set marginFactor 1.2 ##+################################################### ## Set a width for lines (circle and quadrangle lines). ##+################################################### # set lineWidthPx 1 set lineWidthPx 2 ##+################################################ ## Set dash-pattern for the 2 diagonals. ##+################################################ set dashPatternDiag {3 3} ##+################################################# ## Set constants to use for angle to xy-coordinate ## computations. ##+################################################ set pi [expr {4.0 * atan(1.0)}] set twopi [expr {2.0 * $pi}] set piOver2 [expr {$pi / 2.0}] set 3piOver2 [expr {$piOver2 * 3.0}] ##+################################################# ## Set a constants to use in 'format' statements ## used to display the results. ##+################################################ # set decimalDIGITS 6 set decimalDIGITS 4 ##+################################################# ## Set a directory for the output of image files. ## To be used by the 'write_img_file' proc of the ## 'WriteImgFile' button. ##+################################################# set DIRtemp "/tmp" ##+################################################# ## Set an initial value for the radiobuttons variable ## that is used by the 'write_img_file' proc of the ## 'WriteImgFile' button. ##+################################################ set VARimgFormat "PS" # set VARimgFormat "PDF" # set VARimgFormat "GIF" # set VARimgFormat "PNG" ##+################################################# ## Set image conversion commands for the ## 'write_img_file' proc. ##+################################################ set PS2PDFcmd "/usr/bin/ps2pdf" set CONVERTcmd "/usr/bin/convert -background white -flatten" ##+################################################# ## Set image viewer commands for the ## 'write_img_file' proc. ##+################################################ set PSviewer "/usr/bin/evince" # set PDFviewer "/usr/bin/xpdf" set PDFviewer "/usr/bin/evince" # set GIFviewer "/usr/bin/mtpaint" # set GIFviewer "/usr/bin/eom" set GIFviewer "/usr/bin/eog" # set PNGviewer "/usr/bin/mtpaint" # set PNGviewer "/usr/bin/eom" set PNGviewer "/usr/bin/eog" ##+################################################## ## Draw the circle and quadrangle, for the current ## canvas (image area) size. ## (The msg and result labels will be filled in ## appropriately by the 'redraw' proc.) ##+################################################## redraw